PMCS

Download as docx, pdf, or txt
Download as docx, pdf, or txt
You are on page 1of 137

Import matplotlib.

pyplot as plt

Import mysql.connector as con

Import pickle

Import os

Import re

From tkinter.messagebox import *

From typing import Callable

From tkinter import *

From random import *

From string import *

From datetime import datetime as dt

From PIL import ImageTk, Image


From tkinter import ttk

From time import sleep

Def connect() -> None:

“””establishes connection between Python & MySQL software”””

Global mydb, cursor

Try:

Mydb = con.connect(host=”localhost”, user=”root”, password=”root”)

Cursor = mydb.cursor()

Except con.InterfaceError:
Return no_window_message(showerror, “CANONOT CONTINUE
PROGRAM”, “UNABLE TO ESTABLISH CONNECTION WITH MySQL”)

Else:

Try:

Cursor.execute(“create database if not exists project”)

Except:

Return no_window_message(showerror, “ERROR”, “DATABASE


CREATION UNSUCCESSFUL”)

Else:

Cursor.execute(“use project”)

Finally:

Mydb.commit()

If mydb.is_connected():
# if connection is successful, create the Tables and start __main__
application

Create()

Loading()

Else:

No_window_message(showerror, “ERROR”, “SORRY! WE WERE UNABLE


TO START THE APPLICATION”)

Def close() -> None:

“””closes the connection between Python & MySQL software”””

Try:

Cursor.close()
Mydb.close()

Except con.InternalError:

No_window_message(showwarning, “WARNING”, “UNABLE TO CLOSE


CONNECTION TO MySQL”)

Finally:

No_window_message(showinfo, “THANK YOU”, “Thank You for using


KeepMyPass”)

Def create() -> None:

“””creates the required Tables (‘passwords’, ‘contacts’ and ‘events’) in the


database”””

Nu, pk = “not null”, “primary key”

Table, user = “create table if not exists”, f”\nuser varchar(50) {nu}”


Passwords: str = f”””

{table} passwords ({user}, username varchar(50), password


varchar(256) {nu}, app_url varchar(100), notes text,

{pk} (user, username)

)”””

Contacts: str = f”””

{table} contacts ({user}, fullname varchar(50) {nu}, contact_no


varchar(15),

Alternate_no varchar(15), birthday date, city varchar(25) default “NEW


DELHI”,

{pk} (user, contact_no)

)”””

Events: str = f”””


{table} events ({user}, evttitle varchar(30), evtdate date {nu},

Evttime time default “00:00:00”, completed varchar(3) default “NO”,

{pk} (user, evttitle)

)”””

For query in passwords, contacts, events:

Try:

Cursor.execute(query)

Except:

Pass

Else:

Mydb.commit()
Def pass_keys() -> None:

“””creates simple encryption and deceyption keys and sets them as global
variables”””

Global ekey1, dkey1, ekey2, dkey2

Chrs1: list[str] = list(printable)[:-5]

Chrs2: list[str] = chrs1.copy()

Shuffle(chrs2)

Numbers: list[str] = []

While len(numbers) != len(chrs1):

Num = str(int.from_bytes(os.urandom(2), “little”))

If len(num) == 3 and num not in numbers:


Numbers.append(num)

Ekey1, dkey1 = dict(zip(chrs1, numbers)), dict(zip(numbers, chrs1))

Ekey2, dkey2 = dict(zip(numbers, chrs2)), dict(zip(chrs2, numbers))

Def valid_username(uname: str) -> None:

“””displays an error if an entered username is invalid”””

If not uname or uname.isspace():

Return showwarning(“Required Field”, “Username / Mail Address cannot


be Empty”, parent=UserVault)

If “@” in uname:
If uname.count(“@”) != 1 or “_” in uname or uname[0] == “@”:

Return showerror(“Error”, “Invalid Mail – ID”, parent=UserVault)

Match = re.search(r”[a-zA-Z0-9.!#$%&’*+\/=?^_`{|}~-]+@[a-zA-Z0-9-]
+(?:\.[a-zA-Z0-9-]+)*”, uname)

If match is None or match.span()[0] != 0:

Return showerror(“Error”, “Invalid Mail – ID”, parent=UserVault)

If len(uname) not in range(5, 51):

Return showerror(“Error”, “Username / Mail Address must be between 5


– 50 characters”, parent=UserVault)

If uname[0] not in (ascii_letters + “_”):

Return showerror(“Error”, “First letter of Username must be an


underscore or alphabet”, parent=UserVault)
For char in uname:

If char not in (ascii_letters + “_” + “.” + digits):

Return showerror(“Error”, “Invalid character found in Username”,


parent=UserVault)

Def valid_password(password: str) -> None:

“””displays an error if an entered password is invalid”””

If not password or password.isspace():

Return showwarning(“Required Field”, “Password cannot be Empty”,


parent=UserVault)

If len(password) not in range(8, 129):


Return showerror(“Error”, “Password must be between 8 – 128
characters”, parent=UserVault)

For char in password:

If char not in printable[:-5]:

Return showerror(“Error”, “Invalid character found in Password”,


parent=UserVault)

Def valid_contact(contact: str) -> None:

“””displays an error if an entered contact number is invalid”””

If not contact.isdecimal() and not contact.startswith(“+”):

Return showerror(“Error”, “Invalid character found in Contact Number”,


parent=UserVault)
If len(contact) not in range(3, 16):

Return showerror(“Error”, “Contact Number must be between 3 – 15


digits”, parent=UserVault)

Def encrypt(string: str) -> str:

“””encrypts the given string and returns it”””

String, copy = string[::-1].swapcase(), “”

For char in string:

Copy += ekey1[char] + “ “

Enc = [ekey2[num] for num in copy.split()]


Encrypted = “”

For char in enc:

Encrypted += char + choice(punctuation)

Return encrypted

Def decrypt(string: str) -> str:

“””decrypts the given string and returns it”””

Dec = [dkey2[char] for char in string[::2]]

String, copy = “”.join(dec), “”

For I in range(0, len(string), 3):


Copy += dkey1[string[i:i+3]]

Decrypted = copy.swapcase()[::-1]

Return decrypted

Def generate(length: int, specs: tuple[bool, 4]) -> str:

“””returns a random password generated with the given specifications

:param:

Specs: tuple[bool, bool, bool, bool]: the entries in the tuple represent

Uppercase, Lowercase, Digits, Special characters respectively”””

If not any(specs):
Return showwarning(“Required – Character Type”, “Select atleast one
type of character”, parent=UserVault)

Char_type = {0: ascii_uppercase, 1: ascii_lowercase, 2: 3*digits, 3:


punctuation}

Available = password = “”

For I in range(4):

Available += char_type[i] if specs[i] else “”

Chars = list(available) * 20

Shuffle(chars)

For _ in range(length):

Try:
Index = int.from_bytes(os.urandom(2), “big”) // randint(10, 20)

Password += chars[index]

Except IndexError:

Password += choice(chars)

For _ in range(randrange(1, 4)):

Sample1 = sample(chars, randint(25, len(available)))

Sample2 = sample(chars, len(sample1))

Table = str.maketrans(dict(zip(sample1, sample2)))

Password = password.translate(table)

Return password
Def no_window_message(

Messagebox: Callable[[str, str, Tk], None],

Title: str, message: str

) -> None:

“””displays messageboxes without any parent window, if required”””

Temp_window = Tk()

Temp_window.withdraw()

Messagebox(title=title, message=message, parent=temp_window)

Temp_window.destroy()

Def global_vars() -> None:


“””defines various variables required globally”””

Global root, style, color, IMAGES, ICONS, CURSORS, FILES, USER_COUNT,


CREDENTIALS

Root: Tk = Tk() # initializes the main-window

Root.resizable(False, False) # prevents resizing of the window

USER_COUNT: int = 0 # count number of Users already present

Color: str = “#dff3ef” # background color for Entry boxes and Text
boxes

Style = ttk.Style() # configure the stylizing of tabular data

# initialize tuples containing all required icons and image objects through
their location

ICONS: tuple[str] = tuple(icon for icon in os.listdir(os.path.join(os.getcwd(),


“Icons”)))
IMAGES: tuple[ImageTk] = tuple(

ImageTk.PhotoImage(Image.open(image))

For image in os.listdir(os.path.join(os.getcwd(), “Images”))

# initialize a tuple containing all required CURSORS types

CURSORS: tuple[str] = (“hand2”, “starting”, “wait”, “arrow”,


“question_arrow”, “no”, “xterm”, “plus”)

# initialize binary filenames in a tuple

FILES: tuple[str] = (“passwords.dat”, “pkeys.dat”)

# initialize the Admin and the password to Admin Mode


CREDENTIALS: tuple[str] = (“Divyajeet Singh”, “Python KeepMyPass GUI
Project”)

If not os.path.isfile(FILES[0]):

Return

With open(file=FILES[0], mode=”rb”) as file:

While True:

Try:

Record = pickle.load(file)

Except EOFError:

Break

Else:

USER_COUNT += 1
USER_COUNT //= 16

Def empty_screen(screen: Tk, don’t_destroy: Tk|None = None) -> None:

“””empties all widgets from the mentioned ‘screen’”””

For name, widget in tuple(screen.children.items()):

If “toplevel” not in name and widget != don’t_destroy:

Widget.destroy()

Try:

Window.destroy()

Except NameError:

Pass

Plt.close(“all”)
Def back_button(screen: Tk, window: Callable[[], None]) -> None:

“””places a back-button on ‘screen’ which takes the control back to


‘window’”””

Button(screen, image=IMAGES[0], bd=3, command=window,


cursor=CURSORS[0]).place(x=0, y=0)

Def hideshow(

Button: Button, entrybox: Entry, show_img: ImageTk, hide_img: ImageTk

) -> None:

“””hides and shows the Passwords according to the User’s choice”””


If str(button.cget(“image”)) == str(hide_img):

Img, char = show_img, “\N{BULLET}”

Else:

Img, char = hide_img, “”

Button.config(image=img)

Entrybox.config(show=char)

Def exit_app(parent: Tk) -> None:

“””displays the main exit-prompt on ‘parent’ window”””

If askyesno(“EXIT?”, “Do you want to quit KeepMyPass?”, parent=parent):


Plt.close(“all”)

Root.destroy()

Close()

Def loading() -> None:

“””displays the loading screen and starts the application”””

Global_vars()

Root.geometry(“548x210”)

Root.title(“Loading\N{HORIZONTAL ELLIPSIS}”)

Root.iconbitmap(ICONS[0])
Screen = Label(cursor=CURSORS[1])

Screen.place(x=0, y=0)

For _ in range(randint(5, 7)):

For image in IMAGES[1:4]:

Screen.config(image=image)

Root.update()

Sleep(uniform(0.115, 0.225))

Screen.destroy()

Root.geometry(“450x22”)

Bar = ttk.Progressbar(mode=”determinate”, length=450,


orient=HORIZONTAL, cursor=CURSORS[2])

Bar.place(x=0, y=0)
While bar[“value”] < 100:

Bar[“value”] += uniform(0.01, 0.35)

Root.update()

Sleep(random() ** randrange(500, 1001, 50))

Root.config(cursor=CURSORS[3])

Bar.destroy()

No_window_message(showinfo, “Successful!”, “Welcome to the


Application”)

Intro_window()

Def intro_window() -> None:


“””starts the intro-window for the application”””

Root.geometry(“544x544”)

Root.title(“KeepMyPass – Password Manager”)

Root.iconbitmap(ICONS[1])

Welcome = list(IMAGES[4:7])

Def next_window():

“””opens the next image in the intro-window”””

Try:

Del welcome[0]

Button(image=welcome[0], command=next_window,
cursor=CURSORS[0]).place(x=0, y=0)
Except IndexError:

# when intro finishes, the main app is started

Main_window()

Button(image=welcome[0], command=next_window,
cursor=CURSORS[0]).place(x=0, y=0)

Root.mainloop()

Def main_window() -> None:

“””starts the main-window/homepage for the application”””

Empty_screen(root)

Root.geometry(“772x544”)
Root.title(“KeepMyPass”)

Root.iconbitmap(ICONS[2])

Def info() -> None:

“””shows help on how the application is used”””

Messages = [

“To use the Application, you must first Sign Up.”,

“Multiple Users can be created, but each will have access to one’s
own data only.”,

“You only need to remember one Password – your MasterPassword.”,

“Once Signed Up, you can Log in to your Secure Vault and store: {0}
Passwords {0} Contacts {0} Events”,

“If you ever forget your MasterPassword, we may be able to help you
to retrieve your lost account.”,
“A Password-Protected Admin Mode allows the developer to search for
any data!”

Showinfo(“User Guide & Manual”, “\n”.join(messages).format(“\n\t\


N{BULLET}”))

Label(image=IMAGES[7]).place(x=0, y=0)

Button(image=IMAGES[8], bd=3, command=info,


cursor=CURSORS[4]).place(x=699.375, y=0)

Coms = [login_window, signup_window, admin_mode, (lambda:


exit_app(root))]

For I in range(4):

St, cur = (DISABLED, CURSORS[5]) if not (I or USER_COUNT) else


(NORMAL, CURSORS[0])

Button(image=IMAGES[i+9], bd=0, command=coms[i], state=st,


cursor=cur).place(x=480, y=75*I + 146.25)
Root.protocol(“WM_DELETE_WINDOW”, (lambda: exit_app(root)))

Def signup_window() -> None:

“””starts the signup-window for the User”””

Empty_screen(root)

Root.title(“Sign Up for KeepMyPass”)

Root.iconbitmap(ICONS[3])

Label(image=IMAGES[13]).place(x=0, y=0)

User = Entry(width=27, font=(“ariel”, 19, “bold”), bd=2, relief=FLAT,


cursor=CURSORS[6], bg=color)

User.insert(0, “Enter your FullName”)


User.bind(“<Return>”, (lambda event: create_user(user)))

User.place(x=9, y=177.5)

St, cur = (NORMAL, CURSORS[4]) if USER_COUNT else (DISABLED,


CURSORS[5])

Button(image=IMAGES[14], bd=0, command=(lambda:


create_user(user)), cursor=CURSORS[0]).place(x=22.5, y=236)

Button(image=IMAGES[15], bd=0, command=login_window, state=st,


cursor=cur).place(x=52.5, y=311)

Back_button(root, main_window)

Def create_user(user: Entry) -> None:

“””checks the name input by the User and inputs the MasterPassword”””
Name = user.get().strip().title()

If name.casefold() in {“enter your fullname”, “”}:

Return showwarning(“Required Field”, “Name cannot be Empty”)

If check_name(name):

Showwarning(“Matching User Found”, “User Already Exists!”)

If askyesno(“Log In”, “Log in instead?”):

Return login_window()

If askyesno(“Help”, “Forgot Password? Need Help?”):

Return forgot_pass()
Return user.delete(0, END)

If len(name) not in range(5, 51):

Showerror(“Error”, “Name must be between 5 – 50 characters”)

Return user.delete(0, END)

For char in name:

If char not in (ascii_letters + “ “ + “.”):

Showerror(“Invalid character found”, “Name can only contain


Letters”)

Return user.delete(0, END)

Empty_screen(root)

Label(image=IMAGES[16]).place(x=0, y=0)
Specs = dict(width=22, bd=2, font=(“ariel”, 19, “bold”), relief=FLAT,
cursor=CURSORS[6], bg=color)

Mp1, mp2, show, hide = Entry(**specs), Entry(**specs), *IMAGES[17:19]

Mp1.place(x=18, y=206)

Mp2.place(x=18, y=257.5)

Mp1.insert(0, “Choose a MasterPass”)

Mp2.insert(1, “Confirm MasterPass”)

Hs1 = Button(image=hide, bd=2, command=(lambda: hideshow(hs1,


mp1, show, hide)), cursor=CURSORS[0])

Hs2 = Button(image=hide, bd=2, command=(lambda: hideshow(hs2,


mp2, show, hide)), cursor=CURSORS[0])

Hs1.place(x=333.75, y=200)

Hs2.place(x=333.75, y=252)
Values = (name, mp1, mp2, write_file, end_window)

Mp2.bind(“<Return>”, (lambda event: set_mpass(*values)))

Button(image=IMAGES[19], bd=0, command=(lambda:


set_mpass(*values)), cursor=CURSORS[0]).place(x=35.5, y=322.5)

Back_button(root, signup_window)

Def check_name(name_user: str) -> bool:

“””returns True if User already exists, False otherwise”””

Global ekey1, dkey1, ekey2, dkey2

If not USER_COUNT:
Return False

With open(FILES[0], “rb”) as file1, open(FILES[1], “rb”) as file2:

Exists = False

For I in range(1, 16*USER_COUNT + 1):

Pwd_dict = pickle.load(file1)

If not (I % 16):

Ekey1, dkey1, ekey2, dkey2 = pickle.load(file2)

Name = tuple(pwd_dict.keys())[0]

If name_user == decrypt(name):

Exists = True

Break
Return exists

Def set_mpass(

Name_user: str, mpass1: Entry, mpass2: Entry,

File_func: Callable[[str, str], None], next_window:: Callable[[], None]

) -> None:

“””checks the validity of the MasterPassword”””

M1, m2, delete = mpass1.get(), mpass2.get(), False

If any([not m1, not m2, m1.isspace(), m2.isspace()]):

Delete, title, message, box = True, “Required Field”, “MasterPassword


cannot be Empty”, showwarning
Elif m1 != m2:

Delete, title, message, box = True, “Error”, “The MasterPassword you


re-entered does not match”, showerror

Elif len(m1) not in range(8, 129):

Delete, title, message, box = True, “Error”, “MasterPassword must be


between 8 – 128 characters”, showerror

If delete:

Mpass1.delete(0, END)

Mpass2.delete(0, END)

Return box(title, message)


If m1.isalpha() or m2.isdecimal():

Message = “A MasterPassword containing only {} is weak. Still


continue?”

If not askyesno(“Weak Password”, message.format(“Letters” if


m1.isalpha() else “Digits”)):

Return

File_func(name_user, m2)

Next_window(name_user)

Def write_file(name: str, masterpwd: str) -> None:

“””writes fake details & User’s details and keys in the Binary Files”””

With open(FILES[0], “ab”) as file1, open(FILES[1], “ab”) as file2:


Chars, truth = printable[:-5]*20, [True, False]

Pass_keys()

For I in range(15):

New_name: str = “”.join(sample(chars, randint(5, 50 )))

New_pass: str = “”.join(sample(chars, randint(8, 128)))

If choice(truth):

Pwds = [new_pass]

Elif choice(truth):

Pwd1 = “”.join(sample(chars, randint(8, 128)))

Pwds = [new_pass, pwd1]

Else:
Pwd1 = “”.join(sample(chars, randint(8, 128)))

Pwd2 = “”.join(sample(chars, randint(8, 128)))

Pwds = [new_pass, pwd1, pwd2]

Pickle.dump({new_name: pwds}, file1)

Pickle.dump({encrypt(name): [encrypt(masterpwd)]}, file1)

Pickle.dump((ekey1, dkey1, ekey2, dkey2), file2)

Def end_window(name: str) -> None:

“””finishes the MasterPassword creation and increments the number of


Users”””

Global USER_COUNT
Empty_screen(root)

Root.title(“Successful!”)

Root.iconbitmap(ICONS[4])

Label(image=IMAGES[20]).place(x=0, y=0)

Button(image=IMAGES[21], bd=0, command=(lambda:


create_vault(name)), cursor=CURSORS[0]).place(x=412.5, y=247.5)

Button(image=IMAGES[22], bd=0, command=main_window,


cursor=CURSORS[0]).place(x=412.5, y=322.5)

USER_COUNT += 1

Def login_window() -> None:

“””starts the log-in window for the User”””


Empty_screen(root)

Root.title(“Log In to KeepMyPass”)

Root.iconbitmap(ICONS[5])

Label(image=IMAGES[23]).place(x=0, y=0)

Specs = dict(width=22, bd=2, font=(“ariel”, 19, “bold”), relief=FLAT,


cursor=CURSORS[6], bg=color)

Urname, passwd, show, hide = Entry(**specs), Entry(**specs),


*IMAGES[17:19]

Urname.place(x=393.75, y=210)

Passwd.place(x=393.75, y=262.5)

Urname.insert(0, “Enter Name of User”)


Passwd.insert(1, “Enter MasterPassword”)

Passwd.bind(“<Return>”, (lambda event: check_login(urname, passwd)))

Hs = Button(image=hide, bd=2, command=(lambda: hideshow(hs,


passwd, show, hide)), cursor=CURSORS[0])

Hs.place(x=709.5, y=256.5)

Button(image=IMAGES[24], bd=0, command=forgot_pass,


cursor=CURSORS[4]).place(x=422.55, y=311.25)

Button(image=IMAGES[21], bd=0, command=(lambda:


check_login(urname, passwd)), cursor=CURSORS[0]).place(x=390, y=367.5)

Back_button(root, main_window)

Def check_login(name: Entry, pwd: Entry) -> None:

“””checks the name of User & MasterPassword associated with it”””


Global ekey1, dkey1, ekey2, dkey2

Name_user, passwd = name.get().strip().title(), pwd.get()

If not (name_user and len(name_user) in range(5, 51)):

Return showwarning(“Required Field”, “Please enter FullName of User”)

If not check_name(name_user):

Name.delete(0, END)

Pwd.delete(0, END)

Return showerror(“Invalid User”, “User does not Exist”)

With open(FILES[0], “rb”) as file1, open(FILES[1], “rb”) as file2:


Matched = False

For I in range(1, 16*USER_COUNT + 1):

Pwd_dict = pickle.load(file1)

If I % 16:

Continue

Ekey1, dkey1, ekey2, dkey2 = pickle.load(file2)

Kname, pwds = tuple(pwd_dict.items())[0]

If name_user == decrypt(kname):

If passwd == decrypt(pwds[-1]):

Showinfo(“Successful”, “Logged in Successfully!”)

Matched = True

Break
If matched:

Return create_vault(name_user)

Pwd.delete(0, END)

Showerror(“Invalid Password”, “Name of User & MasterPassword do not


match”)

Def forgot_pass() -> None:

“””helps in retrieving lost Account of a User”””

Empty_screen(root)

Root.title(“Retrieve Lost Account”)


Root.iconbitmap(ICONS[6])

Def info() -> None:

“””shows how the lost User Account is retrieved”””

Messages = [

“To retrieve your lost account, your must enter any old
MasterPassword that you may remember.”,

“It doesn’t have to be the entire password, instead all you need is
any 8 matching characters.”,

“Unfortunately, we can’t guarantee to retrieve your account.”,

“Remember, this is not a case-sensitive checking.”,

“As a tip, you should choose the MasterPassword to be one you don’t
use elsewhere.”

]
Showinfo(“Forgot Password Help”, “ “.join(messages))

Label(image=IMAGES[25]).place(x=0, y=0)

Specs = dict(width=22, bd=2, font=(“ariel”, 19, “bold”), relief=FLAT,


cursor=CURSORS[6], bg=color)

Name_user, lastpass = Entry(**specs), Entry(**specs)

Name_user.place(x=416.25, y=240)

Lastpass.place(x=416.25, y=292.5)

Name_user.insert(0, “FullName of User”)

Lastpass.insert(1, “Last Password”)

Lastpass.bind(“<Return>”, (lambda event: retrieve_account(name_user,


lastpass)))

Button(image=IMAGES[8], bd=3, command=info,


cursor=CURSORS[4]).place(x=699.375, y=0)
Button(image=IMAGES[26], bd=0, command=(lambda:
retrieve_account(name_user, lastpass)), cursor=CURSORS[0]).place(x=390,
y=360)

Back_button(root, login_window)

Def retrieve_account(uname: Entry, lastpass: Entry) -> None:

“””retrieves the lost account of a User and prompts to change the


MasterPassword”””

Global ekey1, dkey1, ekey2, dkey2

Name, passwd = uname.get().strip().title(), lastpass.get().casefold()

If not check_name(name):
If not name:

Return showwarning(“Required Field”, “Please enter FullName of


User”)

Showerror(“Invalid User”, “User does not Exist”)

Return uname.delete(0, END)

If not passwd or passwd.isspace():

Return showwarning(“Required Field”, “Please enter the last password


you remember”)

If len(passwd) not in range(8, 129):

Showwarning(“Invalid Password”, “Please enter 8 – 128 matching


charaters of your old MasterPassword”)

Return lastpass.delete(0, END)


With open(FILES[0], “rb”) as file1, open(FILES[1], “rb”) as file2:

Retrieved = False

For I in range(1, 16*USER_COUNT + 1):

Pwd_dict = pickle.load(file1)

If I % 16:

Continue

Ekey1, dkey1, ekey2, dkey2 = pickle.load(file2)

Kname, pwds = tuple(pwd_dict.items())[0]

If name != decrypt(kname):

Continue
For pwd in pwds:

If passwd in decrypt(pwd).casefold():

Showinfo(“Successful”, “Account Retrieval Successful!”)

Retrieved = True

Break

If retrieved:

Return change_masterpass(name)

Showwarning(“We’re sorry!”, “Cannot gain access to your account. Try


again!”)

Lastpass.delete(0, END)
Def change_masterpass(name_user: str) -> None:

“””changes the MasterPassword of the User”””

Empty_screen(root)

Root.title(“Change your MasterPassword”)

Root.iconbitmap(ICONS[7])

Label(image=IMAGES[27]).place(x=0, y=0)

Specs = dict(width=22, bd=2, font=(“ariel”, 19, “bold”), relief=FLAT,


cursor=CURSORS[6], bg=color)

Mp1, mp2, show, hide = Entry(**specs), Entry(**specs), *IMAGES[17:19]

Mp1.place(x=39, y=220.5)

Mp2.place(x=39, y=273)
Mp1.insert(0, “Choose a MasterPass”)

Mp2.insert(1, “Confirm MasterPass”)

Hs1 = Button(image=hide, bd=2, command=(lambda: hideshow(hs1,


mp1, show, hide)), cursor=CURSORS[0])

Hs2 = Button(image=hide, bd=2, command=(lambda: hideshow(hs2,


mp2, show, hide)), cursor=CURSORS[0])

Hs1.place(x=354.75, y=214.5)

Hs2.place(x=354.75, y=267)

Values = (name_user, mp1, mp2, update_file, create_vault)

Mp2.bind(“<Return>”, (lambda event: set_mpass(*values)))

Button(image=IMAGES[19], bd=0, command=(lambda:


set_mpass(*values)), cursor=CURSORS[0]).place(x=58.125, y=337.5)
Def update_file(uname: str, newpass: str) -> None:

“””updates the Binary Files pertaining to User’s MasterPasswords”””

Global ekey1, dkey1, ekey2, dkey2

With open(FILES[0], “rb”) as file1, open(FILES[1], “rb”) as file2,


open(“temp.dat”, “wb”) as file3:

For I in range(1, 16*USER_COUNT + 1):

Pwd_dict = pickle.load(file1)

If not (I % 16):

Ekey1, dkey1, ekey2, dkey2 = pickle.load(file2)

Kname = tuple(pwd_dict.keys())[0]

If uname == decrypt(kname):

Pwd_dict[kname].append(encrypt(newpass))
Pickle.dump(pwd_dict, file3)

Os.remove(FILES[0])

Os.rename(“temp.dat”, FILES[0])

Showinfo(“Successful”, “Your MasterPassword has been Updated


successfully”)

Def delete_file(user: str) -> None:

“””deletes the User account from Binary Files”””

Global ekey1, dkey1, ekey2, dkey2

With open(FILES[0], “rb”) as file1, open(FILES[1], “rb”) as file2, \


Open(“temp_pass.dat”, “wb”) as file3, open(“temp_keys.dat”, “wb”) as
file4:

Del_recs = range(0)

For I in range(1, 16*USER_COUNT + 1):

Pwd_dict = pickle.load(file1)

If not (I % 16):

Ekey1, dkey1, ekey2, dkey2 = pickle.load(file2)

Kname = tuple(pwd_dict.keys())[0]

If user == decrypt(kname):

Del_recs = range(I, i+16)

Else:
Pickle.dump((ekey1, dkey1, ekey2, dkey2), file4)

If I not in del_recs:

Pickle.dump(pwd_dict, file3)

For temp, name in (“temp_pass.dat”, FILES[0]), (“temp_keys.dat”,


FILES[1]):

Os.remove(name)

Os.rename(temp, name)

Def delete_user(name_user: str) -> None:

“””deletes the User data from the database and decrements the number
of Users”””
Global USER_COUNT

Try:

For table in “passwords”, “contacts”, “events”:

Cursor.execute(f”delete from {table} where user = {name_user !r}”)

Except:

Return showerror(“Error”, “Sorry, some error occurred. We were unable


to remove your account”)

Else:

Mydb.commit()

Msg = “All data has been removed from your Vault. “

If askyesno(“Vault Emptied”, f”{msg}Do you still wish to delete your


account?”):

Delete_file(name_user)
USER_COUNT -= 1

Return showinfo(“Successful”, “User Account deleted successfully.”)

Showinfo(“Successful”, “Your Vault has been emptied sucessfully.”)

Def create_vault(user: str) -> None:

“””starts the vault-window for a specific User”””

Global UserVault

Try:

UserVault.destroy()

Except NameError:
Pass

Finally:

UserVault: Tk = Toplevel(root)

UserVault.resizable(False, False)

Vault_window(user)

Main_window()

Def vault_window(name: str) -> None:

“””places the widgets on the vault-window”””

UserVault.geometry(“772x544”)

UserVault.title(f”””{f”{name}’” if name.endswith(“s”) else f”{name}’s”}


Secure Vault”””)
UserVault.iconbitmap(ICONS[8])

Label(UserVault, image=IMAGES[28]).place(x=0, y=0)

Def view_all(b: str|None = “\n\t\N{BULLET} “) -> None:

“””displays what type of information can be viewed from the


Application”””

Messages = [

“You can view all the personal information in your Vault in an orderly
fashion:”,

“{0}Tabular Format {0}Graphical Format \n”,

“Available Tables: {0}Passwords {0}Contacts {0}Events \n”,

“Available Graphs: {4}{0} {4}{1} {4}{2} {4}{3} \


n”.format(*graph_names, b),

“You can edit (Update/Delete) your saved data by double-clicking or


right-clicking it.”
]

Showinfo(“View Data”, “ “.join(messages).format(b), parent=UserVault)

Def view_data(tablename: str) -> None:

“””fetches all data of the User in the given Table (‘passwords’,


‘contacts’, or ‘events’)”””

Display_records(

Parent=UserVault, tablename=tablename,

Title=f”””{f”{name}’” if name.endswith(“s”) else f”{name}’s”}


{tablename.capitalize()}”””,

Query=f”select * from {tablename} where user = {name !r}”,

No_records=(“Empty Vault”, f”There are no {tablename.capitalize()}


in your Vault!”),

)
Def graph(variable: str) -> None:

“””selects and shows the Graph chosen by the User”””

Def graph1() -> None:

“””displays VERTICAL BAR GRAPH: number of passwords per


app/url”””

X_axis, y_axis = [row[0] if row[0] else “Unspecified” for row in data],


[row[1] for row in data]

Plt.bar(x_axis, y_axis, color=”b”, width=0.35)

Def graph2() -> None:

“””displays HORIZONTAL BAR GRAPH: number of contacts per city”””


X_axis, y_axis = [row[0] for row in data], [row[1] for row in data]

Plt.barh(x_axis, y_axis, color=”b”, height=0.35)

Def graph3() -> None:

“””displays PIE CHART: number of events per their status of


completion”””

Labels, slices, colors = [“INCOMPLETE”, “COMPLETED”], [row[1] for


row in data], [“#d11718”, “#25c5d4”]

Plt.pie(slices, labels=labels, colors=colors, autopct=”%1.1f%%”,


explode=[0, 0.035], wedgeprops={“edgecolor”: “black”})

Def graph4() -> None:

“””displays LINE PLOT: number of events per date”””


X_axis, y_axis = [row[0].strftime(“%a, %b %d, ‘%y”) for row in data],
[row[1] for row in data]

Plt.plot(x_axis, y_axis, marker=”o”, color=”b”, linewidth=1.35,


linestyle=”—“)

Plt.close(“all”)

Graph_number = {graph_names[i]: I for I in range(4)}[variable]

Try:

Cursor.execute(

0: “select app_url, count(*) from passwords where user = {}


group by app_url”,

1: “select city, count(*) from contacts where user = {} group by


city”,

2: “select completed, count(*) from events where user = {}


group by completed”,
3: “select evtdate, count(*) from events where user = {} group
by evtdate”,

}[graph_number].format(repr(name))

Except:

Return showerror(“Error”, “Sorry, we were unable to grab the


information necessary for your Graph”, parent=UserVault)

Else:

Data, count = sorted(cursor.fetchall(), key=(lambda row: row[0])),


cursor.rowcount

If count in range(2):

Return showinfo(“Not Enough Data”, “You haven’t saved enough


Data in your Vault to produce this Graph”, parent=UserVault)

Window_heading, graph_heading = {
0: (“Graph: Number of Passwords per App/URL”, “Frequency of
Passwords by App/URL”),

1: (“Graph: Number of Contacts per City”, “Frequency of Contacts by


City”),

2: (“Graph: Number of Events per status of completion”, “Frequency


of Events by their status of completion”),

3: (“Graph: Number of Event per Date”, “Frequency of Events by


Date”),

}[graph_number]

Labels = {

0: (“Applications/URLs”, “Number of Passwords”), 1: (“Number of


Contacts”, “Name of Cities”),

3: (“Dates”, “Number of Events”),

}.get(graph_number, (“”, “”))

Plt.style.use(“seaborn-dark” if graph_number in range(2) else


“grayscale”)
Plt.figure(window_heading)

Plt.title(graph_heading)

{i: (graph1, graph2, graph3, graph4)[i] for I in range(4)}


[graph_number]()

Plt.get_current_fig_manager().window.wm_iconbitmap(ICONS[17])

Plt.xlabel(labels[0])

Plt.ylabel(labels[1])

Plt.grid(True)

Plt.show()

Imgs, buttons, n = IMAGES[34:40], [], “Number of”


Coms = [view_all, (lambda: view_data(“passwords”)), (lambda:
view_data(“contacts”)), (lambda: view_data(“events”))]

For I in range(4):

Buttons.append(Button(UserVault, bd=0, image=IMAGES[i+29],


command=coms[i], cursor=CURSORS[4] if not I else CURSORS[0]))

Buttons[i].place(x=397.5, y=75*I + 131.25)

Op1 = Button(UserVault, image=imgs[0], bd=3, cursor=CURSORS[0],


command=(lambda: vault_ops(name, op1, imgs[0], imgs[1])))

Op2 = Button(UserVault, image=imgs[2], bd=3, cursor=CURSORS[0],


command=(lambda: pwd_generator(name, op2, imgs[2], imgs[3])))

Btn = Button(UserVault, image=imgs[4], bd=0, cursor=CURSORS[0],


command=(lambda: add_info(name, buttons, btn, imgs[4], imgs[5])))

Graph_names = [

F”{n} Passwords per App/URL”, f”{n} Contacts per City”, f”{n} Events
Completed/Incomplete”, f”{n} Events per Date”
]

Menu = ttk.OptionMenu(UserVault, StringVar(), “”, *graph_names,


command=graph)

Logout = (lambda: askyesno(“Log Out?”, “Log Out of Secure Vault? You will
have to Log In again!”, parent=UserVault))

Menu.config(image=IMAGES[33], cursor=CURSORS[0])

Menu.place(x=0, y=474.25)

Op1.place(x=0, y=0)

Op2.place(x=699.375, y=0)

Btn.place(x=676.875, y=448.125)

UserVault.protocol(“WM_DELETE_WINDOW”, (lambda: (plt.close(“all”),


UserVault.destroy()) if logout() else None))
Def vault_ops(

Name: str, button: Button, img1: ImageTk, img2: ImageTk

) -> None:

“””displays additional options in the Vault”””

Empty_screen(UserVault, don’t_destroy=button)

If str(button.cget(“image”)) == str(img1):

Label(UserVault, image=IMAGES[40]).place(x=0, y=0)

UserVault.iconbitmap(ICONS[9])

Button = Button(UserVault, image=img2, bd=3, cursor=CURSORS[0],


command=(lambda: vault_ops(name, button, img1, img2)))
Button.place(x=0, y=0)

Def user_logout() -> None:

“””prompts a User to log out of the Secure Vault”””

If askokcancel(“Log Out?”, “Log Out of Secure Vault? You will have to


Log In again!”, parent=UserVault):

UserVault.destroy()

Def user_change() -> None:

“””prompts a User to change their MasterPassword”””

If askokcancel(“Change MasterPassword”, “You will be temporarily


Logged Out. Still continue?”, parent=UserVault):

UserVault.destroy()
Change_masterpass(name)

Def user_delete() -> None:

“””prompts a User to delete their account”””

If askokcancel(“Confirm?”, “Are you sure you want to delete your


account? All data will be lost.”, parent=UserVault):

UserVault.destroy()

Delete_user(name)

Coms = [user_change, user_logout, (lambda: exit_app(UserVault))]

For I in range(3):
Button(UserVault, image=IMAGES[i+41], bd=0, command=coms[i],
cursor=CURSORS[0]).place(x=43, y=75*I + 176.25)

Button(UserVault, image=IMAGES[44], bd=3, command=user_delete,


cursor=CURSORS[0]).place(x=699.375, y=0)

Else:

Vault_window(name)

Def pwd_generator(

Name: str, button: Button, img1: ImageTk, img2: ImageTk

) -> None:

“””displays the password generator”””

Empty_screen(UserVault, don’t_destroy=button)
If str(button.cget(“image”)) == str(img1):

UserVault.title(“Secure Password Generator”)

UserVault.iconbitmap(ICONS[10])

Label(UserVault, image=IMAGES[45]).place(x=0, y=0)

Button = Button(UserVault, image=img2, bd=3, cursor=CURSORS[0],


command=(lambda: pwd_generator(name, button, img1, img2)))

Button.place(x=699.375, y=0)

Def security() -> None:

“””displays the password-security information”””

Messages = [
“Our Passwords are generared using Top – Class encryption
techniques.”,

“These are purely random & secured using intense character


mapping & combination.”,

“Our Generators ensure that a unique password is returned each


time.”,

“For queries or safety issues, contact developers.”

Showinfo(“Security Policy & Information”, “ “.join(messages),


parent=UserVault)

Def copy_clipboard() -> None:

“””copies the current password to the system’s clipboard”””

If not box.get():

Return showinfo(“No Password Found”, “Please generate a


Password first”, parent=UserVault)
Else:

Root.clipboard_clear()

Root.clipboard_append(box.get())

Showinfo(“Password Copied”, “The current Password has been


copied to the ClipBoard”, parent=UserVault)

Button(UserVault, image=IMAGES[46], bd=3, command=security,


cursor=CURSORS[4]).place(x=0, y=0)

Button(UserVault, image=IMAGES[47], bd=2, cursor=CURSORS[0],


command=copy_clipboard).place(x=708.75, y=102.75)

Box = Entry(UserVault, width=49, bd=2, font=(“ariel”, 19, “bold”),


relief=FLAT, cursor=CURSORS[6], state=”readonly”)

Box.place(x=15, y=108.75)

Specs = dict(from_=8, to=128, length=375, width=11.25, bg=”white”,


sliderrelief=FLAT, orient=HORIZONTAL)
Slider = Scale(UserVault, cursor=CURSORS[0], **specs)

Slider.place(x=375, y=161.25)

Variables, checks = [IntVar() for _ in range(4)], []

For I in range(4):

Checks.append(Checkbutton(UserVault, bd=17, bg=”white”,


variable=variables[i], cursor=CURSORS[0]))

Checks[i].place(x=375, y=(235 + (I * 57.5)))

Variables[0].set(1)

Btn = Button(UserVault, image=IMAGES[48], cursor=CURSORS[0],


bd=0, command=(lambda: gen_pass(slider, variables, box)))

Btn.place(x=337.5, y=468.75)

Else:
Vault_window(name)

Def gen_pass(

Scale: Scale, variables: list[int, 4], entrybox: Entry

) -> None:

“””accesses the User’s choices to generate a password”””

P_len, values = scale.get(), tuple(var.get() for var in variables)

Password, st = generate(p_len, values), entrybox.cget(“state”)

If password != “ok”:

Entrybox.config(state=NORMAL)

Entrybox.delete(0, END)
Entrybox.insert(0, password)

Entrybox.config(state=st)

Def add_info(

Name: str, buttons: list[Button, 4], button: Button, add: ImageTk, cross:
ImageTk

) -> None:

“””chooses which information the User wants to save”””

If str(button.cget(“image”)) == str(add):

UserVault.iconbitmap(ICONS[11])

Def add_data() -> None:


“””displays what type of information can be stored in the
application”””

Messages = [

“The Secure Vault is the storage house of your: {0}Passwords


{0}Contacts {0}Events \n”,

“Your personal data is stored in the database in encrypted formats


– not even we can access them!”

Showinfo(“Store Data”, “”.join(messages).format(“\n\t\N{BULLET} “),


parent=UserVault)

Coms = [add_data, (lambda: add_passwords(name)), (lambda:


add_contacts(name)), (lambda: add_events(name))]

For I in range(4):

Buttons[i].config(image=IMAGES[i+49], command=coms[i])

Button.config(image=cross)
Else:

Vault_window(name)

Def confirm(

Save_data: Callable[[str, list[Entry], str], None], user: str, entryboxes:


list[Entry],

Msg: bool|None = True, query: str|None = None

) -> bool:

“””confirms if the User wants to save the entered data”””

If msg:
If askokcancel(“Confirm?”, “Do you want to save the data?”,
parent=UserVault):

Save_data(user, entryboxes, query)

Else:

Return False

Else:

Save_data(user, entryboxes, query)

Return True

Def add_passwords(user: str) -> None:

“””creates the add-passwords window”””

Empty_screen(UserVault)
UserVault.title(“Add New Passwords”)

UserVault.iconbitmap(ICONS[12])

Label(UserVault, image=IMAGES[53]).place(x=0, y=0)

Specs = dict(master=UserVault, width=21, bd=2, font=(“ariel”, 19,


“bold”), relief=FLAT, cursor=CURSORS[6], bg=color)

Entries = [Entry(**specs) for _ in range(3)] + [Text(height=3.4, **specs)]

For I in range(4):

Entries[i].place(x=180, y=74.25*I + 112.5)

Entries[2].insert(0, “(Optional)”)

Entries[3].insert(0.0, “(Optional)”)

Hs = Button(UserVault, image=hide, bd=2, cursor=CURSORS[0],


command=(lambda: hideshow(hs, entries[1], *IMAGES[17:19])))
Hs.place(x=495, y=180.75)

Specs = dict(from_=8, to=128, length=202, width=10, bg=”white”,


sliderrelief=FLAT, orient=HORIZONTAL)

Slider = Scale(UserVault, cursor=CURSORS[0], **specs)

Slider.place(x=551.25, y=186.75)

Variables, checks = [IntVar() for _ in range(4)], []

For I in range(4):

Checks.append(Checkbutton(UserVault, bd=10, bg=”white”,


variable=variables[i], cursor=CURSORS[0]))

Checks[i].place(x=498.85, y=69.85*I + 250.5)

Variables[0].set(1)
Btn1 = Button(UserVault, image=IMAGES[54], bd=0, cursor=CURSORS[0],
command=(lambda: gen_pass(slider, variables, entries[1])))

Btn2 = Button(UserVault, image=IMAGES[55], bd=0, cursor=CURSORS[0],


command=(lambda: confirm(save_pass, user, entries)))

Btn1.place(x=495, y=105)

Btn2.place(x=15, y=463.125)

Back_button(UserVault, (lambda: vault_window(user)))

Def save_pass(name_user: str, values: list[Entry], query: str|None = None) ->


None:

“””adds a new/updates an existing record in Table ‘passwords’ in


database”””

V0, v1, v2, v3 = *[v.get().strip() for v in values[:3]], values[3].get(0.0,


END).strip()
V1 = values[1].get()

V2 = “” if v2.casefold() == “(optional)” else v2.upper()

V3 = “” if v3.casefold() == “(optional)” else v3.upper()

If valid_username(v0):

Return values[0].delete(0, END)

If valid_password(v1):

Return values[1].delete(0, END)

If len(v2) not in range(101):

Showerror(“Error”, “URL must be less than 100 characters”,


parent=UserVault)
Return values[2].delete(0, END)

If len(v3) not in range(201):

Showerror(“Error”, “Notes must be less than 200 characters”,


parent=UserVault)

Return values[3].delete(0.0, END)

Check_name(name_user) # updates encryption keys

Sv_up = “save” if query is None else “update”

Try:

Cursor.execute(

F”insert into passwords values {(name_user.upper(), v0.upper(),


encrypt(v1), v2, v3)}”
If query is None else query.format(repr(v0.upper()), repr(encrypt(v1)),
repr(v2), repr(v3))

Except con.errors.IntegrityError:

Return showerror(“Error”, f”Data with Username {v0 !r} already exists”,


parent=UserVault)

Except:

Return showerror(“Error”, f”Sorry, we were unable to {sv_up} your


Data!”, parent=UserVault)

Else:

Mydb.commit()

Showinfo(“Successful”, f”Your Data has been {sv_up + ‘d’}!”,


parent=UserVault)

Finally:

{v.delete(0, END) for v in values[:-1]}

Values[3].delete(0.0, END)
Values[2].insert(0, “(Optional)”)

Values[3].insert(0.0, “(Optional)”)

Def add_contacts(user: str) -> None:

“””creates the add-contacts window”””

Empty_screen(UserVault)

UserVault.title(“Add New Contacts”)

UserVault.iconbitmap(ICONS[13])

Label(UserVault, image=IMAGES[56]).place(x=0, y=0)

Specs = dict(width=31, bd=2, font=(“ariel”, 19, “bold”), relief=FLAT,


cursor=CURSORS[6], bg=color)
Entries = [Entry(UserVault, **specs) for _ in range(5)]

For I in range(5):

Entries[i].place(x=315, y=67.5*I + 120.5)

Entries[2].insert(0, “(Optional)”)

Entries[3].insert(0, “(Optional)”)

Entries[4].insert(0, “(Optional – Default NEW DELHI)”)

Button = Button(UserVault, image=IMAGES[55], bd=0,


cursor=CURSORS[0], command=(lambda: confirm(save_cont, user, entries)))

Button.place(x=15, y=463.125)

Back_button(UserVault, (lambda: vault_window(user)))


Def save_cont(name_user: str, values: list[Entry], query: str|None = None) ->
None:

“””adds a new/updates an existing record in Table ‘contacts’ in


database”””

V0, v1, v2, v3, v4 = [v.get().strip() for v in values]

V2 = “” if v2.casefold() == “(optional)” else v2

V3 = “01-01-0001” if v3.casefold() in {“(optional)”, “”} else v3

V4 = “NEW DELHI” if v4.casefold() in {“(optional – default new delhi)”, “”}


else v4.upper()

If len(v0) not in range(3, 51):

If not v0:

Return showwarning(“Required Field”, “Contact Name cannot be


empty”, parent=UserVault)
Showerror(“Error”, “Contact Name must be between 3 – 50 characters”,
parent=UserVault)

Return values[0].delete(0, END)

If not v1:

Return showwarning(“Required Field”, “Contact Number cannot be


empty”, parent=UserVault)

If valid_contact(v1):

Return values[1].delete(0, END)

If v2:

If valid_contact(v2):

Return values[2].delete(0, END)

Try:

V3 = dt.strptime(v3, “%d-%m-%Y”).date()
Except ValueError:

Try:

V3 = dt.strptime(v3, “%d/%m/%Y”).date()

Except ValueError:

Showerror(“Error”, “Invalid Date format used”, parent=UserVault)

Return values[3].delete(0, END)

Sv_up = “save” if query is None else “update”

Try:

Cursor.execute(

F”insert into contacts values {(name_user.upper(), v0.upper(), v1, v2,


str(v3), v4)}”
If query is None else query.format(repr(v0.upper()), repr(v1), repr(v2),
repr(str(v3)), repr(v4))

Except con.errors.IntegrityError:

Return showerror(“Error”, f”Data with Contact Number {v1 !r} already


exists”, parent=UserVault)

Except:

Return showerror(“Error”, f”Sorry, we were unable to {sv_up} your


Data!”, parent=UserVault)

Else:

Mydb.commit()

Showinfo(“Successful”, f”Your Data has been {sv_up + ‘d’}!”,


parent=UserVault)

Finally:

{v.delete(0, END) for v in values}

Values[2].insert(0, “(Optional)”)
Values[3].insert(0, “(Optional)”)

Values[4].insert(0, “(Optional – Default NEW DELHI)”)

Def add_events(user: str) -> None:

“””creates the add-events window”””

Empty_screen(UserVault)

UserVault.title(“Add New Events”)

UserVault.iconbitmap(ICONS[14])

Label(UserVault, image=IMAGES[57]).place(x=0, y=0)

Specs = dict(width=31, bd=2, font=(“ariel”, 19, “bold”), relief=FLAT,


cursor=CURSORS[6], bg=color)
Entries = [Entry(UserVault, **specs) for _ in range(4)]

For I in range(4):

Entries[i].place(x=315, y=69*I + 112.5)

Entries[2].insert(0, “(Optional – Default 00:00:00)”)

Entries[3].insert(0, “YES/NO (Default NO)”)

Button = Button(UserVault, image=IMAGES[55], bd=0,


cursor=CURSORS[0], command=(lambda: confirm(save_evnt, user, entries)))

Button.place(x=15, y=463.125)

Back_button(UserVault, (lambda: vault_window(user)))

Def save_evnt(name_user: str, values: list[Entry], query: str|None = None) ->


None:
“””adds a new/updates an existing record in Table ‘events’ in database”””

V0, v1, v2, v3 = [v.get().strip() for v in values]

V2 = “00:00:00” if v2.casefold() in {“(optional – default 00:00:00)”, “”}


else v2

V3 = “NO” if v3.casefold() in {“yes / no (default no)”, “”} else v3.upper()

If len(v0) not in range(3, 31):

If not v0:

Return showwarning(“Required Field”, “Title of Event cannot be


empty”, parent=UserVault)

Showerror(“Error”, “Title of Event must be between 3 – 31 characters”,


parent=UserVault)

Return values[0].delete(0, END)


If not v1:

Showwarning(“Required Field”, “Date of Event cannot be empty”,


parent=UserVault)

Return values[1].delete(0, END)

Try:

V1 = dt.strptime(v1, “%d-%m-%Y”).date()

Except ValueError:

Try:

V1 = dt.strptime(v1, “%d/%m/%Y”).date()

Except ValueError:

Showerror(“Error”, “Invalid Date format used”, parent=UserVault)

Return values[1].delete(0, END)


If v2.count(“:”) == 1 and “ “ not in v2:

V2 = f”{v2[:-2]} {v2[-2:]}”

Try:

V2 = dt.strptime(v2, “%I:%M %p”).time()

Except ValueError:

Try:

V2 = dt.strptime(v2, “%H:%M:%S”).time()

Except ValueError:

Showerror(“Error”, “Invalid Time format used”, parent=UserVault)

Return values[2].delete(0, END)

If v3 not in {“YES”, “NO”}:


Return showerror(“Error”, “Status of completion must be YES/NO”,
parent=UserVault)

Sv_up = “save” if query is None else “update”

Try:

Cursor.execute(

F”insert into events values {(name_user.upper(), v0.upper(), str(v1),


str(v2), v3)}”

If query is None else query.format(repr(v0.upper()), repr(str(v1)),


repr(str(v2)), repr(v3))

Except con.errors.IntegrityError:

Return showerror(“Error”, f”Data with Event Title {v0 !r} already


exists”, parent=UserVault)

Except:
Return showerror(“Error”, f”Sorry, we were unable to {sv_up} your
Data!”, parent=UserVault)

Else:

Mydb.commit()

Showinfo(“Successful”, f”Your Data has been {sv_up + ‘d’}!”,


parent=UserVault)

Finally:

{v.delete(0, END) for v in values}

Values[2].insert(0, “(Optional – Default 00:00:00)”)

Values[3].insert(0, “YES / NO (Default NO)”)

Def display_records(

Parent: Tk, tablename: str, title: str, query: str, no_records: tuple[str]

) -> None:
“””fetches and displays records from the database and helps User to
update their records”””

Global window, trv

Try:

Window.destroy()

Except NameError:

Pass

Finally:

Icon = {“passwords”: ICONS[12], “contacts”: ICONS[13], “events”:


ICONS[14]}

Try:

Cursor.execute(query)
Except:

Return showerror(“Error”, “Sorry, some unexpected error occurred”,


parent=parent)

Else:

Data, count = cursor.fetchall(), cursor.rowcount

Height = 20*count + 70

If not count:

Return showinfo(*no_records, parent=parent)

If count != 1:

If parent is root:

Data.sort(key=(lambda row: (row[0], row[1])))

Else:

Data.sort(key=(lambda row: (row[1], row[2])))


Window, ns = Toplevel(parent), “Not Specified”

Window.resizable(False, False)

Window.title(title)

Window.iconbitmap(ICONS[15] if parent is root else icon[tablename])

If parent is not root:

Name = data[0][0].title()

If tablename == “passwords”:

Headings = [“S.No.”, “Username”, “Password”, “Application/URL”,


“Notes”]

Widths = [50, 150, 150, 150, 200]

If “name” in locals():

Check_name(name) # updates decryption keys


Elif tablename == “contacts”:

Headings = [“S.No.”, “Name of Contact”, “Contact Number”, “Alternate


Number”, “Date of Birth”, “Place”]

Widths = [50, 150, 150, 150, 200, 150]

Else:

Headings = [“S.No.”, “Title of Event”, “Date of Event”, “Time of Event”,


“Status of Completion”]

Widths = [50, 150, 200, 150, 170]

If parent is root:

Headings[0], widths[0] = “Name of User”, 150

Window.geometry(f”{sum(widths)+45}x{height}”)

Table, columns = Frame(window, cursor=CURSORS[4] if parent is root else


CURSORS[7]), range(len(headings))

Table.pack(side=LEFT, padx=20)
Trv = ttk.Treeview(table, columns=tuple(columns), show=”headings”,
height=str(count), selectmode=”browse”)

For I in columns:

Trv.heading(I, text=headings[i])

Trv.column(I, minwidth=widths[i], width=widths[i])

For I in range(len(data)):

Row = list(data[i])

If tablename == “passwords”:

App_url = row[3].endswith((“.COM”, “.NET”, “.IN”))

If parent is root:
Check_name(row[0].title()) # updates decryption key for every
User separately

Row[1], row[2] = row[1].lower(), decrypt(row[2])

Row[3] = row[3].lower() if app_url else row[3].capitalize() if row[3]


else ns

Row[4] = row[4].capitalize() if row[4] else ns

Elif tablename == “contacts”:

Row[1], row[2], row[5] = row[1].title(), row[2].zfill(10), row[5].title()

Row[3] = row[3].zfill(10) if row[3] else ns

Row[4] = row[4].strftime(“%A, %B %d, ‘%Y”) if row[4] !=


dt.min.date() else ns

Else:

Row[1] = row[1].title()

Row[2] = row[2].strftime(“%A, %B %d, ‘%Y”)


Row[3] = (dt.min + row[3]).time().strftime(“%I:%M:%S %p”)

Row[0] = (I + 1) if parent is not root else row[0].title()

Trv.insert(“”, “end”, values=tuple(row))

Trv.pack()

If parent is root:

Title, message = “Unable to change Data”, “Admins are not allowed to


edit (delete/update) the User’s Data”

For event in “<Button-3>”, “<Double-Button-1>”:

Trv.bind(event, (lambda event: showwarning(title, message,


parent=window)))

Return style.map(“Treeview”, background=[(“selected”, “green”)])


Menu = None

Def edit_row(event: Event) -> None:

“””displays a menu to delete/update User’s records”””

Nonlocal menu

If event.y not in range(25, 20*count + 26):

Return

If event.num == 3:

Trv.selection_set(trv.identify_row(event.y))

Menu = Menu(window, tearoff=False)


Menu.add_command(label=”Delete Row”, command=(lambda:
get_row(tablename, True, name)))

Menu.add_command(label=”Update Row”, command=(lambda:


get_row(tablename, False, name)))

Menu.tk_popup(x=event.x_root, y=event.y_root)

Trv.bind(“<Button-3>”, edit_row)

Trv.bind(“<Double-Button-1>”, edit_row)

Def get_row(tablename: str, selection: bool, name_user: str) -> None:

“””displays update/delete data prompts in UserVault”””

Items = trv.item(trv.focus())[“values”]

Style.map(“Treeview”, background=[(“selected”, “red” if selection else


“#f59304”)])
If selection:

If askyesno(“Confirm”, “Do you want to delete the selected data?”,


parent=window):

Delete_row(items, tablename, name_user)

Window.destroy()

Elif askyesno(“Confirm”, “Do you want to update this data?”,


parent=window):

Update_row(items, tablename, name_user)

Window.destroy()

Style.map(“Treeview”, background=[(“selected”, “#0078d7”)])


Def delete_row(row: tuple[str], table: str, name_user: str) -> None:

“””deletes a row from a Table in the database”””

Pri_key = {“passwords”: “username”, “contacts”: “contact_no”, “events”:


“evttitle”}[table]

Value = repr(row[1]) if table != “contacts” else row[2]

Try:

Cursor.execute(f”delete from {table} where {pri_key} = {value} and


user = {name_user !r}”)

Except:

Return showerror(“Error”, “Sorry, we were unable to delete your Data!”,


parent=window)

Else:

Mydb.commit()

Showinfo(“Successful”, “Your Data has been deleted”, parent=window)


Def update_row(row: tuple[str], table: str, name_user: str) -> None:

“””updates a row from a Table in the database”””

UserVault.title(f”Update your {table.capitalize()}”)

Specs = dict(master=UserVault, width=37, bd=2, font=(“ariel”, 19,


“bold”), relief=FLAT, cursor=CURSORS[6], bg=color)

U1 = “username = {}, password = {}, app_url = {}, notes = {} where


username =”

U2 = “fullname = {}, contact_no = {}, alternate_no = {}, birthday = {},


city = {} where contact_no =”

U3 = “evttitle = {}, evtdate = {}, evttime = {}, completed = {} where


evttitle =”

Attrs = {
“passwords”: (ICONS[12], IMAGES[58], 3, True, 75, save_pass, f”update
passwords set {u1} {row[1] !r}”),

“contacts”: (ICONS[13], IMAGES[59], 5, False, 71.25, save_cont,


f”update contacts set {u2} {str(row[2]) !r}”),

“events”: (ICONS[14], IMAGES[60], 4, False, 75, save_evnt, f”update


events set {u3} {row[1] !r}”),

}.get(table)

UserVault.iconbitmap(attrs[0])

Label(UserVault, image=attrs[1]).place(x=0, y=0)

Entries = [Entry(**specs) for _ in range(attrs[2])] + ([Text(height=3.4,


**specs)] if attrs[3] else [])

Extra = 15 if table == “events” else 0

If table != “passwords”:

Ind = 2 if table == “events” else -2


If row[ind] != “Not Specified”:

Row[ind] = dt.strptime(row[ind], “%A, %B %d, ‘%Y”).strftime(“%d-


%m-%Y”)

If table == “events”:

Row[3] = dt.strptime(row[3], “%I:%M:%S %p”).strftime(“%H:%M:


%S”)

For I in range(len(entries)):

Entries[i].place(x=202.5, y=(112.5 + extra + (I * (attrs[4] + extra//2))))

Row[i+1] = “” if row[i+1] == “Not Specified” else row[i+1]

Try:

Entries[i].insert(0, row[i+1])

Except TclError:

Entries[i].insert(0.0, row[i+1])
Def discard() -> None:

“””asks if the User to cancel the updates”””

If askokcancel(“Discard Changes?”, “Do you wish to Cancel all


changes?”, parent=UserVault):

Return vault_window(name_user)

Def update() -> None:

“””asks if the User to save the updates”””

If askokcancel(“Save Changes?”, “Do you wish to save your Updates?”,


parent=UserVault):

If confirm(save_data=attrs[5], user=name_user, msg=False,


entryboxes=entries, query=attrs[6]):

Vault_window(name_user)
Button(UserVault, image=IMAGES[61], cursor=CURSORS[0], bd=0,
command=update).place(x=8.25, y=469.5)

Button(UserVault, image=IMAGES[62], cursor=CURSORS[0], bd=0,


command=discard).place(x=390, y=469.5)

Def admin_mode() -> None:

“””requests access to the admin mode-window”””

Global admin

If not askyesno(“Log Users Out?”, “Running Admin Mode will log all users
out. Still continue?”):

Return
Try:

Plt.close(“all”)

UserVault.destroy()

Except NameError:

Pass

Try:

Admin.destroy()

Except NameError:

Pass

Finally:

Admin = Toplevel(root)

Admin.geometry(“534x96”)

Admin.resizable(False, False)
Admin.title(“Access Admin Mode”)

Admin.iconbitmap(ICONS[16])

Def access() -> None:

“””grants or revokes access to Admin Status”””

Credentials = tuple(box.get().strip() for box in entries)

If not all(credentials):

Return showwarning(“Required Fields”, “Entries cannot be empty”,


parent=admin)

If credentials != CREDENTIALS:
Return showerror(“ACCESS DENIED”, “Cannot access Admin Mode”,
parent=admin)

Showinfo(“ACCESS GRANTED”, “Welcome to KeepMyPass’ Admin Mode”)

Admin.destroy()

Admin_window()

Text1, text2 = “Enter Name of the Admin:”, “Enter Password to Admin


Mode:”

Text = 49*” “ + “Run Admin Mode” + 49*” “

Label(admin, font=(“ariel”, 11, “bold”), text=text1.ljust(30)).place(x=5,


y=5)

Label(admin, font=(“ariel”, 11, “bold”), text=text2.ljust(30)).place(x=5,


y=32.5)

Specs = dict(width=35, bd=2, font=(“ariel”, 11, “bold”), relief=FLAT,


cursor=CURSORS[6], bg=”#e1e1e1”)
Entries = [Entry(admin, **specs) for _ in range(2)]

Entries[0].place(x=242.5, y=5)

Entries[1].place(x=242.5, y=32.5)

Entries[1].bind(“<Return>”, (lambda event: access()))

Button(admin, text=text, font=(“ariel”, 11, “bold”), bd=2,


cursor=CURSORS[0], command=access).place(x=3.5, y=60)

Def admin_window() -> None:

“””starts the admin mode-window for the Programmer”””

Empty_screen(root)

Root.title(“Search Mode”)
Root.iconbitmap(ICONS[15])

Label(image=IMAGES[63]).place(x=0, y=0)

Box = Entry(width=49, bd=2, font=(“ariel”, 19, “bold”), relief=FLAT,


cursor=CURSORS[6], bg=color)

Box.bind(“<Return>”, (lambda event: search(box, var)))

Box.place(x=15, y=106.5)

Def info() -> None:

“””displays information about admin mode”””

Messages = [

“An Admin can search through all User’s Vaults at once.”,

“Select the type of data you want to see and enter a search key.”,

“Any data that matches your search will be displayed.”,


“You can for ‘*’ to show all data of the selected type of all the current
Users.”,

“You may also see an informative Stack Plot showing Number of each
item stored per active User.”,

“An active User is one who has at least one record stored in our
database.”

Showinfo(“Search User Vaults”, “ “.join(messages))

Def exit_admin() -> None:

“””displays the exit admin mode-prompt”””

If askyesno(“EXIT?”, “Do you quit Admin Mode?”):

Try:

Window.destroy()
Style.map(“Treeview”, background=[(“selected”, “#0078d7”)])

Except NameError:

Pass

Finally:

Main_window()

Def update() -> None:

“””updates the entrybox to display a relevant message”””

Text = box.get().strip().casefold()

If not text or text in {f”search user {tb}” for tb in {“passwords”,


“contacts”, “events”}}:

Box.delete(0, END)

Box.insert(0, f”Search User {var.get()}”)


Var, items = StringVar(), [“Passwords”, “Contacts”, “Events”]

For I in range(3):

Button = Radiobutton(bg=”white”, bd=17, variable=var,


value=items[i], command=update, cursor=CURSORS[0])

Button.place(x=52.5, y=(225 + (I * 76)))

Var.set(“Passwords”)

Update()

Button(image=IMAGES[8] , bd=3, command=info,


cursor=CURSORS[4]).place(x=699.375, y=0)

Button(image=IMAGES[33], bd=3, cursor=CURSORS[0],


command=graph_admin).place(x=0, y=0)

Button(image=IMAGES[64], bd=2, cursor=CURSORS[0],


command=(lambda: search(box, var))).place(x=708.75, y=100)

Button(image=IMAGES[65], bd=0, cursor=CURSORS[0],


command=exit_admin).place(x=11.25, y=457.5)
Def search(entrybox: Entry, variable: StringVar) -> None:

“””fetches matching record(s) from the database and displays them”””

Skey, tablename = entrybox.get().strip().casefold(), variable.get().lower()

Enter_txt = {f”search user {table}” for table in {“passwords”, “contacts”,


“events”}}

If not skey or skey in enter_txt:

Return showwarning(“Required Field”, “Please enter a Search Key”)

M = f”’%{skey}%’”

If tablename == “passwords”:
Cond = f”username like {m} or app_url like {m} or notes like {m}”

Elif tablename == “contacts”:

Cond = f”contact_no like {m} or alternate_no like {m} or fullname like


{m} or birthday like {m} or city like {m}”

Else:

Cond = f”evttitle like {m} or evtdate like {m} or evttime like {m} or
completed like {m}”

Cond = True if skey == “*” else f”user like {m} or “ + cond

Message = (“No Matches”, “Data matching your search could not be


found”) if skey != “*” else \

(“Empty Database”, f”There are no {tablename.capitalize()} in the


Database!”)

Display_records(

Parent=root, tablename=tablename,
Title=f”Searched {tablename.capitalize()}”,

Query=f”select * from {tablename} where {cond}”,

No_records=message

Def graph_admin() -> None:

“””displays the STACK PLOT: number of each item per active User in admin
mode”””

Plt.close(“all”)

Data, users, count = [], [], 0

For table in “passwords”, “contacts”, “events”:


Try:

Cursor.execute(f”select user, count(*) from {table} group by user


order by user”)

Except:

Return showerror(“Error”, “Sorry, we were unable to grab the


information necessary for this Graph”)

Else:

Current_rows, count = cursor.fetchall(), (count + cursor.rowcount)

Data.append(current_rows)

Users.extend(current_rows)

Users, numbers, colors = sorted({row[0].title() for row in users}), [[], [],


[]], [“green”, “#d11718”, “#008fd5”]

For user in set(users):


For I in range(3):

For name, n in data[i]:

If user.casefold() == name.casefold():

Break

Else:

Data[i].append((user, 0))

Data[i].sort(key=(lambda row: row[0]))

For I in range(3):

For name, n in data[i]:

Numbers[i].append(n)

Users = [“\n”+users[i] if i%2 else users[i] for I in range(len(users))]


Plt.style.use(“grayscale”)

Plt.figure(“Graph – Number of Items by each User”,


figsize=(1.575*len(users), 5.375))

Plt.title(“Frequency of Passwords, Contacts & Events per active User”)

Plt.get_current_fig_manager().window.wm_iconbitmap(ICONS[17])

Plt.stackplot(users, *numbers, colors=colors, labels=[“Passwords”,


“Contacts”, “Events”])

Plt.xlabel(“Name of User(s)”)

Plt.ylabel(“Number of Item(s)”)

Plt.legend()

Plt.grid(True)

Plt.show()
If __name__ == “__main__”:

Connect()

You might also like