├── LICENSE ├── README.md ├── config.sql ├── csv_books.csv ├── images ├── add_books.png ├── home_screen.png └── return_books.png ├── main.py ├── requirements.txt ├── tele_creds.txt └── tele_server.py /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2022 Madhav Yadav 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Library-Management-System(GUI) 2 | A Client-Server Architecture Based Library Management System with a decent looking GUI! 3 | ***PS: It was my class 12th Computer Science Project!!*** 4 | 5 | 6 | # Installation 7 | 1. Generate a telegram-bot using BotFather(https://telegram.me/BotFather). 8 | 2. Run `pip install -r requirements.txt` 9 | 3. Run `source config.sql` in your mysql command-line client terminal to setup all tables automatically. 10 | 4. Enter your mysql credentials and telegram bot api-token in `tele_creds.txt` 11 | 5. Put `tele_creds.txt` and `tele_server.py` to your either your startup folder or on some server class machine to keep the file running. 12 | 6. Finally, Run `python3 main.py` 13 | 14 | # Default GUI credentials 15 | Username: `root` 16 | Password: `toor` 17 | 18 | # Some Screenshots:- 19 | ## Home screen - 20 | ![Home Screen](https://github.com/mymadhavyadav07/Library-Management-System/blob/main/images/home_screen.png) 21 | 22 | ## Add Books - 23 | ![Add Books](https://github.com/mymadhavyadav07/Library-Management-System/blob/main/images/add_books.png) 24 | 25 | ## Return Books - 26 | ![Return Books](https://github.com/mymadhavyadav07/Library-Management-System/blob/main/images/return_books.png) 27 | 28 | 29 | 30 | # Lastly... 31 | ### Star this repo please ⭐(stop ignoring this message) 32 | 33 | Made with lots of ❤ and dedication by **Madhav Yadav**!! 34 | 35 | ![Madhav Yadav](https://avatars.githubusercontent.com/u/66372332?v=4) 36 | -------------------------------------------------------------------------------- /config.sql: -------------------------------------------------------------------------------- 1 | CREATE DATABASE library; 2 | USE library; 3 | 4 | CREATE TABLE IF NOT EXISTS `LIB_USERS` ( 5 | USERNAME varchar(20) NOT NULL UNIQUE KEY, 6 | PASSWORD varchar(64) NOT NULL UNIQUE KEY 7 | ); 8 | 9 | INSERT INTO `LIB_USERS` VALUES('root','ce5ca673d13b36118d54a7cf13aeb0ca012383bf771e713421b4d1fd841f539a'); 10 | 11 | 12 | 13 | CREATE TABLE IF NOT EXISTS `BOOKS` ( 14 | BOOK_ID int NOT NULL, 15 | BOOK_NAME varchar(40) NOT NULL UNIQUE KEY, 16 | AUTHOR_NAME varchar(40) NOT NULL, 17 | BOOK_PRICE int, 18 | QTY int default 1 19 | ); 20 | 21 | CREATE TABLE IF NOT EXISTS `ISSUED_BOOKS` ( 22 | STUDENT_ID int NOT NULL, 23 | STUDENT_NAME varchar(40) NOT NULL, 24 | CLASS varchar(3) NOT NULL, 25 | SECTION varchar(1) NOT NULL, 26 | ROLL_NO int NOT NULL, 27 | BOOK_ID int NOT NULL, 28 | BOOK_PRICE int, 29 | ISSUE_DATE date NOT NULL, 30 | RETURN_DATE date, 31 | FINE int 32 | ); 33 | 34 | CREATE TABLE IF NOT EXISTS `TELEBOT` ( 35 | STUDENT_ID int NOT NULL 36 | ); 37 | -------------------------------------------------------------------------------- /csv_books.csv: -------------------------------------------------------------------------------- 1 | BOOK_ID,BOOK_NAME,AUTHOR_NAME,BOOK_PRICE,QTY 2 | -------------------------------------------------------------------------------- /images/add_books.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mymadhavyadav07/Library-Management-System/2c4a5940d7960f3f1d5f9c6a839525127af05154/images/add_books.png -------------------------------------------------------------------------------- /images/home_screen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mymadhavyadav07/Library-Management-System/2c4a5940d7960f3f1d5f9c6a839525127af05154/images/home_screen.png -------------------------------------------------------------------------------- /images/return_books.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mymadhavyadav07/Library-Management-System/2c4a5940d7960f3f1d5f9c6a839525127af05154/images/return_books.png -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | import tkinter as tk 2 | from time import sleep 3 | import mysql.connector as conn 4 | from tkinter import messagebox 5 | import os 6 | import csv 7 | import requests 8 | import pickle 9 | import base64 10 | from datetime import date 11 | import customtkinter as ck 12 | import hashlib 13 | from tkinter import filedialog as fd 14 | from os.path import exists 15 | 16 | 17 | class main: 18 | def __init__(self) -> None: 19 | ck.set_appearance_mode("dark") 20 | ck.set_default_color_theme("blue") 21 | 22 | self.mysql_password = '' 23 | self.mysql_username = '' 24 | self.db = '' 25 | self.cursor = '' 26 | 27 | self.root = ck.CTk() 28 | self.root.title("Library Management System (APS New Cantt, Prayagraj)") 29 | self.root.bind("", lambda key: self.listener(key,self.root)) 30 | self.root.withdraw() 31 | 32 | self.win_del = ck.CTk() 33 | self.win_del.title("Delete Books") 34 | self.win_del.bind("", lambda key: self.listener(key,self.win_del)) 35 | self.win_del.withdraw() 36 | 37 | self.win_add = ck.CTk() 38 | self.win_add.title("Add Books") 39 | self.win_add.bind("", lambda key: self.listener(key,self.win_add)) 40 | self.win_add.withdraw() 41 | 42 | self.win_return = ck.CTk() 43 | self.win_return.title("Return Books") 44 | self.win_return.bind("", lambda key: self.listener(key,self.win_return)) 45 | self.win_return.withdraw() 46 | 47 | self.issue = ck.CTk() 48 | self.issue.title("Issue Books") 49 | self.issue.bind("", lambda key: self.listener(key,self.issue)) 50 | self.issue.withdraw() 51 | 52 | self.reset = ck.CTk() 53 | self.reset.title("Reset Password") 54 | self.reset.bind("", lambda key: self.listener(key,self.reset)) 55 | self.reset.withdraw() 56 | 57 | self.fine = 50 58 | self.fields = ["BOOK_ID",'BOOK_NAME','AUTHOR_NAME','BOOK_PRICE','QTY'] 59 | 60 | 61 | # -----------------------------------------MAIN FUNCTIONS----------------------------------------- 62 | 63 | def listener(self, key, window): 64 | win_title = str(window.wm_title()) 65 | 66 | if key.keysym == "F11": 67 | if window.wm_attributes()[7] == 0: 68 | window.attributes("-fullscreen", True) 69 | else: 70 | window.attributes("-fullscreen", False) 71 | 72 | elif key.keysym == "F1": 73 | self.reset_fine_gui() 74 | 75 | elif key.keysym == "F2": 76 | self.reset_password_gui() 77 | 78 | elif key.keysym == "F3": 79 | if window.appearance_mode == 1: 80 | window.set_appearance_mode("light") 81 | else: 82 | window.set_appearance_mode("dark") 83 | 84 | elif key.keysym == "F5": 85 | self.add_books_csv() 86 | 87 | 88 | def on_closing(self): 89 | confirm = messagebox.askquestion("Confirm", "Do you really want to quit?") 90 | if confirm.lower() == "yes": 91 | exit() 92 | 93 | def win_issue_on_closing(self): 94 | self.issue.withdraw() 95 | 96 | def reset_on_closing(self): 97 | self.reset.withdraw() 98 | 99 | def reset_fine_on_closing(self): 100 | self.reset_fine.withdraw() 101 | 102 | def win_return_on_closing(self): 103 | self.win_return.withdraw() 104 | 105 | def win_del_on_closing(self): 106 | self.win_del.withdraw() 107 | 108 | def win_add_on_closing(self): 109 | self.win_add.withdraw() 110 | 111 | def abt_on_closing(self): 112 | self.about.withdraw() 113 | 114 | def login_on_closing(self): 115 | exit() 116 | 117 | def add_books(self, book_id, book_name, author, bookprice, qty): 118 | sql = 'INSERT INTO BOOKS(BOOK_ID, BOOK_NAME, AUTHOR_NAME, BOOK_PRICE, QTY) VALUES(%s, %s, %s, %s, %s)' 119 | val = (book_id.get(), book_name.get(), author.get(), bookprice.get(), qty.get()) 120 | 121 | try: 122 | self.cursor.execute(sql, val) 123 | self.db.commit() 124 | messagebox.showinfo("Success", "Book added successfully!") 125 | except: 126 | messagebox.showerror("Error", "Something Went Wrong!") 127 | 128 | def delete_book(self, del_book_id): 129 | try: 130 | self.cursor.execute('delete from books where BOOK_ID=' + del_book_id.get()) 131 | self.db.commit() 132 | messagebox.showinfo("Success", 'Book deleted successfully!') 133 | except: 134 | messagebox.showerror("Error", "Something Went Wrong!") 135 | 136 | def issue_books(self, std_name, class_, section, roll, issue_bid, issue_date): 137 | self.cursor.execute("SELECT QTY FROM BOOKS WHERE BOOK_ID="+issue_bid.get()) 138 | result = self.cursor.fetchall() 139 | if (len(result) != 0) and (result[0][-1] != 0): 140 | self.cursor.execute("select STUDENT_ID from ISSUED_BOOKS;") 141 | 142 | try: 143 | std_id = self.cursor.fetchall()[-1][0] 144 | except: 145 | std_id = 0 146 | 147 | try: 148 | self.cursor.execute("SELECT BOOK_PRICE FROM BOOKS WHERE BOOK_ID="+issue_bid.get()) 149 | bprice = self.cursor.fetchall()[0][0] 150 | except IndexError: 151 | messagebox.showerror("Error", "Book Not Available. Check the list of books again.") 152 | 153 | try: 154 | sql = 'INSERT INTO ISSUED_BOOKS(STUDENT_ID, STUDENT_NAME, CLASS, SECTION, ROLL_NO, BOOK_ID, BOOK_PRICE, ISSUE_DATE) VALUES(%s, %s, %s, %s, %s, %s, %s, %s)' 155 | val = ((std_id+1), std_name.get(), class_.get().upper(), section.get().upper(), int(roll.get()), int(issue_bid.get()), int(bprice), issue_date.get()) 156 | 157 | self.cursor.execute(sql, val) 158 | self.db.commit() 159 | 160 | self.cursor.execute("UPDATE BOOKS SET QTY=QTY-1 WHERE BOOK_ID="+issue_bid.get()) 161 | self.db.commit() 162 | 163 | messagebox.showinfo("Success", "Book Issued Successfully!") 164 | except: 165 | messagebox.showerror("Error", "Something went wrong.") 166 | 167 | else: 168 | messagebox.showinfo("Info", "Sorry, the book is not currently available!") 169 | 170 | def return_books(self, return_date, return_stdid): 171 | try: 172 | return_date_splitted = return_date.get().split('-') 173 | date1 = date(int(return_date_splitted[0]), int(return_date_splitted[1]), int(return_date_splitted[2])) 174 | 175 | self.cursor.execute("SELECT * FROM ISSUED_BOOKS") 176 | rows = self.cursor.fetchall() 177 | 178 | for i in rows: 179 | if int(i[0]) == int(return_stdid.get()): 180 | date2 = i[7] 181 | 182 | days = (date1 - date2).days 183 | if days > 7: 184 | self.cursor.execute("UPDATE ISSUED_BOOKS SET FINE=%s WHERE STUDENT_ID=%s", ((days-7)*self.fine, return_stdid.get())) 185 | self.db.commit() 186 | 187 | self.cursor.execute("SELECT BOOK_ID FROM ISSUED_BOOKS WHERE STUDENT_ID="+return_stdid.get()) 188 | return_bid = self.cursor.fetchall()[-1][0] 189 | 190 | self.cursor.execute("UPDATE ISSUED_BOOKS SET RETURN_DATE=%s WHERE STUDENT_ID=%s", (return_date.get(), return_stdid.get())) 191 | self.db.commit() 192 | 193 | self.cursor.execute("UPDATE BOOKS SET QTY=QTY+1 WHERE BOOK_ID="+str(return_bid)) 194 | self.db.commit() 195 | 196 | messagebox.showinfo("Info", "Success!") 197 | 198 | except Exception as e: 199 | messagebox.showerror("Error", f"Something Went Wrong!\n{e}") 200 | 201 | 202 | def view_books(self): 203 | self.cursor.execute("DESC BOOKS") 204 | fields = [] 205 | 206 | for i in self.cursor.fetchall(): 207 | fields.append(i[0]) 208 | 209 | self.cursor.execute("SELECT * FROM BOOKS") 210 | rows = self.cursor.fetchall() 211 | 212 | filepath = fd.asksaveasfilename(title="Save as") 213 | 214 | try: 215 | with open(filepath, 'w') as file: 216 | writer = csv.writer(file) 217 | writer.writerow(fields) 218 | writer.writerows(rows) 219 | 220 | messagebox.showinfo(title="Successfull", message="CSV file has been successfully created!") 221 | 222 | sleep(1) 223 | os.startfile(filepath) 224 | except: 225 | messagebox.showerror("Error", "Something Went Wrong!") 226 | 227 | def view_issued_books(self): 228 | self.cursor.execute('DESC ISSUED_BOOKS') 229 | fields = [] 230 | 231 | for i in self.cursor.fetchall(): 232 | fields.append(i[0]) 233 | 234 | self.cursor.execute("SELECT * FROM ISSUED_BOOKS") 235 | rows = self.cursor.fetchall() 236 | 237 | try: 238 | path = fd.asksaveasfile().name 239 | except: 240 | messagebox.showerror("Error","Please try to save the file with different name (if it is running in background)!") 241 | 242 | try: 243 | with open(path, 'w') as file: 244 | writer = csv.writer(file) 245 | writer.writerow(fields) 246 | writer.writerows(rows) 247 | 248 | messagebox.showinfo(title="Successfull", message="CSV file made!!") 249 | sleep(1) 250 | os.startfile(path) 251 | 252 | except: 253 | messagebox.showerror("Error", "Something Went Wrong!") 254 | 255 | def reset_password(self, new_passwrd, username, old_passwrd): 256 | self.cursor.execute("SELECT * FROM LIB_USERS") 257 | query1 = self.cursor.fetchall()[0] 258 | new_passwrd_hash = hashlib.sha256(new_passwrd.encode()).hexdigest() 259 | old_passwrd_hash = hashlib.sha256(old_passwrd.encode()).hexdigest() 260 | 261 | try: 262 | self.cursor.execute("UPDATE LIB_USERS SET PASSWORD=%s WHERE USERNAME=%s AND PASSWORD=%s", (new_passwrd_hash,username,old_passwrd_hash)) 263 | self.db.commit() 264 | except: 265 | messagebox.showerror("Error", "Something went wrong.") 266 | 267 | self.cursor.execute("SELECT * FROM LIB_USERS") 268 | query2 = self.cursor.fetchall()[0] 269 | 270 | if new_passwrd_hash == old_passwrd_hash: 271 | messagebox.showinfo("Info", "Old Password and New Password are same!") 272 | 273 | elif query1 == query2: 274 | messagebox.showerror("Error", "Username or Old Password is/are incorrect.") 275 | 276 | else: 277 | messagebox.showinfo("Info", "Password Changed Successfully.") 278 | 279 | def add_books_csv(self): 280 | with open("csv_books.csv","r") as file: 281 | reader = csv.reader(file) 282 | fields = next(reader) 283 | data = [i for i in reader] 284 | 285 | if data == []: 286 | messagebox.showinfo('Info","Please enter some data in "csv_books.csv file!"') 287 | 288 | elif data[0][0] == 'BOOK_ID': 289 | messagebox.showinfo("Info","Please enter column names in the very first row of the csv file!") 290 | 291 | else: 292 | try: 293 | for i in data: 294 | self.cursor.execute("INSERT INTO BOOKS VALUES(%s, %s, %s, %s, %s)",(i[0],i[1],i[2],i[3],i[4])) 295 | self.db.commit() 296 | messagebox.showinfo("Success","Data inserted successfully!!") 297 | 298 | except: 299 | messagebox.showerror("Error","Something went wrong!") 300 | 301 | 302 | # -----------------------------------------MAIN FUNCTIONS END-------------------------------- 303 | 304 | 305 | 306 | # -----------------------------------------GUI START----------------------------------------- 307 | 308 | def main_window(self): 309 | self.root.deiconify() 310 | self.root.state("zoomed") 311 | self.root.protocol("WM_DELETE_WINDOW", self.on_closing) 312 | 313 | menu = tk.Menu(self.root) 314 | 315 | help = tk.Menu(menu, tearoff = 0) 316 | menu.add_cascade(label ='Help', menu = help) 317 | help.add_command(label ='Change Fine Amount F1', command = self.reset_fine_gui) 318 | help.add_command(label ='Reset Password F2', command = self.reset_password_gui) 319 | help.add_command(label="Add Books (via csv) F5", command=self.add_books_csv) 320 | 321 | ck.CTkLabel(self.root, text="Welcome", text_font=("Roboto",25)).place(relx=0.25, rely=0.1, relheight=0.1, relwidth=0.5) 322 | 323 | add_btn = ck.CTkButton(self.root, text_font=("Roboto",16), text="Add Book", command=self.add_books_gui) 324 | add_btn.place(relx=0.28, rely=0.26, relwidth=0.45, relheight=0.1) 325 | 326 | delete_btn = ck.CTkButton(self.root, text_font=("Roboto",16), text="Delete Book", command=self.delete_book_gui) 327 | delete_btn.place(relx=0.28, rely=0.38, relwidth=0.45, relheight=0.1) 328 | 329 | view_btn = ck.CTkButton(self.root, text_font=("Roboto",16), text="Download List of Books", command=self.view_books) 330 | view_btn.place(relx=0.28, rely=0.50, relwidth=0.45, relheight=0.1) 331 | 332 | issue_btn = ck.CTkButton(self.root, text_font=("Roboto",16), text="Issue Book to Student", command=self.issue_books_gui) 333 | issue_btn.place(relx=0.28, rely=0.62, relwidth=0.45, relheight=0.1) 334 | 335 | return_btn = ck.CTkButton(self.root, text_font=("Roboto",16), text="Return Book", command=self.return_books_gui) 336 | return_btn.place(relx=0.28, rely=0.74, relwidth=0.45, relheight=0.1) 337 | 338 | view_issued = ck.CTkButton(self.root, text_font=("Roboto",16), text="View Issued Books", command=self.view_issued_books) 339 | view_issued.place(relx=0.28, rely=0.86, relwidth=0.45, relheight=0.1) 340 | 341 | self.root.config(menu = menu) 342 | self.root.mainloop() 343 | 344 | def add_books_gui(self): 345 | self.win_add.protocol("WM_DELETE_WINDOW", self.win_add_on_closing) 346 | 347 | self.win_add.deiconify() 348 | self.win_add.state("zoomed") 349 | 350 | ck.CTkLabel(self.win_add, text="Add Books", text_font=("Roboto",30)).place(relx=0.21, rely=0.1, relheight=0.1, relwidth=0.6) 351 | 352 | add_frame = ck.CTkFrame(self.win_add, bg='black') 353 | add_frame.place(relx=0.27, rely=0.4, relheight=0.5, relwidth=0.5) 354 | 355 | ck.CTkLabel(add_frame, text="Book ID:", text_font=("Roboto",18)).place(relx=0.05, rely=0.03) 356 | 357 | book_id = ck.CTkEntry(add_frame, width=480, text_font=("Roboto",18)) 358 | book_id.place(relx=0.3, rely=0.03) 359 | 360 | ck.CTkLabel(add_frame, text="Book Name:", text_font=("Roboto",18)).place(relx=0.05, rely=0.16) 361 | 362 | book_name = ck.CTkEntry(add_frame, width=480, text_font=("Roboto",18)) 363 | book_name.place(relx=0.3, rely=0.16) 364 | 365 | ck.CTkLabel(add_frame, text="Author Name:", text_font=("Roboto",18)).place(relx=0.05, rely=0.29) 366 | 367 | author = ck.CTkEntry(add_frame, width=480, text_font=("Roboto",18)) 368 | author.place(relx=0.3, rely=0.29) 369 | 370 | ck.CTkLabel(add_frame, text="Book Price:", text_font=("Roboto",18)).place(relx=0.05, rely=0.42) 371 | 372 | bookprice = ck.CTkEntry(add_frame, width=480, text_font=("Roboto",18)) 373 | bookprice.place(relx=0.3, rely=0.42) 374 | 375 | ck.CTkLabel(add_frame, text="Book Qty:", text_font=("Roboto",18)).place(relx=0.05, rely=0.55) 376 | 377 | qty = ck.CTkEntry(add_frame, width=480, text_font=("Roboto",18)) 378 | qty.place(relx=0.3, rely=0.55) 379 | 380 | submit = ck.CTkButton(add_frame, text="Submit", text_font=("Roboto",18), command=lambda: self.add_books(book_id, book_name, author, bookprice, qty)) 381 | submit.place(relx=0.40, rely=0.75, relwidth=0.4) 382 | 383 | self.win_add.mainloop() 384 | 385 | 386 | def delete_book_gui(self): 387 | self.win_del.protocol("WM_DELETE_WINDOW", self.win_del_on_closing) 388 | 389 | self.win_del.deiconify() 390 | self.win_del.state("zoomed") 391 | 392 | ck.CTkLabel(self.win_del, text="Delete Books", text_font=("Roboto",30)).place(relx=0.21, rely=0.1, relheight=0.1, relwidth=0.6) 393 | 394 | del_frame = ck.CTkFrame(self.win_del, bg='black') 395 | del_frame.place(relx=0.27, rely=0.4, relheight=0.5, relwidth=0.5) 396 | 397 | ck.CTkLabel(del_frame, text="Book ID:", text_font=("Roboto",18)).place(relx=0.1, rely=0.4) 398 | 399 | del_book_id = ck.CTkEntry(del_frame, width=400, text_font=("Roboto",18)) 400 | del_book_id.place(relx=0.36, rely=0.4) 401 | 402 | submit = ck.CTkButton(del_frame, text="Submit", text_font=("Roboto",18), command=lambda: self.delete_book(del_book_id)) 403 | submit.place(relx=0.3,rely=0.75, relwidth=0.5) 404 | 405 | self.win_del.mainloop() 406 | 407 | 408 | def issue_books_gui(self): 409 | self.issue.protocol("WM_DELETE_WINDOW", self.win_issue_on_closing) 410 | self.issue.deiconify() 411 | self.issue.state("zoomed") 412 | 413 | issue_frame = ck.CTkFrame(self.issue) 414 | issue_frame.place(relx=0.16, rely=0.28, relheight=0.65, relwidth=0.7) 415 | 416 | ck.CTkLabel(self.issue, text="Issue Books", text_font=("Roboto",25)).place(relx=0.21, rely=0.1, relheight=0.1, relwidth=0.6) 417 | ck.CTkLabel(issue_frame, text="Student Name -", text_font=("Roboto",18)).place(relx=0.05, rely=0.07) 418 | 419 | std_name = ck.CTkEntry(issue_frame, width=480, text_font=("Roboto",18)) 420 | std_name.place(relx=0.43, rely=0.07) 421 | 422 | ck.CTkLabel(issue_frame, text="Class -", text_font=("Roboto",20)).place(relx=0.05, rely=0.18) 423 | 424 | class_ = ck.CTkEntry(issue_frame, width=480, text_font=("Roboto",18)) 425 | class_.place(relx=0.43, rely=0.18) 426 | 427 | ck.CTkLabel(issue_frame, text="Section -", text_font=("Roboto",18)).place(relx=0.05, rely=0.31) 428 | 429 | section = ck.CTkEntry(issue_frame, width=480, text_font=("Roboto",18)) 430 | section.place(relx=0.43, rely=0.31) 431 | 432 | ck.CTkLabel(issue_frame, text="Roll Number -", text_font=("Roboto",18)).place(relx=0.05, rely=0.44) 433 | 434 | roll = ck.CTkEntry(issue_frame, width=480, text_font=("Roboto",18)) 435 | roll.place(relx=0.43, rely=0.44) 436 | 437 | ck.CTkLabel(issue_frame, text="Book ID -", text_font=("Roboto",18)).place(relx=0.05, rely=0.57) 438 | 439 | issue_bid = ck.CTkEntry(issue_frame, width=480, text_font=("Roboto",18)) 440 | issue_bid.place(relx=0.43, rely=0.57) 441 | 442 | ck.CTkLabel(issue_frame, text="Issue Date (YYYY-MM-DD) -", text_font=("Roboto",18)).place(relx=0.05, rely=0.70) 443 | 444 | issue_date = ck.CTkEntry(issue_frame, width=480, text_font=("Roboto",18)) 445 | issue_date.place(relx=0.43, rely=0.70) 446 | 447 | submit = ck.CTkButton(issue_frame, text="Submit", text_font=("Roboto",18), command=lambda: self.issue_books(std_name, class_, section, roll, issue_bid, issue_date)) 448 | submit.place(relx=0.3, rely=0.85, relwidth=0.5) 449 | 450 | self.issue.mainloop() 451 | 452 | def return_books_gui(self): 453 | self.win_return.protocol("WM_DELETE_WINDOW", self.win_return_on_closing) 454 | self.win_return.deiconify() 455 | self.win_return.state("zoomed") 456 | 457 | return_frame = ck.CTkFrame(self.win_return, bg='black') 458 | return_frame.place(relx=0.16, rely=0.28, relheight=0.65, relwidth=0.7) 459 | 460 | ck.CTkLabel(self.win_return, text="Return Books", text_font=("Roboto",25)).place(relx=0.21, rely=0.1, relheight=0.1, relwidth=0.6) 461 | 462 | ck.CTkLabel(return_frame, bg='black', fg='lime', text="Student ID -", text_font=("Roboto",20)).place(relx=0.05, rely=0.35) 463 | 464 | return_stdid = ck.CTkEntry(return_frame, width=480, text_font=("Roboto",18)) 465 | return_stdid.place(relx=0.37, rely=0.35) 466 | 467 | ck.CTkLabel(return_frame, text="Return Date -", text_font=("Roboto",20)).place(relx=0.05, rely=0.48) 468 | 469 | return_date = ck.CTkEntry(return_frame, width=480, text_font=("Roboto",18)) 470 | return_date.place(relx=0.37, rely=0.48) 471 | 472 | submit = ck.CTkButton(return_frame, text="Submit", text_font=("Roboto",22), command=lambda: self.return_books(return_date, return_stdid)) 473 | submit.place(relx=0.3, rely=0.8, relwidth=0.5) 474 | 475 | self.win_return.mainloop() 476 | 477 | def reset_fine_gui(self): 478 | my_fine = ck.CTkInputDialog(self.root, title="Fine", text="Enter your desired fine.") 479 | my_fine = my_fine.get_input() 480 | 481 | try: 482 | self.fine = int(my_fine) 483 | messagebox.showinfo("Info","Fine Updated Successfully!") 484 | with open("info.dat",'wb') as file: 485 | data = {"username":self.mysql_username,"password":self.mysql_password,"fine":self.fine} 486 | data = f'hafhahgha[adauhaicnacb[]a[afag=+{str(data)}+ajkfhacuagugxuabjrbjakajajda=-=-===[]agag'.encode("ascii") 487 | data = base64.b64encode(data) 488 | pickle.dump(data,file) 489 | except: 490 | messagebox.showerror("Error", "Something went wrong!") 491 | 492 | def reset_password_gui(self): 493 | self.reset.protocol("WM_DELETE_WINDOW", self.reset_on_closing) 494 | self.reset.deiconify() 495 | self.reset.state("zoomed") 496 | 497 | ck.CTkLabel(self.reset, text="Reset Password", text_font=("Roboto",25)).place(relx=0.45, rely=0.1) 498 | 499 | ck.CTkLabel(self.reset, text="Username: ", text_font=("Roboto",20)).place(relx=0.26, rely=0.35) 500 | username = ck.CTkEntry(self.reset, width=480) 501 | username.place(relx=0.43, rely=0.35) 502 | 503 | ck.CTkLabel(self.reset, text="Old Password: ", text_font=("Roboto",20),).place(relx=0.26, rely=0.45) 504 | 505 | old_passwrd = ck.CTkEntry(self.reset, width=480, show="*") 506 | old_passwrd.place(relx=0.43, rely=0.45) 507 | 508 | hide_var1 = ck.StringVar(value="on") 509 | 510 | def switch_event1(): 511 | if hide_var1.get() == "on": 512 | old_passwrd.configure(show = "*") 513 | else: 514 | old_passwrd.configure(show = "") 515 | 516 | hide_switch = ck.CTkSwitch(master=self.reset, text="Hide Password", command=switch_event1, 517 | variable=hide_var1, onvalue="on", offvalue="off", text_font=("Roboto",13)) 518 | hide_switch.place(relx=0.76, rely=0.46) 519 | 520 | 521 | ck.CTkLabel(self.reset, text="New Password: ", text_font=("Roboto",20)).place(relx=0.26, rely=0.55) 522 | new_passwrd = ck.CTkEntry(self.reset, width=480, show="*") 523 | new_passwrd.place(relx=0.43,rely=0.55) 524 | 525 | hide_var2 = ck.StringVar(value="on") 526 | 527 | def switch_event2(): 528 | if hide_var2.get() == "on": 529 | new_passwrd.configure(show = "*") 530 | else: 531 | new_passwrd.configure(show = "") 532 | 533 | hide_switch2 = ck.CTkSwitch(master=self.reset, text="Hide Password", command=switch_event2, 534 | variable=hide_var2, onvalue="on", offvalue="off", text_font=("Roboto",13)) 535 | hide_switch2.place(relx=0.76, rely=0.55) 536 | 537 | 538 | reset_btn = ck.CTkButton(self.reset, text="Submit", width=240, text_font=("Roboto",18), command=lambda: self.reset_password(new_passwrd.get(), username.get(), old_passwrd.get())) 539 | reset_btn.place(relx=0.46, rely=0.7) 540 | 541 | self.reset.mainloop() 542 | 543 | def load(self): 544 | ck.set_appearance_mode("dark") 545 | 546 | load = ck.CTk() 547 | load.title("Loading") 548 | load.attributes("-fullscreen",True) 549 | load.config(bg = "black") 550 | 551 | ck.CTkLabel(load, text="Army Public School, New Cantt", text_font=("Courier New",25), text_color="lime").place(relx=0.3, rely=0.05) 552 | ck.CTkLabel(load, text="Prayagraj", text_font=("Courier New",25), text_color="lime").place(relx=0.42, rely=0.11) 553 | ck.CTkLabel(load, text="Library Management System", text_font=("Courier New",25), text_color="lime").place(relx=0.32, rely=0.17) 554 | ck.CTkLabel(load, text="Loading...", text_font=("Courier New",25), text_color="lime").place(relx=0.17, rely=0.43) 555 | 556 | box = 0.15 557 | for i in range(35): 558 | ck.CTkLabel(load, text="", bg_color='black', height=20, width=20).place(relx=(box)+0.02, rely=0.5) 559 | box += 0.02 560 | 561 | for i in range(3): 562 | box = 0.15 563 | for j in range(35): 564 | ck.CTkLabel(load, text="", bg_color="lime", height=20, width=20).place(relx=(box)+0.02, rely=0.5) 565 | sleep(0.02) 566 | box += 0.02 567 | load.update() 568 | box = 0.15 569 | 570 | for j in range(35): 571 | ck.CTkLabel(load, text="", bg_color='black', height=20, width=20).place(relx=(box)+0.02, rely=0.5) 572 | sleep(0.02) 573 | box += 0.02 574 | load.update() 575 | load.destroy() 576 | 577 | 578 | def check_creds(self, screen, username, passwrd): 579 | try: 580 | self.db = conn.connect(host='localhost', user=self.mysql_username, password=self.mysql_password, database='library') 581 | self.cursor = self.db.cursor() 582 | except conn.Error: 583 | reply = messagebox.askyesno("Error","Incorrect MySQL Username/Password!!\nDo you want to update your MySQL credentials?") 584 | if reply: 585 | self.start() 586 | else: 587 | exit() 588 | 589 | self.cursor.execute("SELECT * FROM lib_users") 590 | result = self.cursor.fetchall()[0] 591 | passwrd_hash = hashlib.sha256(passwrd.get().encode()).hexdigest() 592 | 593 | if (result[0] == username.get()) and (result[1] == passwrd_hash): 594 | screen.destroy() 595 | self.load() 596 | self.main_window() 597 | else: 598 | messagebox.showerror("Error", "Invalid Credentials.") 599 | 600 | 601 | def login(self): 602 | login_screen =ck.CTk() 603 | login_screen.title("Login") 604 | login_screen.geometry("950x400") 605 | login_screen.resizable(False,False) 606 | login_screen.protocol("WM_DELETE_WINDOW", self.login_on_closing) 607 | 608 | 609 | ck.CTkLabel(login_screen, text="Login", text_font=("Roboto",22,'bold')).place(relx=0.45, rely=0.05) 610 | ck.CTkLabel(login_screen, text="Username:", text_font=("Roboto",18)).place(relx=0.15, rely=0.3) 611 | 612 | username = ck.CTkEntry(login_screen, width=360) 613 | username.place(relx=0.35, rely=0.3) 614 | 615 | ck.CTkLabel(login_screen, text="Password:", text_font=("Roboto",18)).place(relx=0.15, rely=0.6) 616 | passwrd = ck.CTkEntry(login_screen, width=360, show="*") 617 | passwrd.place(relx=0.35, rely=0.6) 618 | 619 | hide_var = ck.StringVar(value="on") 620 | 621 | def switch_event(): 622 | if hide_var.get() == "on": 623 | passwrd.configure(show = "*") 624 | else: 625 | passwrd.configure(show = "") 626 | 627 | hide_switch = ck.CTkSwitch(master=login_screen, text="Hide Password", command=switch_event, 628 | variable=hide_var, onvalue="on", offvalue="off", text_font=("Roboto",13)) 629 | hide_switch.place(relx=0.75, rely=0.61) 630 | 631 | submit = ck.CTkButton(login_screen, text="Submit", text_font=("Roboto",12), command=lambda: self.check_creds(login_screen, username, passwrd)) 632 | submit.place(relx=0.45, rely=0.8) 633 | 634 | login_screen.mainloop() 635 | 636 | def start(self): 637 | my_user = ck.CTkInputDialog(self.root, title="Username", text="Enter your mysql username.") 638 | my_user = my_user.get_input() 639 | 640 | my_pass = ck.CTkInputDialog(self.root, title="Password", text="Enter your mysql password.") 641 | my_pass = my_pass.get_input() 642 | 643 | fine = ck.CTkInputDialog(self.root, title="Fine", text="Enter desired fine amount.") 644 | fine = fine.get_input() 645 | 646 | try: 647 | self.db = conn.connect(host='localhost', user=my_user, password=my_pass, db='library') 648 | self.cursor = self.db.cursor() 649 | 650 | self.mysql_password = my_pass 651 | self.mysql_username = my_user 652 | 653 | with open("info.dat",'wb') as file: 654 | data = {"username":my_user,"password":my_pass,"fine":fine} 655 | data = f'hafhahgha[adauhaicnacb[]a[afag=+{str(data)}+ajkfhacuagugxuabjrbjakajajda=-=-===[]agag'.encode("ascii") 656 | data = base64.b64encode(data) 657 | pickle.dump(data,file) 658 | except: 659 | messagebox.showerror("Error","Something went wrong!!\nTry Checking your MySQL Credentials!") 660 | 661 | self.login() 662 | 663 | # -----------------------------------------------GUI END----------------------------------------------- 664 | 665 | current_version = 'v1.0' 666 | 667 | 668 | response = requests.get("https://api.github.com/repos/mymadhavyadav07/Library-Management-System/releases/latest") 669 | try: 670 | reply = response.json()['message'] 671 | if reply == "Not Found": 672 | print("No version available") 673 | except KeyError: 674 | version = response.json()['name'] 675 | repo_url = f"https://github.com/mymadhavyadav07/Library-Management-System/archive/refs/tags/{version}.zip" 676 | 677 | if current_version < version: 678 | reply = messagebox.askyesno("Info","New version is available.\nDo you want an update??") 679 | if reply: 680 | r = requests.get(repo_url) 681 | with open(f"Library-Management-System({version}).zip",'wb') as f: 682 | f.write(r.content) 683 | messagebox.showinfo("Info","Please switch to the new version :)") 684 | exit() 685 | 686 | screen = main() 687 | if exists("info.dat"): 688 | with open("info.dat",'rb') as file: 689 | info = pickle.load(file) 690 | info = eval(base64.b64decode(info).decode("ascii").split("+")[1]) 691 | screen.mysql_username = info['username'] 692 | screen.mysql_password = info['password'] 693 | screen.fine = int(info["fine"]) 694 | screen.login() 695 | else: 696 | screen.start() 697 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | customtkinter==4.6.3 2 | mysql-connector-python==8.0.28 3 | pyTelegramBotAPI==4.7.0 4 | -------------------------------------------------------------------------------- /tele_creds.txt: -------------------------------------------------------------------------------- 1 | MYSQL_USERNAME: 2 | MYSQL_USERNAME: 3 | TELEGRAM_BOT_API_TOKEN: -------------------------------------------------------------------------------- /tele_server.py: -------------------------------------------------------------------------------- 1 | from tkinter import messagebox 2 | import mysql.connector as conn 3 | import telebot 4 | import io 5 | import csv 6 | 7 | try: 8 | with open("tele_creds.txt",'r') as file: 9 | contents = file.read().split("\n") 10 | MYSQL_USERNAME = contents[0].partition(":")[2] 11 | MYSQL_PASSWORD = contents[1].partition(":")[2] 12 | API_TOKEN = contents[2].partition(":")[2] 13 | file.close() 14 | except FileNotFoundError: 15 | messagebox.showerror("Error", "tele_creds.txt not found on current path!!") 16 | 17 | CHAT_ID = '' 18 | HELP = f"""COMMANDS: 19 | /help - show this message 20 | /view - view all books 21 | /issued - view all issued books""" 22 | 23 | db = conn.connect(host = 'localhost', user = MYSQL_USERNAME, password = MYSQL_PASSWORD, database='library') 24 | cursor = db.cursor() 25 | 26 | bot = telebot.TeleBot(API_TOKEN) 27 | BOT_NAME = bot.get_me().first_name 28 | 29 | def online_alert(): 30 | try: 31 | cursor.execute("SELECT * FROM TELEBOT") 32 | result = cursor.fetchall() 33 | for i in result: 34 | bot.send_message(chat_id=int(i[0]), text="Bot is Online!") 35 | except: 36 | pass 37 | 38 | online_alert() 39 | 40 | @bot.message_handler(commands = ['help']) 41 | def help(message): 42 | bot.send_message(message.chat.id, HELP) 43 | 44 | 45 | @bot.message_handler(commands = ['start']) 46 | def start(message): 47 | cursor.execute("SELECT * FROM TELEBOT WHERE STUDENT_ID=%s",(message.chat.id,)) 48 | result = cursor.fetchall() 49 | CHAT_ID = message.chat.id 50 | 51 | if result == []: 52 | cursor.execute("INSERT INTO TELEBOT(STUDENT_ID) VALUES(%s)", (message.chat.id,)) 53 | db.commit() 54 | 55 | 56 | bot.send_message(message.chat.id, f"Welcome to {BOT_NAME}") 57 | bot.send_message(message.chat.id, HELP) 58 | 59 | 60 | @bot.message_handler(commands = ['view']) 61 | def view(message): 62 | cursor.execute("DESC books") 63 | result = cursor.fetchall() 64 | fields = [] 65 | 66 | for i in result: 67 | fields.append(i[0]) 68 | 69 | cursor.execute("SELECT * FROM books") 70 | data = cursor.fetchall() 71 | 72 | # csv module can write data in io.StringIO buffer only 73 | s = io.StringIO() 74 | csv.writer(s).writerow(fields) 75 | csv.writer(s).writerows(data) 76 | s.seek(0) 77 | 78 | # telebot library can send files only from io.BytesIO buffer 79 | # we need to convert StringIO to BytesIO 80 | buf = io.BytesIO() 81 | buf.write(s.getvalue().encode()) 82 | buf.seek(0) 83 | buf.name = f'BOOKS.csv' 84 | 85 | bot.send_document(message.chat.id, document=buf) 86 | 87 | 88 | @bot.message_handler(commands = ['issued']) 89 | def generate_csv(query): 90 | student_name = query.text 91 | 92 | cursor.execute("DESC issued_books") 93 | result = cursor.fetchall() 94 | fields = [] 95 | 96 | for i in result: 97 | fields.append(i[0]) 98 | 99 | cursor.execute("SELECT * FROM issued_books WHERE STUDENT_NAME=%s", (student_name,)) 100 | result = cursor.fetchall() 101 | data = [] 102 | for i in result: 103 | row = [] 104 | row.append(i[0]) 105 | row.append(i[1]) 106 | row.append(i[2]) 107 | row.append(i[3]) 108 | row.append(i[4]) 109 | row.append(i[5]) 110 | row.append(i[6]) 111 | row.append(i[7].strftime('%Y-%m-%d')) 112 | row.append(i[8]) 113 | 114 | data.append(row) 115 | 116 | s = io.StringIO() 117 | csv.writer(s).writerow(fields) 118 | csv.writer(s).writerows(data) 119 | s.seek(0) 120 | 121 | buf = io.BytesIO() 122 | buf.write(s.getvalue().encode()) 123 | buf.seek(0) 124 | buf.name = f'ISSUED_BOOKS.csv' 125 | 126 | bot.send_document(query.chat.id, document=buf) 127 | 128 | 129 | bot.infinity_polling() 130 | --------------------------------------------------------------------------------