Skip to content
Snippets Groups Projects
Commit 3e9a4912 authored by morkolai's avatar morkolai
Browse files

#29 Laget funksjon for å legge til 1 ord og delvis testet den

parent 98e53a44
No related branches found
No related tags found
1 merge request!15Kodebok-koder
Pipeline #73097 failed
"""Generate codes."""
import string
# https://realpython.com/lessons/cryptographically-secure-random-data-python/
import secrets
def get_code(length, mode='ascii'):
def get_code(length, mode="ascii"):
"""
Generate a single random code.
......@@ -27,9 +28,9 @@ def get_code(length, mode='ascii'):
characters = string.ascii_uppercase
if mode == 'digits':
if mode == "digits":
characters = string.digits
elif mode == 'combo':
elif mode == "combo":
characters = string.ascii_uppercase + string.digits
while i < length:
......@@ -39,7 +40,7 @@ def get_code(length, mode='ascii'):
return code
def get_code_set(size, length, mode='ascii'):
def get_code_set(size, length, mode="ascii"):
"""
Generate a set of codes.
......@@ -85,7 +86,7 @@ def get_code_length(quantity):
Lenght of code
"""
length = 0
while len(string.ascii_uppercase)**length < quantity:
while len(string.ascii_uppercase) ** length < quantity:
length = length + 1
return length
......@@ -7,20 +7,23 @@ import schedule
import soitool.coder
# Set name and path to (future) database
DBNAME = 'database'
DBNAME = "database"
CURDIR = os.path.dirname(__file__)
DBPATH = os.path.join(CURDIR, DBNAME)
# DDL-statements for creating tables
CODEBOOK = 'CREATE TABLE CodeBook' \
'(Word VARCHAR PRIMARY KEY, Category VARCHAR,' \
'Code CHAR(3) UNIQUE, Type int DEFAULT 0)'
CATEGORYWORDS = 'CREATE TABLE CategoryWords' \
'(Word VARCHAR PRIMARY KEY, Category VARCHAR)'
BYHEART = 'CREATE TABLE ByHeart(Word VARCHAR PRIMARY KEY)'
class Database():
CODEBOOK = (
"CREATE TABLE CodeBook"
"(Word VARCHAR PRIMARY KEY, Category VARCHAR,"
"Code VARCHAR UNIQUE, Type int DEFAULT 0)"
)
CATEGORYWORDS = (
"CREATE TABLE CategoryWords" "(Word VARCHAR PRIMARY KEY, Category VARCHAR)"
)
BYHEART = "CREATE TABLE ByHeart(Word VARCHAR PRIMARY KEY)"
class Database:
"""
Holds database-connection and related functions.
......@@ -32,17 +35,17 @@ class Database():
db_exists = os.path.exists(DBPATH)
if db_exists:
print('Connecting to existing DB.')
print("Connecting to existing DB.")
self.conn = sqlite3.connect(DBPATH)
print('DB-connection established.')
print("DB-connection established.")
else:
print('Creating new DB.')
print("Creating new DB.")
self.conn = sqlite3.connect(DBPATH)
self.create_tables()
print('DB created.')
print("DB created.")
self.fill_tables()
print('Tables filled with data.')
print("Tables filled with data.")
self.conn.row_factory = sqlite3.Row # Enables row['columnName']
......@@ -75,19 +78,24 @@ class Database():
codes = soitool.coder.get_code_set(len(entries), entries_len)
# Insert data in db
stmt = 'INSERT INTO CodeBook(Word, Category, Type, Code)' \
'VALUES(?,?,?,?)'
stmt = (
"INSERT INTO CodeBook(Word, Category, Type, Code)"
"VALUES(?,?,?,?)"
)
for word in entries:
self.conn.execute(stmt, (word['word'], word['category'],
word['type'], codes.pop()))
self.conn.execute(
stmt,
(word["word"], word["category"], word["type"], codes.pop()),
)
def fill_by_heart(self):
"""Read data from ByHeart.txt and fill DB-table ByHeart."""
file_path = os.path.join(CURDIR, "testdata/ByHeart.txt")
f = open(file_path, "r", encoding='utf-8')
f = open(file_path, "r", encoding="utf-8")
# Loop through words on file and insert them into ByHeart-table
stmt = 'INSERT INTO ByHeart(Word) VALUES(?)'
stmt = "INSERT INTO ByHeart(Word) VALUES(?)"
for expr in f:
self.conn.execute(stmt, (expr.rstrip(),))
f.close()
......@@ -95,7 +103,7 @@ class Database():
def fill_category_words(self):
"""Read data from CategoryWords.txt and fill DB-table CategoryWords."""
file_path = os.path.join(CURDIR, "testdata/CategoryWords.txt")
f = open(file_path, "r", encoding='utf-8')
f = open(file_path, "r", encoding="utf-8")
# Get number of categories on file
no_of_categories = int(f.readline().rstrip())
......@@ -108,7 +116,7 @@ class Database():
no_of_words = int(line[1].rstrip())
# Loop through words in category and add rows to DB
stmt = 'INSERT INTO CategoryWords(Word, Category) VALUES(?, ?)'
stmt = "INSERT INTO CategoryWords(Word, Category) VALUES(?, ?)"
for _ in range(no_of_words):
word = f.readline().rstrip()
self.conn.execute(stmt, (word, category,))
......@@ -123,11 +131,11 @@ class Database():
List of strings
Categories
"""
stmt = 'SELECT DISTINCT Category FROM CategoryWords'
stmt = "SELECT DISTINCT Category FROM CategoryWords"
queried = self.conn.execute(stmt)
categories = []
for row in queried:
categories.append(row['Category'])
categories.append(row["Category"])
return categories
......@@ -146,44 +154,82 @@ class Database():
[{'word': str, 'type': str, 'category': str, 'code': str}]
"""
# Get either full or small codebook
stmt = 'SELECT * FROM CodeBook'
stmt = "SELECT * FROM CodeBook"
if small:
stmt = stmt + ' WHERE Type = ?'
stmt = stmt + " WHERE Type = ?"
queried = self.conn.execute(stmt, (1,)).fetchall()
else:
queried = self.conn.execute(stmt).fetchall()
codebook = []
for entry in queried:
codebook.append({'word': entry['Word'],
'category': entry['Category'],
'type': entry['Type'],
'code': entry['Code']})
codebook.append(
{
"word": entry["Word"],
"category": entry["Category"],
"type": entry["Type"],
"code": entry["Code"],
}
)
return codebook
def update_codebook(self):
"""Update codes in DB."""
# Get all the words (PK)
stmt = 'SELECT Word FROM CodeBook'
stmt = "SELECT Word FROM CodeBook"
words = self.conn.execute(stmt).fetchall()
# Get number of entries
stmt = 'SELECT COUNT(*) FROM CodeBook'
stmt = "SELECT COUNT(*) FROM CodeBook"
number_of_entries = self.conn.execute(stmt).fetchall()[0][0]
# Generate new codes
code_len = soitool.coder.get_code_length(number_of_entries)
codes = soitool.coder.get_code_set(number_of_entries, code_len)
# Statement for update
stmt = 'UPDATE CodeBook SET Code = ? WHERE Word = ?'
stmt = "UPDATE CodeBook SET Code = ?"
# Wiping Code column because of UNIQUE
for i in range(number_of_entries):
self.conn.execute(stmt, (None, words[i][0]))
self.conn.execute(stmt, (None,))
# Fill Code column with new codes
stmt = stmt + "WHERE Word = ?"
for i in range(number_of_entries):
self.conn.execute(stmt, (codes.pop(), words[i][0]))
print("Code in CodeBook updated")
def add_code(self, word, mode="ascii"):
"""
Add a code to the new word.
Parameters
----------
word : string
The word to generate a code for.
mode : string
'ascii' for letters (default), 'digits' for digits and 'combo'
for combination of letters and digits.
"""
# Get length of codes
stmt = "SELECT COUNT(*) FROM CodeBook"
number_of_entries = self.conn.execute(stmt).fetchall()[0][0]
# TODO ordet ligger alt inne så her er det feil
code_len = soitool.coder.get_code_length(number_of_entries)
# If adding a word makes the code longer, update whole CodeBook in db
if code_len < soitool.coder.get_code_length(number_of_entries + 1):
self.update_codebook()
else:
# Get all the used codes
stmt = "SELECT Code FROM CodeBook"
codes_li = self.conn.execute(stmt).fetchall()
codes_set = set([i[:][0] for i in codes_li])
# Get new unique code
code = soitool.coder.get_code(code_len, mode)
while code in codes_set:
code = soitool.coder.get_code(code_len, mode)
# Update db with new code
stmt = "UPDATE CodeBook SET Code = ? WHERE Word = ?"
self.conn.execute(stmt, (code, word))
def update_codebook_auto(self, clock):
"""
Update DB every day at clock.
......
......@@ -32,7 +32,7 @@ class DatabaseTest(unittest.TestCase):
no_of_expr = len(expressions)
# Retrieve expressions from DB:
stmt = 'SELECT * FROM ByHeart'
stmt = "SELECT * FROM ByHeart"
queried = self.database.conn.execute(stmt).fetchall()
# Assert equal amount of expressions in table and file
......@@ -73,7 +73,7 @@ class DatabaseTest(unittest.TestCase):
self.assertEqual(categories_db, categories_file)
# Retrieve data from DB
stmt = 'SELECT * FROM CategoryWords'
stmt = "SELECT * FROM CategoryWords"
queried = self.database.conn.execute(stmt).fetchall()
# Assert equal categories in table and file
......@@ -89,7 +89,7 @@ class DatabaseTest(unittest.TestCase):
file.close()
# Get data from db
stmt = 'SELECT * FROM CodeBook'
stmt = "SELECT * FROM CodeBook"
actual = self.database.conn.execute(stmt).fetchall()
# Check same lenght
......@@ -100,10 +100,10 @@ class DatabaseTest(unittest.TestCase):
# Check equality
for i, entry in enumerate(expected):
self.assertEqual(entry['word'], actual[i][0])
self.assertEqual(entry['category'], actual[i][1])
self.assertEqual(entry["word"], actual[i][0])
self.assertEqual(entry["category"], actual[i][1])
self.assertRegex(actual[i][2], "[A-Z]{" + str(code_len) + "}")
self.assertEqual(entry['type'], actual[i][3])
self.assertEqual(entry["type"], actual[i][3])
def test_get_categories(self):
"""Assert function get_categories works as expected."""
......@@ -145,10 +145,10 @@ class DatabaseTest(unittest.TestCase):
# Compare contents
for i, entry in enumerate(expected):
self.assertEqual(entry['word'], actual[i]['word'])
self.assertEqual(entry['category'], actual[i]['category'])
self.assertRegex(actual[i]['code'], "[A-Z]{" + str(code_len) + "}")
self.assertEqual(entry['type'], actual[i]['type'])
self.assertEqual(entry["word"], actual[i]["word"])
self.assertEqual(entry["category"], actual[i]["category"])
self.assertRegex(actual[i]["code"], "[A-Z]{" + str(code_len) + "}")
self.assertEqual(entry["type"], actual[i]["type"])
def test_get_codebook_small(self):
"""Assert function get_codebook only return the small codebook."""
......@@ -161,7 +161,7 @@ class DatabaseTest(unittest.TestCase):
# Fill expected with only small codebook entries
expected = []
for entry in data:
if entry['type'] == 1:
if entry["type"] == 1:
expected.append(entry)
# Get small codebook from db
......@@ -175,29 +175,44 @@ class DatabaseTest(unittest.TestCase):
# Compare contents
for i, entry in enumerate(expected):
self.assertEqual(entry['word'], actual[i]['word'])
self.assertEqual(entry['category'], actual[i]['category'])
self.assertRegex(actual[i]['code'], "[A-Z]{" + str(code_len) + "}")
self.assertEqual(entry['type'], actual[i]['type'])
self.assertEqual(entry["word"], actual[i]["word"])
self.assertEqual(entry["category"], actual[i]["category"])
self.assertRegex(actual[i]["code"], "[A-Z]{" + str(code_len) + "}")
self.assertEqual(entry["type"], actual[i]["type"])
def test_update_codebook(self):
"""Test that the codes get updated."""
# Get number of entries
stmt = 'SELECT COUNT(*) FROM CodeBook ORDER BY Word'
stmt = "SELECT COUNT(*) FROM CodeBook ORDER BY Word"
number_of_entries = self.database.conn.execute(stmt).fetchall()[0][0]
# Get old and updated word-code combinations
stmt = 'SELECT Word, Code FROM CodeBook'
stmt = "SELECT Word, Code FROM CodeBook"
old = self.database.conn.execute(stmt).fetchall()
self.database.update_codebook()
updated = self.database.conn.execute(stmt).fetchall()
# Collect approximately score of not updated pairs
pairs = 0
for i in range(0, number_of_entries, 2):
if old[i]['Code'] == updated[i]['Code']:
if old[i]["Code"] == updated[i]["Code"]:
pairs = pairs + 1
# Test that at least some of the test are new
self.assertTrue(pairs < number_of_entries)
def test_add_code(self):
"""Test add a single code to CodeBook"""
testdata = ("testword", "testcategory")
stmt = "INSERT INTO CodeBook (Word, Category) VALUES (?,?)"
self.database.conn.execute(stmt, (testdata[0], testdata[1]))
self.database.add_code(testdata[0])
stmt = "SELECT Code FROM CodeBook WHERE Word = ?"
code = self.database.conn.execute(stmt, (testdata[0],)).fetchall()[0][
0
]
self.assertRegex(code, "[A-Z0-9]")
if __name__ == '__main__':
def test_add_code_extending_code_length(self):
"""Test all codes get updated when code lenght i changed."""
if __name__ == "__main__":
unittest.main()
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment