diff --git a/README.md b/README.md index 9a0c952..0df43f9 100644 --- a/README.md +++ b/README.md @@ -1 +1,82 @@ -# Library Management System +# Library Icon Library Management System +**A console-based Library Management System that demonstrates understanding of Kotlin's core concepts including OOP, functional programming, collections (HashMaps), recursion, and more.** + +Console Icon Kotlin Icon + +## ❓How To Run +Navigate to `app/src/main/kotlin/org/example/Main.kt` + +Run `fun main()` + +## ⭐️ Features +## 🔶 Object-Oriented Programming ## +✅ Abstraction + +An abstract LibraryItem class with core variables and abstract functions + +`app/src/main/kotlin/org/example/LibraryItem.kt` + +✅ Inheritance & Polymorphism + +Book, DVD and Magazine classes which inherit variables from LibraryItem and uniquely implement abstract functions + +`app/src/main/kotlin/org/example/Book.kt` +`app/src/main/kotlin/org/example/DVD.kt` +`app/src/main/kotlin/org/example/Magazine.kt` + +✅ Encapsulation + +A Member class which encapsulates all variables and functions required for Member objects + +`app/src/main/kotlin/org/example/Member.kt` + +## 🔶 Collections & HashMaps ## + +A Library class which uses HashMaps to hold collections of LibraryItems, Members and track Borrowed Items. + +`app/src/main/kotlin/org/example/Library.kt` + +## 🔶 Functional Programming ## + +✅ Higher-Order Functions & Lambdas + +findBookByAuthor(), findItemBy() + +to specify search by criteria + +getLibraryStatistics() + +to get formatted display of entire Library data + +processOverdueItems() + +to process overdue fees on items borrowed by members + +`app/src/main/kotlin/org/example/Library.kt` + +✅ Extension Functions + +Implemented in the Library class fuctions + +List.filterByAvailability() + +to filter a list of LibraryItems and filter by set Availability (true or false) + +String.isValidEmail() + +to varify if a certain string is a valid email + +LibraryItem.getFormattedInfo() + +to obtain a well formatted display of LibraryItem information + +`app/src/main/kotlin/org/example/Library.kt` + +## 🔶 Recursion ## + +calculateCompoundLateFee() + +a recursive function which calculates compounded late fees on item based on a base amount and number of days the item is late for return + +`app/src/main/kotlin/org/example/RecursiveFunctions.kt` + diff --git a/app/src/main/kotlin/org/example/App.kt b/app/src/main/kotlin/org/example/App.kt deleted file mode 100644 index a1d413b..0000000 --- a/app/src/main/kotlin/org/example/App.kt +++ /dev/null @@ -1,15 +0,0 @@ -/* - * This source file was generated by the Gradle 'init' task - */ -package org.example - -class App { - val greeting: String - get() { - return "Hello World!" - } -} - -fun main() { - println(App().greeting) -} diff --git a/app/src/main/kotlin/org/example/Book.kt b/app/src/main/kotlin/org/example/Book.kt new file mode 100644 index 0000000..ad9dda4 --- /dev/null +++ b/app/src/main/kotlin/org/example/Book.kt @@ -0,0 +1,14 @@ +package org.example + +class Book( + id: String, + title: String, + val author: String, + val isbn: String, + val pages: Int +) : LibraryItem(id, title) { + override fun getItemType(): String = "Book" + override fun calculateLateFee(daysLate: Int): Double { + return daysLate * 0.5 + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/org/example/DVD.kt b/app/src/main/kotlin/org/example/DVD.kt new file mode 100644 index 0000000..b9a04df --- /dev/null +++ b/app/src/main/kotlin/org/example/DVD.kt @@ -0,0 +1,14 @@ +package org.example + +class DVD( + id: String, + title: String, + val director: String, + val duration: Int, + val genre: String +) : LibraryItem(id, title) { + override fun getItemType(): String = "DVD" + override fun calculateLateFee(daysLate: Int): Double { + return daysLate * 1.0 + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/org/example/Library.kt b/app/src/main/kotlin/org/example/Library.kt new file mode 100644 index 0000000..7f8628f --- /dev/null +++ b/app/src/main/kotlin/org/example/Library.kt @@ -0,0 +1,148 @@ +package org.example + +import kotlin.math.PI +import kotlin.math.pow + +class Library { + // Map of item id to LibraryItem + private val itemsById = HashMap() + + // Map of category to list of LibraryItems in category + private val itemsByCategory = HashMap>() + + // Map of memberId to Member + private val members = HashMap() + + // Map of memberId to Member.borrowedItems + private val borrowedItems = HashMap>() + + //library.addItem(Book("B001", "The Kotlin Guide", "John Doe", "978-1234567890", 300)) + fun addItem(item: LibraryItem) { + itemsById[item.id] = item + itemsByCategory[item.getItemType()]?.add(item) + } + //library.registerMember(Member("M001", "Alice Johnson", "alice@email.com")) + fun registerMember(member: Member) { + members[member.getMemberId()] = member + } + //borrowItem("M001", "B001") + fun borrowItem(memberId: String, itemId: String) { + + var member: Member? = null + if(members.contains(memberId)){ + member = members[memberId] + } + + var item: LibraryItem? = null + if(itemsById.contains(itemId)) { + item = itemsById[itemId] + } + + if(member != null && item != null) { + member.borrowItem(item) + borrowedItems[memberId]?.add(itemId) + } + } + + fun returningItem(memberId: String, itemId: String) { + + var member: Member? = null + if(members.contains(memberId)){ + member = members[memberId] + } + + var item: LibraryItem? = null + if(itemsById.contains(itemId)) { + item = itemsById[itemId] + } + + if(member != null && item != null) { + member.returnItem(item) + borrowedItems[memberId]?.remove(itemId) + } + } + + //library.findBooksByAuthor("John Doe") + fun findBooksByAuthor(author: String): List { +// + val booksInLibrary = itemsByCategory["Book"] as MutableList? + + val booksByAuthor: MutableList = mutableListOf() + + booksInLibrary?.forEach { book -> + if(book.author == author) { + booksByAuthor.add(book) + } + } + + val result = booksByAuthor.toList() + + return result + } + + +// fun findItemsBy( +// type: Class, +// predicate: (T) -> Boolean +// ) : List { +// if((itemsByCategory).any(type)) +// +// } + + fun getLibraryStatistics(): Map { + val numberOfBooks = itemsByCategory["Book"]?.count() + val numberOfDVDs = itemsByCategory["DVD"]?.count() + val numberOfMagazines = itemsByCategory["Magazine"]?.count() + + val result: Map + + result = mapOf( + Pair("Number of Books in Library:",numberOfBooks), + Pair("Number of DVDs in Library:",numberOfDVDs), + Pair("Number of Magazines in Library:",numberOfMagazines) + + ) as Map + + return result + } + + fun processOverdueItems(action: (LibraryItem, Member, Int) -> Unit) { + + } + + fun List.filterByAvailability(available: Boolean): List { + val items = itemsById.values + + val resultList: MutableList = mutableListOf() + + items.forEach { item -> + if(item.isAvailable == available) { + resultList.add(item) + } + } + + return resultList.toList() + } + + fun String.isValidEmail(): Boolean { + if( + String.toString().contains('@') && + String.toString().contains(".") + ) { + return true + } + else { + return false + } + } + +// fun LibraryItem.getFormattedInfo(): String { +// val item: LibraryItem +// val info: String +// +// info = "Item ID: ${item.id}" +// } + + + +} \ No newline at end of file diff --git a/app/src/main/kotlin/org/example/LibraryItem.kt b/app/src/main/kotlin/org/example/LibraryItem.kt new file mode 100644 index 0000000..a0f790e --- /dev/null +++ b/app/src/main/kotlin/org/example/LibraryItem.kt @@ -0,0 +1,16 @@ +package org.example + +abstract class LibraryItem( + val id: String, + val title: String, + var isAvailable: Boolean = true +) { + abstract fun getItemType(): String + abstract fun calculateLateFee(daysLate: Int): Double + + open fun displayInfo(): String { + return "ID: $id, Title: $title, Available: $isAvailable" + } + + +} \ No newline at end of file diff --git a/app/src/main/kotlin/org/example/Magazine.kt b/app/src/main/kotlin/org/example/Magazine.kt new file mode 100644 index 0000000..a273bbb --- /dev/null +++ b/app/src/main/kotlin/org/example/Magazine.kt @@ -0,0 +1,13 @@ +package org.example + +class Magazine( + id: String, + title: String, + val issueNumber: Int, + val publisher: String +) : LibraryItem(id, title) { + override fun getItemType(): String = "Magazine" + override fun calculateLateFee(daysLate: Int): Double { + return daysLate * 0.25 + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/org/example/Main.kt b/app/src/main/kotlin/org/example/Main.kt new file mode 100644 index 0000000..94fc8a5 --- /dev/null +++ b/app/src/main/kotlin/org/example/Main.kt @@ -0,0 +1,54 @@ +/* + * This source file was generated by the Gradle 'init' task + */ +package org.example + +fun main() { + val smith= Member("M1", "S", "s") + smith.setName("Smith") + smith.setEmail("smith.com") + smith.setEmail("smith@gmail.com") + + val john = Member("M2", "John", "john@gmail.com") + + val library = Library() +// Add sample data + library.addItem(Book("B1", "The Kotlin Guide", "John Doe", "978-1234567890", 300)) + library.addItem(Book("B2", "Harry Potter", "JK", "1111-222", 122)) + library.addItem(DVD("D1", "Kotlin Tutorial", "Jane Smith", 120, "Educational")) + library.addItem(Magazine("Z1", "Times Magazine", 25, "NY-Times")) + +// Register member + library.registerMember(Member("M3", "Alice Johnson", "alice@email.com")) + library.registerMember(smith) + library.registerMember(john) + + val libraryStats = library.getLibraryStatistics() + println("Library Stats: $libraryStats") + +// Demonstrate borrowing + library.borrowItem("M1", "B1") + library.borrowItem("M2", "B1") + library.borrowItem("M2", "B2") + library.borrowItem("M2", "D1") + library.borrowItem("M3", "Z1") + +//Demostrate returning + library.returningItem("M1", "B2") + library.returningItem("M1", "B1") + + smith.totalLateFees(5) + john.totalLateFees(5) +// Show functional programming + val availableBooks = library.findBooksByAuthor("John Doe") + .filter { it.isAvailable } +// .map { it.getFormattedInfo() } + println("Available books by John Doe:") + availableBooks.forEach { book -> + println(book.title) + } +//// Demonstrate recursion + val compoundFee = calculateCompoundLateFee(5.0, 7) + println("Compound late fee for 7 days: $$compoundFee") + +} diff --git a/app/src/main/kotlin/org/example/Member.kt b/app/src/main/kotlin/org/example/Member.kt new file mode 100644 index 0000000..cc3a22a --- /dev/null +++ b/app/src/main/kotlin/org/example/Member.kt @@ -0,0 +1,64 @@ +package org.example + +class Member( + private val memberId: String, + private var name: String, + private var email: String +) { + private val borrowedItems = mutableListOf() + + fun getMemberId(): String = memberId + fun getName(): String = name + fun getEmail(): String = email + + fun setName(memberName: String) { + name = memberName + println("Member Name set to $name") + } + fun setEmail(memberEmail: String) { + if( + memberEmail.contains('@') + && + memberEmail.contains('.') + ) { + email = memberEmail + println("Member Email set to $email") + } else { + println("invalid email") + } + } + + fun borrowItem(item: LibraryItem) { + if(item.isAvailable) { + borrowedItems.add(item) + item.isAvailable = false + println("Member $memberId $name borrowed item ${item.id} ${item.title}") + } + else { + println("item unavailable") + } + } + + fun returnItem(item: LibraryItem) { + if(borrowedItems.contains(item)) { + borrowedItems.remove(item) + item.isAvailable = true + println("Member $memberId $name returned item ${item.id} ${item.title}") + } else { + println("Member $memberId $name did not borrow this item") + } + } + + fun totalLateFees(daysLate: Int) { + if(borrowedItems.isEmpty()) { + println("$name borrowed no items") + } + else { + var total = 0.0 + borrowedItems.forEach { item -> + total += item.calculateLateFee(daysLate) + } + println("$name Total Late Fees = $$total") + } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/org/example/RecursiveFunctions.kt b/app/src/main/kotlin/org/example/RecursiveFunctions.kt new file mode 100644 index 0000000..36f9f8b --- /dev/null +++ b/app/src/main/kotlin/org/example/RecursiveFunctions.kt @@ -0,0 +1,15 @@ +package org.example + +fun calculateCompoundLateFee(baseFee: Double, days: Int): Double { + // Using Math +// val fee: Double = baseFee * (1.05).pow(days) +// return fee + + // Recursive Function + var fee = baseFee + if(days != 0) { + fee = fee + baseFee * 1.05 + calculateCompoundLateFee(baseFee = fee, days = days - 1 ) + } + return fee +} \ No newline at end of file diff --git a/app/src/main/resources/cmd.png b/app/src/main/resources/cmd.png new file mode 100644 index 0000000..02d0548 Binary files /dev/null and b/app/src/main/resources/cmd.png differ diff --git a/app/src/main/resources/kotlin.png b/app/src/main/resources/kotlin.png new file mode 100644 index 0000000..9222d0e Binary files /dev/null and b/app/src/main/resources/kotlin.png differ diff --git a/app/src/main/resources/library.png b/app/src/main/resources/library.png new file mode 100644 index 0000000..50a68b0 Binary files /dev/null and b/app/src/main/resources/library.png differ