diff --git a/LICENSE-MIT.txt b/LICENSE-MIT.txt index 0402262..41d107c 100644 --- a/LICENSE-MIT.txt +++ b/LICENSE-MIT.txt @@ -1,4 +1,4 @@ -Copyright (c) 2014 Sartaj Singh, Sumit Sahrawat +Copyright (c) 2015 Sartaj Singh, Sumit Sahrawat, Govind Sahai Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation diff --git a/rollcall/__init__.py b/rollcall/__init__.py index a24b5d2..e69de29 100644 --- a/rollcall/__init__.py +++ b/rollcall/__init__.py @@ -1,10 +0,0 @@ -#!/usr/bin/env python2 - -## -# rollcall -# https://github.com/numerals/rollcall.git -# -# Copyright (c) 2014 Sartaj Singh, Sumit Sahrawat -# Licensed under the MIT license. -## - diff --git a/rollcall/display.py b/rollcall/display.py index d436914..47f825f 100644 --- a/rollcall/display.py +++ b/rollcall/display.py @@ -44,7 +44,7 @@ def total_classes(json_dic, field=None): def classes_with_tag(json_dic, tag=fj.TAGS['p']): """ takes in a json dictionary - returns list of all the classes + returns list of all the classes with the tag """ classes = [] diff --git a/rollcall/exce.py b/rollcall/exce.py index d2dc812..5fe0367 100644 --- a/rollcall/exce.py +++ b/rollcall/exce.py @@ -22,6 +22,12 @@ class NoField(Exception): class UnknownTag(Exception): """ - Raised when tag is not recognised + Raised when tag is not recognised + """ + pass + +class DatabaseError(Exception): + """ + Raised when there is some problem with DB """ pass diff --git a/rollcall/func_json.py b/rollcall/func_json.py index 06f28e3..756a2b8 100644 --- a/rollcall/func_json.py +++ b/rollcall/func_json.py @@ -47,7 +47,7 @@ def update_status(json_dic, field, val): if no such field raises NoField exception """ if not json_dic.has_key(field): - raise exce.NoField("No such field: %s" %(field)) + raise exce.NoField("No such Field : %s" %(field)) json_dic[field] = val return json_dic[field] @@ -63,7 +63,7 @@ def gen_dict(semester_start, class_weekdays, semester_weeks=16): json_dict = {} for d in class_dates: - json_dict[format_date(d)] = "future" + json_dict[format_date(d)] = TAGS['f'] return json_dict @@ -77,7 +77,7 @@ def update_json_dict(json_dic, date, status): formatted_date = format_date(date) if not json_dic.has_key(formatted_date): - raise exce.NoField("No Field: %s" %(date)) + raise exce.NoField("No such Date : %s" %(date)) json_dic[formatted_date] = status return json_dic diff --git a/rollcall/tests/test_main/new.blah b/rollcall/gui/__init__.py similarity index 100% rename from rollcall/tests/test_main/new.blah rename to rollcall/gui/__init__.py diff --git a/rollcall/gui/rollcallGUIClass.py b/rollcall/gui/rollcallGUIClass.py new file mode 100644 index 0000000..a07b732 --- /dev/null +++ b/rollcall/gui/rollcallGUIClass.py @@ -0,0 +1,65 @@ +#!/usr/bin/env python2 + +""" +Contains the main GUI Class +""" + +import os +import gtk + + +def find_file(dire, fName): + """ + Generates the complete path of a file + returns the complete path + """ + path = os.path.join(os.path.dirname(dire), fName) + return path + + +def load_interface(dire, fName): + """ + Loads the interface + in particular loads the glade file + returns the builder + """ + fName = find_file(dire, fName) + builder = gtk.Builder() + builder.add_from_file(fName) + return builder + + +class rollcallGUIClass: + """ + Sets up the GUI interface + """ + def __init__(self): + + self.builder = load_interface(__file__, 'glade/rollcallGUI.glade') + self.save_objects() + self.builder.connect_signals(self.setup_signals()) + self.window.show_all() + + def setup_signals(self): + """ + Sets up the signals + """ + sig = {} + + return sig + + def save_objects(self): + """ + Get the required objects + """ + pass + + def close(self, *args): + """ + Handles Destroy Event + """ + gtk.main_quit() + + +if __name__ == '__main__': + gtk.main() diff --git a/rollcall/main.py b/rollcall/main.py index 29a785a..9db72c5 100644 --- a/rollcall/main.py +++ b/rollcall/main.py @@ -47,7 +47,7 @@ def get_json_file(sub): returns json as a dictionary """ if not fileExists(sub): - raise exce.SubjectError("Subject: %s does not exit" %(sub)) + raise exce.SubjectError("Subject: %s does not exist" %(sub)) with open(sub, "r") as recordFile: json_string = recordFile.read() @@ -109,22 +109,13 @@ def gen_percent(tag=fj.TAGS['p'], ext='.json', dire=pDir()): percent = display.percent(json_dic, tag) yield filename, percent -def fileDelete(fName): +def deleteSubject(fName): """ - Delete a file and return status + Delete a file and raises SubjectError if not Found """ - if fileExists(fName): - os.remove(fName) - return True - return False - -def delete(sub): - """ - Delete a subject - """ - if fileDelete(sub): - return True - return False + if not fileExists(fName): + raise exce.SubjectError("Subject: %s is not Found" %(fName)) + os.remove(fName) def reset(ext='.json', dire=pDir()): """ diff --git a/rollcall/sql/__init__.py b/rollcall/sql/__init__.py new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/rollcall/sql/__init__.py @@ -0,0 +1 @@ + diff --git a/rollcall/sql/rollcallSQLClass.py b/rollcall/sql/rollcallSQLClass.py new file mode 100644 index 0000000..bd0bd18 --- /dev/null +++ b/rollcall/sql/rollcallSQLClass.py @@ -0,0 +1,192 @@ +#!/usr/bin/env python2 +#ref : http://zetcode.com/db/sqlitepythontutorial/ + +""" +SQL Class rollcall +""" + +from __future__ import print_function +import os +import sys +import rollcall.exce as exce +from rollcall.func_json import TAGS +from rollcall.main import pDir, full_path_to + +try: + import sqlite3 as sql +except ImportError: + print("SQLite3 is not found!") + sys.exit() + +dbNameDef = 'rollcall.db' +dbPathDef = full_path_to(dbNameDef) + +def connect_db(dbPath = dbPathDef): + """ + Connect to DB + and Return the sqlite3.Connection object + """ + try: + conn = sql.connect(dbPath) + except: + raise exce.DatabaseError("Unable to open Database File!") + return conn + +def close_db_connection(conn): + """ + close the connection 'conn' + where conn is sqlite3.Connection object + """ + conn.close() + +def get_subjects(dbPath = dbPathDef): + """ + Get a List of All Subjects Present in DB + yield subject + """ + conn = connect_db(dbPath) + command = "SELECT name FROM sqlite_master WHERE type='table'" + try: + subQuery = conn.execute(command) + subList = subQuery.fetchall() + except: + conn.rollback() + close_db_connection(conn) + raise exce.DatabaseError("Some problem occurred in DB!") + close_db_connection(conn) + for sub in subList: + yield sub[0] + +def get_count_subject_tag(subName, tag = 'p', dbPath = dbPathDef): + """ + Get the number of classes in Subject with tag + """ + + if not TAGS.has_key(tag): + raise exce.UnknownTag("Tag: %s UNKNOWN" %(tag)) + + if not subName in get_subjects(dbPath): + raise exce.SubjectError("There is no records for %s." %(subName)) + + conn = connect_db(dbPath) + command = """SELECT COUNT(*) FROM %s + WHERE class_tag=\"%s\"""" % (subName, TAGS[tag]) + try: + count = conn.execute(command) + count = count.fetchall() + except: + conn.rollback() + close_db_connection(conn) + raise exce.DatabaseError("Some problem occurred in DB!") + close_db_connection(conn) + return count[0][0] + +def get_total_class_subject(subName, dbPath = dbPathDef): + """ + Get the total classes happened till date + """ + + if not subName in get_subjects(dbPath): + raise exce.SubjectError("There is no records for %s." %(subName)) + + conn = connect_db(dbPath) + command = """SELECT COUNT(*) FROM %s""" % (subName) + try: + count = conn.execute(command) + count = count.fetchall() + except: + conn.rollback() + close_db_connection(conn) + raise exce.DatabaseError("Some problem occurred in DB!") + close_db_connection(conn) + return count[0][0] + +#Todo : Add tests +def add_subject(subName, dbPath = dbPathDef): + """ + Add a Subject to the DB + """ + + if subName in get_subjects(dbPath): + raise exce.SubjectExists("Records for %s are already present." %(subName)) + + conn = connect_db(dbPath) + cur = conn.cursor() + command = """CREATE TABLE %s + ( + class_no INTEGER NOT NULL, + class_tag VARCHAR(10) NOT NULL + CONSTRAINT chk_tag CHECK (class_tag IN ("absent", + "present", "future", "holiday", "other")), + class_date DATETIME NOT NULL DEFAULT(DATETIME('now', 'localtime')), + PRIMARY KEY (class_no) + )""" % subName + try: + cur.execute(command) + conn.commit() + except: + conn.rollback() + close_db_connection(conn) + raise exce.DatabaseError("Some problem occurred in DB!") + close_db_connection(conn) + +#Todo : add tests +def delete_subject(subName, dbPath = dbPathDef): + """ + Delete a Subject if Made by mistake :) + """ + + if not subName in get_subjects(dbPath): + raise exce.SubjectError("There is no records for %s." %(subName)) + + conn = connect_db(dbPath) + cur = conn.cursor() + command = """DROP TABLE %s""" % subName + try: + cur.execute(command) + conn.commit() + except: + conn.rollback() + close_db_connection(conn) + raise exce.DatabaseError("Some problem occurred in DB!") + close_db_connection(conn) + +def update_subject(subName, tag = 'p', dbPath = dbPathDef): + """ + Update Subject with Tag + """ + + if not TAGS.has_key(tag): + raise exce.UnknownTag("Tag: %s UNKNOWN" %(tag)) + + if not subName in get_subjects(dbPath): + raise exce.SubjectError("There is no records for %s." %(subName)) + + conn = connect_db(dbPath) + cur = conn.cursor() + command = """INSERT INTO %s (class_tag) + VALUES (\"%s\")""" %(subName, TAGS[tag]) + try: + cur.execute(command) + conn.commit() + except: + conn.rollback() + close_db_connection(conn) + raise exce.DatabaseError("Some problem occurred in DB!") + close_db_connection(conn) + +def get_subject_tag_percent(subName, tag = 'p', dbPath = dbPathDef): + """ + Get Percent of tag in subName in total Records till Date + """ + + if not TAGS.has_key(tag): + raise exce.UnknownTag("Tag: %s UNKNOWN" %(tag)) + + if not subName in get_subjects(dbPath): + raise exce.SubjectError("There is no records for %s." %(subName)) + + count_tag = get_count_subject_tag(subName, tag, dbPath) + count_total = get_total_class_subject(subName, dbPath) + fract = count_tag * 1.0 / count_total + return fract*100 diff --git a/rollcall/sql/tests/__init__.py b/rollcall/sql/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/rollcall/sql/tests/helper.py b/rollcall/sql/tests/helper.py new file mode 100644 index 0000000..b099389 --- /dev/null +++ b/rollcall/sql/tests/helper.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python2 + +""" +Helper Module for SQL Tests +""" + +import os +from os.path import realpath, dirname +import sys + +import rollcall.sql.rollcallSQLClass as rcsql + +def hadd_subjects(dbPath): + """ + Helper Method which add subjects to DB at dbPath + """ + rcsql.add_subject('Maths', dbPath) + rcsql.add_subject('Physics', dbPath) + rcsql.add_subject('Chemistry', dbPath) + rcsql.add_subject('Python', dbPath) + +def hupdate_subjects(dbPath): + """ + Helper Method which updates each subject with two 'p' & one 'a' + """ + for subject in rcsql.get_subjects(dbPath): + rcsql.update_subject(subject, 'p', dbPath) + rcsql.update_subject(subject, 'p', dbPath) + rcsql.update_subject(subject, 'a', dbPath) diff --git a/rollcall/sql/tests/test_connections/__init__.py b/rollcall/sql/tests/test_connections/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/rollcall/sql/tests/test_connections/test_add_subject.py b/rollcall/sql/tests/test_connections/test_add_subject.py new file mode 100644 index 0000000..1055fc6 --- /dev/null +++ b/rollcall/sql/tests/test_connections/test_add_subject.py @@ -0,0 +1,25 @@ +import unittest +import os +from rollcall.sql.tests import helper +from rollcall.exce import SubjectExists +from rollcall.sql.rollcallSQLClass import full_path_to, add_subject + +class TestAddSubjects(unittest.TestCase): + """ + Testing class + """ + def setUp(self): + self.dbName = 'rollcall_check.db' + self.dire = os.path.dirname(__file__) + self.dbPath = full_path_to(self.dbName, self.dire) + helper.hadd_subjects(self.dbPath) + + def test_add_subject(self): + newName = 'Haskell' + oldName = 'Maths' + + self.assertRaises(SubjectExists, add_subject, oldName, self.dbPath) + add_subject(newName, self.dbPath) + + def tearDown(self): + os.remove(self.dbPath) diff --git a/rollcall/sql/tests/test_connections/test_connect_db.py b/rollcall/sql/tests/test_connections/test_connect_db.py new file mode 100644 index 0000000..59d4e1d --- /dev/null +++ b/rollcall/sql/tests/test_connections/test_connect_db.py @@ -0,0 +1,25 @@ +import unittest +import os +import sqlite3 as sql +from rollcall.sql.rollcallSQLClass import full_path_to, connect_db, close_db_connection +from rollcall.exce import DatabaseError + +class TestConnectDB(unittest.TestCase): + """ + Testing class + """ + def setUp(self): + self.dbName = 'rollcall_check.db' + self.dire = os.path.dirname(__file__) + self.dbPath = full_path_to(self.dbName, self.dire) + self.dummyPath = '/lol/' + + def test_connect_db(self): + self.conn = connect_db(self.dbPath) + self.assertTrue(isinstance(self.conn, sql.Connection)) + close_db_connection(self.conn) + + self.assertRaises(DatabaseError, connect_db, self.dummyPath) + + def tearDown(self): + os.remove(self.dbPath) diff --git a/rollcall/sql/tests/test_connections/test_count_subject_tag.py b/rollcall/sql/tests/test_connections/test_count_subject_tag.py new file mode 100644 index 0000000..a67f5d2 --- /dev/null +++ b/rollcall/sql/tests/test_connections/test_count_subject_tag.py @@ -0,0 +1,33 @@ +import unittest +import os +from rollcall.exce import SubjectError, UnknownTag +from rollcall.sql.tests import helper +from rollcall.sql.rollcallSQLClass import full_path_to, \ +get_count_subject_tag, get_total_class_subject, get_subjects + +class TestCountSubjectTag(unittest.TestCase): + """ + Testing class + """ + def setUp(self): + self.dbName = 'rollcall_check.db' + self.dire = os.path.dirname(__file__) + self.dbPath = full_path_to(self.dbName, self.dire) + helper.hadd_subjects(self.dbPath) + helper.hupdate_subjects(self.dbPath) + + def test_count_tag(self): + for eachSub in get_subjects(self.dbPath): + self.assertEqual(get_count_subject_tag(eachSub, 'p', self.dbPath), 2) + self.assertEqual(get_count_subject_tag(eachSub, 'a', self.dbPath), 1) + + self.assertEqual(get_total_class_subject(eachSub, self.dbPath), 3) + + goodName = 'Maths' + badName = 'Scala' + + self.assertRaises(SubjectError, get_count_subject_tag, badName, 'p', self.dbPath) + self.assertRaises(UnknownTag, get_count_subject_tag, goodName, 'zz', self.dbPath) + + def tearDown(self): + os.remove(self.dbPath) diff --git a/rollcall/sql/tests/test_connections/test_delete_subject.py b/rollcall/sql/tests/test_connections/test_delete_subject.py new file mode 100644 index 0000000..2ffe266 --- /dev/null +++ b/rollcall/sql/tests/test_connections/test_delete_subject.py @@ -0,0 +1,27 @@ +import unittest +import os +from rollcall.exce import SubjectError, DatabaseError +from rollcall.sql.tests import helper +from rollcall.sql.rollcallSQLClass import delete_subject, full_path_to + +class TestDeleteSubject(unittest.TestCase): + """ + Testing class + """ + def setUp(self): + self.dbName = 'rollcall_check.db' + self.dire = os.path.dirname(__file__) + self.dbPath = full_path_to(self.dbName, self.dire) + self.dummyPath = '/lol/' + helper.hadd_subjects(self.dbPath) + + def test_delete_subject(self): + newName = 'Haskell' + oldName = 'Maths' + + self.assertRaises(SubjectError, delete_subject, newName, self.dbPath) + delete_subject(oldName, self.dbPath) + self.assertRaises(DatabaseError, delete_subject, oldName, self.dummyPath) + + def tearDown(self): + os.remove(self.dbPath) diff --git a/rollcall/sql/tests/test_connections/test_get_subjects.py b/rollcall/sql/tests/test_connections/test_get_subjects.py new file mode 100644 index 0000000..cc9cc19 --- /dev/null +++ b/rollcall/sql/tests/test_connections/test_get_subjects.py @@ -0,0 +1,24 @@ +import unittest +import os +from rollcall.sql.tests import helper +from rollcall.sql.rollcallSQLClass import full_path_to, get_subjects + +class TestGetSubjects(unittest.TestCase): + """ + Testing class + """ + def setUp(self): + self.dbName = 'rollcall_check.db' + self.dire = os.path.dirname(__file__) + self.dbPath = full_path_to(self.dbName, self.dire) + helper.hadd_subjects(self.dbPath) + + def test_get_subject(self): + subList = list(get_subjects(self.dbPath)) + mySubList=['Maths', 'Physics', 'Chemistry', 'Python'] + for eachSub in mySubList: + self.assertTrue(eachSub in subList) + self.assertEqual(len(subList),len(mySubList)) + + def tearDown(self): + os.remove(self.dbPath) diff --git a/rollcall/sql/tests/test_connections/test_subject_tag_percent.py b/rollcall/sql/tests/test_connections/test_subject_tag_percent.py new file mode 100644 index 0000000..4b72ec2 --- /dev/null +++ b/rollcall/sql/tests/test_connections/test_subject_tag_percent.py @@ -0,0 +1,32 @@ +import unittest +import os +from rollcall.exce import SubjectError +from rollcall.sql.tests import helper +from rollcall.sql.rollcallSQLClass import full_path_to, get_subject_tag_percent,\ +get_subjects + +class TestUpdateSubject(unittest.TestCase): + """ + Testing class + """ + def setUp(self): + self.dbName = 'rollcall_check.db' + self.dire = os.path.dirname(__file__) + self.dbPath = full_path_to(self.dbName, self.dire) + self.dummyPath = '/lol/' + helper.hadd_subjects(self.dbPath) + helper.hupdate_subjects(self.dbPath) + + def test_subject_tag_percent(self): + newName = 'Haskell' + oldName = 'Maths' + + for eachSub in get_subjects(self.dbPath): + self.assertAlmostEqual(get_subject_tag_percent(oldName, 'p', self.dbPath), 100.0 * 2.0 / 3.0) + self.assertAlmostEqual(get_subject_tag_percent(oldName, 'a', self.dbPath), 100.0 * 1.0 / 3.0) + self.assertAlmostEqual(get_subject_tag_percent(oldName, 'h', self.dbPath), 0.0) + + self.assertRaises(SubjectError, get_subject_tag_percent, newName, 'p', self.dbPath) + + def tearDown(self): + os.remove(self.dbPath) diff --git a/rollcall/sql/tests/test_connections/test_update_subject.py b/rollcall/sql/tests/test_connections/test_update_subject.py new file mode 100644 index 0000000..7b7b25d --- /dev/null +++ b/rollcall/sql/tests/test_connections/test_update_subject.py @@ -0,0 +1,28 @@ +import unittest +import os +from rollcall.exce import SubjectError, UnknownTag +from rollcall.sql.tests import helper +from rollcall.sql.rollcallSQLClass import full_path_to, update_subject + +class TestUpdateSubject(unittest.TestCase): + """ + Testing class + """ + def setUp(self): + self.dbName = 'rollcall_check.db' + self.dire = os.path.dirname(__file__) + self.dbPath = full_path_to(self.dbName, self.dire) + self.dummyPath = '/lol/' + helper.hadd_subjects(self.dbPath) + + def test_update_subject(self): + newName = 'Haskell' + oldName = 'Maths' + + self.assertRaises(SubjectError, update_subject, newName, 'p', self.dbPath) + self.assertRaises(UnknownTag, update_subject, oldName, 'zz', self.dbPath) + + update_subject(oldName, 'a', self.dbPath) + + def tearDown(self): + os.remove(self.dbPath) diff --git a/rollcall/tests/__init__.py b/rollcall/tests/__init__.py index a24b5d2..e69de29 100644 --- a/rollcall/tests/__init__.py +++ b/rollcall/tests/__init__.py @@ -1,10 +0,0 @@ -#!/usr/bin/env python2 - -## -# rollcall -# https://github.com/numerals/rollcall.git -# -# Copyright (c) 2014 Sartaj Singh, Sumit Sahrawat -# Licensed under the MIT license. -## - diff --git a/rollcall/tests/test_main/test_fileDelete.py b/rollcall/tests/test_main/test_fileDelete.py index edde8c8..52180c7 100644 --- a/rollcall/tests/test_main/test_fileDelete.py +++ b/rollcall/tests/test_main/test_fileDelete.py @@ -1,7 +1,8 @@ import unittest import os from rollcall.tests import helper -from rollcall.main import fileDelete, fileExists +from rollcall.main import deleteSubject, fileExists +from rollcall.exce import SubjectError class TestfileDelete(unittest.TestCase): """ @@ -14,11 +15,11 @@ def test_fileDelete_file_exists(self): self.dummy = os.path.join(self.dire, 'dummy.json') helper.newFile(self.dummy) self.assertTrue(fileExists(self.dummy)) - self.assertTrue(fileDelete(self.dummy)) + deleteSubject(self.dummy) self.assertFalse(fileExists(self.dummy)) def test_fileDelete_file_does_not_exist(self): self.new = os.path.join(self.dire, 'new.json') self.assertFalse(fileExists(self.new)) - self.assertFalse(fileDelete(self.new)) + self.assertRaises(SubjectError, deleteSubject, self.new) self.assertFalse(fileExists(self.new)) diff --git a/rollcall/tests/test_main/test_reset.py b/rollcall/tests/test_main/test_reset.py index eec714a..1b85b4f 100644 --- a/rollcall/tests/test_main/test_reset.py +++ b/rollcall/tests/test_main/test_reset.py @@ -19,6 +19,11 @@ def test_reset_on_dir_with_json_files(self): helper.newFile(self.notjson) self.assertTrue(fileExists(self.dummy)) self.assertTrue(fileExists(self.new)) + self.assertTrue(fileExists(self.notjson)) reset('.json', self.dire) self.assertFalse(fileExists(self.dummy)) self.assertFalse(fileExists(self.new)) + self.assertTrue(fileExists(self.notjson)) + + def tearDown(self): + os.remove(self.notjson) diff --git a/setup.py b/setup.py index a24b5d2..18ff536 100644 --- a/setup.py +++ b/setup.py @@ -1,10 +1 @@ #!/usr/bin/env python2 - -## -# rollcall -# https://github.com/numerals/rollcall.git -# -# Copyright (c) 2014 Sartaj Singh, Sumit Sahrawat -# Licensed under the MIT license. -## - diff --git a/signature.txt b/signature.txt deleted file mode 100644 index a24b5d2..0000000 --- a/signature.txt +++ /dev/null @@ -1,10 +0,0 @@ -#!/usr/bin/env python2 - -## -# rollcall -# https://github.com/numerals/rollcall.git -# -# Copyright (c) 2014 Sartaj Singh, Sumit Sahrawat -# Licensed under the MIT license. -## -