diff --git a/data/Mood.csv b/data/Mood.csv new file mode 100644 index 0000000000000000000000000000000000000000..a618430dd43e22116b755e69d7363f2f4612796d --- /dev/null +++ b/data/Mood.csv @@ -0,0 +1 @@ +tanggal,rate,relax level,energy level \ No newline at end of file diff --git a/data/Quote.csv b/data/Quote.csv new file mode 100644 index 0000000000000000000000000000000000000000..8c107be445e9fc228b197bb4020cbcd73466b36e --- /dev/null +++ b/data/Quote.csv @@ -0,0 +1 @@ +id,author,content diff --git a/data/Sleep.csv b/data/Sleep.csv new file mode 100644 index 0000000000000000000000000000000000000000..773b9775d962df6b58ca0ec38e8c1bc4db7769ba --- /dev/null +++ b/data/Sleep.csv @@ -0,0 +1 @@ +tanggal,jamMulai,jamSelesai,rating diff --git a/src/Mood/MoodModification.py b/src/Mood/MoodModification.py new file mode 100644 index 0000000000000000000000000000000000000000..e4e934cd6814f644cd58e3d692976fa20e685889 --- /dev/null +++ b/src/Mood/MoodModification.py @@ -0,0 +1,41 @@ +# File : MoodModification.py +# Berisi kelas entitas MoodModification, yang bertanggung jawab untuk +# Menyimpan informasi mood di suatu tanggal tertentu + +from Utility.Date import Date + +class MoodModification : + # CONSTRUCTOR + # Menginisialisasi objek MoodModification + def __init__(self, dateString, rate, relaxLevel, energyLevel) : + self.date = Date(dateString) + self.rate = rate + self.relaxLevel = relaxLevel + self.energyLevel = energyLevel + + # GETTER + # Mengembalikan tanggal mood dari informasi mood ini + def getDate(self) : + return self.date + + # Mengembalikan rating dari informasi mood ini + def getRate(self) : + return self.rate + + # Mengembalikan relax level dari informasi mood ini + def getRelaxLevel(self) : + return self.relaxLevel + + # Mengembalikan energy level dari informasi mood ini + def getEnergyLevel (self) : + return self.energyLevel + + # SETTER + # Mengubah informasi mood pada tanggal ini + def modifyData(self,rate=None,relaxLevel=None,energyLevel=None) : + if (rate != None) : + self.rate = rate + if (relaxLevel != None) : + self.relaxLevel = relaxLevel + if (energyLevel != None) : + self.energyLevel = energyLevel \ No newline at end of file diff --git a/src/Mood/MoodModificationController.py b/src/Mood/MoodModificationController.py new file mode 100644 index 0000000000000000000000000000000000000000..192d8db643121f34738b0e5b09b90766c7de2a58 --- /dev/null +++ b/src/Mood/MoodModificationController.py @@ -0,0 +1,90 @@ +# File : MoodModificationController.py +# Berisi kelas controller MoodModificationController, yang bertanggung jawab untuk +# mengatur keberadaan dan penyimpanan dari record mood yang berupa objek entitas MoodModification + +import csv +import os +from Utility.Date import Date +from Mood.MoodModification import MoodModification + +class MoodModificationController: + + # CONSTRUCTOR + # Menginisialisasi objek dengan membaca file csv yang sesuai dan menyimpan senarai objek entitas + def __init__(self): + self.fileName = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..", "..", "data/Mood.csv") + self.header = "" + self.data = [] + + with open(self.fileName, "r") as moodFile: + reader = csv.reader(moodFile) + self.header = next(reader) + for row in reader: + self.data.append(MoodModification(row[0], row[1], row[2], row[3])) + moodFile.close() + + # PREDICATE + # Mengembalikan true jika record telah tercatat untuk suatu tanggal tertentu, false jika tidak + def isInRecord(self, dateString): + date = Date(dateString) + for record in self.data: + if (record.getDate() == date): + return True + return False + + # ADDITIONAL + # Mengembalikan indeks yang tepat untuk meletakkan suatu record, dihitung berdasarkan tanggal record + def findNewIndexFor(self, dateString): + index = 0 + found = False + date = Date(dateString) + + while (index < len(self.data) and not found): + if (date < self.data[index].getDate()): + found = True + else: + index += 1 + + return index + + # Mengembalikan indeks dimana suatu record dengan tanggal tertentu diletakkan + def findIndexOf(self, dateString): + date = Date(dateString) + + for i in range(0, len(self.data)): + if (date == self.data[i].getDate()): + return i + + # CRUD OPERATION + # Menyimpan informasi mood pada suatu tanggal yang baru, jika tanggal sudah ada + def createRecord(self, dateString, rate, relaxLevel, energyLevel): + index = self.findNewIndexFor(dateString) + self.data.insert(index, MoodModification(dateString, rate, relaxLevel, energyLevel)) + self.writeRecords() + + # Membaca record informasi mood pada suatu tanggal tertentu + def readRecord(self, dateString): + index = self.findIndexOf(dateString) + return self.data[index].getDate(), self.data[index].getRate(), self.data[index].getRelaxLevel(), self.data[index].getEnergyLevel() + + # Mengubah record informasi mood pada suatu tanggal tertentu + def updateRecord(self, dateString, rate, relaxLevel, energyLevel): + index = self.findIndexOf(dateString) + self.data[index].modifyData(rate, relaxLevel, energyLevel) + self.writeRecords() + + # Menghapus record informasi mood pada suatu tanggal tertentu + def deleteRecord(self, dateString): + index = self.findIndexOf(dateString) + self.data.pop(index) + self.writeRecords() + + # WRITE TO CSV + # Melakukan write pada file csv data + def writeRecords(self): + with open(self.fileName, "w", newline="") as moodFile: + writer = csv.writer(moodFile) + writer.writerow(self.header) + for record in self.data: + writer.writerow([record.getDate().toString(), record.getRate(), record.getRelaxLevel(), record.getEnergyLevel()]) + moodFile.close() \ No newline at end of file diff --git a/src/Quote/QuoteModification.py b/src/Quote/QuoteModification.py new file mode 100644 index 0000000000000000000000000000000000000000..86b899af117e2246f5a7a080e20dabe545675082 --- /dev/null +++ b/src/Quote/QuoteModification.py @@ -0,0 +1,40 @@ +# File : QuoteModification.py +# Berisi kelas entitas QuoteModification, yang bertanggung jawab untuk +# Menyimpan informasi quote di suatu ID tertentu + +class QuoteModification : + + # CONSTRUCTOR + # Menginisialisasi objek QuoteModification + def __init__ (self, id, author, content): + self.id = id + self.author = author + self.content = content + + # GETTER + # Mengembalikan atribut ID dari Quote terkait + def getID (self): + return self.id + + # Mengembalikan atribut author dari Quote terkait + def getAuthor (self): + return self.author + + # Mengembalikan atribut content dari Quote terkait + def getContent (self): + return self.content + + # SETTER + # Megubah nilai atribut ID terkait + def setID (self, id): + self.id = id + + # Melakukan modifikasi terhadap data, nilai default = null + def modifyData(self, id = None, author = None, content = None) : + # Melakukan perubahan data mood hasil validasi dan pengecekan oleh controller. + if (id != None) : + self.id = id + if (author != None) : + self.author = author + if (content != None) : + self.content = content \ No newline at end of file diff --git a/src/Quote/QuoteModificationController.py b/src/Quote/QuoteModificationController.py new file mode 100644 index 0000000000000000000000000000000000000000..35d0408ba8b785e24aa5f7941d27f558b020b602 --- /dev/null +++ b/src/Quote/QuoteModificationController.py @@ -0,0 +1,94 @@ +# File : QuoteModificationController.py +# Berisi kelas controller QuoteModificationController, yang bertanggung jawab untuk +# Menyimpan seluruh data informasi quote di suatu ID tertentu + +# Impor modul eksternal +import os +import csv +from Quote.QuoteModification import QuoteModification + +class QuoteModificationController : + + # CONSTRUCTOR + # Menginisialisasi objek dengan membaca file csv yang sesuai dan menyimpan senarai objek entitas + def __init__ (self) : + self.filename = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..", "..", "data/Quote.csv") + self.header = "" + self.data = [] + + # Melakukan pembacaan terhadap file + with open(self.filename, "r") as file : + reader = csv.reader(file, quotechar = '"', delimiter = ',', quoting=csv.QUOTE_ALL, skipinitialspace = True) + self.header = next(reader) + with open(self.filename, "r") as file : + reader = csv.reader(file) + self.header = next(reader) + + # Pembuatan data controller berisi daftar Quote + for row in reader : + mod = QuoteModification(row[0], row[1], row[2]) + self.data.append(mod) + file.close() + + # PREDICATE + # Mengembalikan indeks dimana suatu record dengan ID tertentu diletakkan + def isInRecord (self, id): + for mod in self.data : + if (id == int(mod.getID())) : + return True + return False + + # Mengembalikan indeks dimana suatu record dengan ID tertentu diletakkan + def findIndexOf(self, id): + for i in range(len(self.data)): + if (id == int(self.data[i].getID())): + return i + + # Melakukan perbaharuan ID setelah dihapus + def updateId(self, id): + count = 1 + for mod in self.data : + # Salin nilai sebelum yang dihapus + if (count > id): + mod.setID(str(int(mod.getID()) - 1)) + count += 1 + + # Membuat data Quote yang baru + def createRecord (self, id, author, content) : + # Melakukan perbaharuan terhadap data ID + if self.isInRecord(id): + return self.updateRecord(id, author, content) + + # Membuat data Quote baru dan memasukkannya dalam senarai + newQuote = QuoteModification(id, author, content) + self.data.append(newQuote) + self.data.sort(key = lambda x : int(x.getID())) + self.writeRecords() + + # Membaca data dari csv + def readRecord(self, id) : + index = self.findIndexOf(id) + return self.data[index].getID(), self.data[index].getAuthor(), self.data[index].getContent() + + # Melakukan perbaharuan terhadap data Quote dengan id tertentu + def updateRecord(self, id, author, content) : + index = self.findIndexOf(id) + self.data[index].modifyData(id, author, content) + self.writeRecords() + + # Menghapus data Quote dengan id tertentu + def deleteRecord(self, id) : + index = self.findIndexOf(id) + self.data.pop(index) + self.updateId(index) + self.writeRecords() + + # WRITE TO CSV + # Melakukan write pada file csv data + def writeRecords(self): + with open(self.filename, "w", newline="") as file : + csvwriter = csv.writer(file) + csvwriter.writerow(self.header) + for mod in self.data : + csvwriter.writerow([mod.getID(), mod.getAuthor(), mod.getContent()]) + file.close() \ No newline at end of file diff --git a/src/Sleep/SleepTrackerController.py b/src/Sleep/SleepTrackerController.py new file mode 100644 index 0000000000000000000000000000000000000000..c18df683c52cdd83c6db09399af0739c7d1ebda9 --- /dev/null +++ b/src/Sleep/SleepTrackerController.py @@ -0,0 +1,90 @@ +# File : SleepTrackerController.py +# Berisi kelas controller SleepTrackerController, yang bertanggung jawab untuk +# Mengatur keberadaan dan penyimpanan dari record sleep yang berupa objek entitas SleepTrackerModification + +import os +import csv +from Utility.Date import Date +from Sleep.SleepTrackerModification import SleepTrackerModification + +class SleepTrackerController: + + # CONSTRUCTOR + # Menginisialisasi objek dengan membaca file csv yang sesuai dan menyimpan senarai objek entitas + def __init__(self): + self.fileName = os.path.join(os.path.dirname(os.path.realpath(__file__)), "..", "..", "data/Sleep.csv") + self.header = "" + self.data = [] + + with open(self.fileName, "r") as sleepFile: + reader = csv.reader(sleepFile) + self.header = next(reader) + for row in reader: + self.data.append(SleepTrackerModification(row[0], row[1], row[2], row[3])) + sleepFile.close() + + # PREDICATE + # Mengembalikan true jika record telah tercatat untuk suatu tanggal tertentu, false jika tidak + def isInRecord(self, dateString): + date = Date(dateString) + for record in self.data: + if (record.getDate() == date): + return True + return False + + # ADDITIONAL + # Mengembalikan indeks yang tepat untuk meletakkan suatu record, dihitung berdasarkan tanggal record + def findNewIndexFor(self, dateString): + index = 0 + found = False + date = Date(dateString) + + while (index < len(self.data) and not found): + if (date < self.data[index].getDate()): + found = True + else: + index += 1 + + return index + + # Mengembalikan indeks dimana suatu record dengan tanggal tertentu diletakkan + def findIndexOf(self, dateString): + date = Date(dateString) + + for i in range(0, len(self.data)): + if (date == self.data[i].getDate()): + return i + + # CRUD OPERATION + # Menyimpan informasi tidur pada suatu tanggal yang baru, jika tanggal sudah ada + def createRecord(self, dateString, startTime, finishTime, rating): + index = self.findNewIndexFor(dateString) + self.data.insert(index, SleepTrackerModification(dateString, startTime, finishTime, rating)) + self.writeRecords() + + # Membaca record informasi tidur pada suatu tanggal tertentu + def readRecord(self, dateString): + index = self.findIndexOf(dateString) + return self.data[index].getDate(), self.data[index].getStartTime(), self.data[index].getFinishTime(), self.data[index].getRating() + + # Mengubah record informasi tidur pada suatu tanggal tertentu + def updateRecord(self, dateString, startTime, finishTime, rating): + index = self.findIndexOf(dateString) + self.data[index].modifyData(startTime, finishTime, rating) + self.writeRecords() + + # Menghapus record informasi tidur pada suatu tanggal tertentu + def deleteRecord(self, dateString): + index = self.findIndexOf(dateString) + self.data.pop(index) + self.writeRecords() + + # WRITE TO CSV + # Melakukan write pada file csv data + def writeRecords(self): + with open(self.fileName, "w", newline="") as sleepFile: + writer = csv.writer(sleepFile) + writer.writerow(self.header) + for record in self.data: + writer.writerow([record.getDate().toString(), record.getStartTime(), record.getFinishTime(), record.getRating()]) + sleepFile.close() \ No newline at end of file diff --git a/src/Sleep/SleepTrackerModification.py b/src/Sleep/SleepTrackerModification.py new file mode 100644 index 0000000000000000000000000000000000000000..0a2ef78b62ad7c0e08108f62b07f417222201b94 --- /dev/null +++ b/src/Sleep/SleepTrackerModification.py @@ -0,0 +1,42 @@ +# File : SleepTrackerModification.py +# Berisi kelas entitas SleepTrackerModification, yang bertanggung jawab untuk +# Menyimpan informasi tidur di suatu tanggal tertentu + +import sys +sys.path.append("..") + +from Utility.Date import Date + +class SleepTrackerModification: + + # CONSTRUCTOR + # Menginisialisasi objek SleepTrackerModification + def __init__(self, dateString, startTime, finishTime, rating): + self.date = Date(dateString) + self.startTime = startTime + self.finishTime = finishTime + self.rating = rating + + # GETTER + # Mengembalikan tanggal tidur dari informasi tidur ini + def getDate(self): + return self.date + + # Mengembalikan waktu mulai tidur dari informasi tidur ini + def getStartTime(self): + return self.startTime + + # Mengembalikan waktu bangun tidur dari informasi tidur ini + def getFinishTime(self): + return self.finishTime + + # Mengembalikan rating tidur dari informasi tidur ini + def getRating(self): + return self.rating + + # SETTER + # Mengubah informasi tidur pada tanggal ini + def modifyData(self, startTime, finishTime, rating): + self.startTime = startTime + self.finishTime = finishTime + self.rating = rating \ No newline at end of file diff --git a/src/Utility/Date.py b/src/Utility/Date.py new file mode 100644 index 0000000000000000000000000000000000000000..fdb0d6ea53bbbde63bb8e1b742e2aff6781d34cb --- /dev/null +++ b/src/Utility/Date.py @@ -0,0 +1,58 @@ +# File : Date.py +# Berisi kelas entitas Date, yang bertanggung jawab untuk +# Menyimpan informasi tanggal + +class Date: + + # CONSTRUCTOR + # Mengubah string berisi tanggal menjadi atribut yang sesuai + # Format : DD-MM-YYYY + def __init__(self, dateString): + self.day = int(dateString[0:2]) + self.month = int(dateString[3:5]) + self.year = int(dateString[6:]) + + # GETTER + # Mengembalikan atribut day + def getDay(self): + return self.day + + # Mengembalikan atribut month + def getMonth(self): + return self.month + + # Mengembalikan atribut year + def getYear(self): + return self.year + + # COMPARATOR + # Mengembalikan true jika tanggal ini datang setelah other, false jika tidak + def __gt__(self, other): + return ((self.year > other.year) or + (self.year == other.year and self.month > other.month) or + (self.year == other.year and self.month == other.month and self.day > other.day)) + + # Mengembalikan true jika tanggal ini sama dengan other, false jika tidak + def __eq__(self, other): + return (self.year == other.year and self.month == other.month and self.day == other.day) + + # Mengembalikan true jika tanggal ini datang sebelum other, false jika tidak + def __lt__(self, other): + return (not self > other and not self == other) + + # TRANSFORMER + # Mengubah objek menjadi string berisi tanggal + def toString(self): + result = "" + + if (self.day < 10): + result += f"0{self.day}-" + else: + result += f"{self.day}-" + if (self.month < 10): + result += f"0{self.month}-" + else: + result += f"{self.month}-" + result += f"{self.year}" + + return result \ No newline at end of file diff --git a/src/Utility/Statistics.py b/src/Utility/Statistics.py new file mode 100644 index 0000000000000000000000000000000000000000..e16e5988a7a7b2a8f746914e112d097740b9a2a5 --- /dev/null +++ b/src/Utility/Statistics.py @@ -0,0 +1,130 @@ +# File : Statistics.py +# Berisi kelas entitas Statistik, yang bertanggung jawab untuk +# Menampilkan statistik bagi data yang membuktuhkan + +import matplotlib.pyplot as plt +import pandas as pd +from Utility.Time import Time + +class Statistics: + + # CONSTRUCTOR + # Konstruktor default + # Masukan tipe : Mood / Sleep + def __init__ (self, filename, tipe): + self.filename = filename + self.tipe = tipe + df = pd.read_csv(self.filename, delimiter=',', header='infer') + self.df = df.tail(7) # Mengambil 7 data terakhir + self.df.set_index('tanggal', inplace=True) # Menjadikan tanggal sebagai indeks + self.data = self.df.to_numpy() + + # GETTER + # Mengembalikan atribut tipe + def getTipe (self): + return self.tipe + + # PEMBUATAN STATISTIK + # Membuat statistik pada 7 data terakhir periode tertentu + def generateStatistics (self): + # Inisiasi grafik + fig, ax = plt.subplots(figsize=(12, 6)) + + # Pemrosesan berbasis tipe + if (self.tipe == "Mood"): + # Membuat grafik + ax.plot(self.df.index, self.df.rate, label='Rate') + ax.plot(self.df.index, self.df.relax_level, label='Relax level') + ax.plot(self.df.index, self.df.energy_level, label='Energy level') + + # Menambahkan label + for j, label in enumerate(self.df.rate): + ax.annotate(label, (self.df.index[j], self.df.rate[j])) + for j, label in enumerate(self.df.relax_level): + ax.annotate(label, (self.df.index[j], self.df.relax_level[j])) + for j, label in enumerate(self.df.energy_level): + ax.annotate(label, (self.df.index[j], self.df.energy_level[j])) + + # Melakukan pemrosesan gambar dan simpan + plt.legend(self.df.columns) + + elif (self.tipe == "Sleep"): + # Membuat senarai waktu + selisih = [] + labels = [] + for j in range (len(self.data)): + finish = Time(self.data[j][1]) + start = Time(self.data[j][0]) + selisih.append((finish - start).toMinutes() / 70) + labels.append((finish - start).toString()) + + # Membuat grafik + ax.plot(self.df.index, selisih, label='Selisih') + ax.plot(self.df.index, self.df.rating, label='Rating') + + # Menambahkan label + for j, label in enumerate(labels): + ax.annotate(label, (self.df.index[j], selisih[j]), fontsize=8) + for j, label in enumerate(self.df.rating): + ax.annotate(label, (self.df.index[j], self.df.rating[j])) + + # Melakukan pemrosesan gambar dan simpan + plt.legend(["selisih", "rating"]) + + # Menyimpan gambar untuk ditapilkan + plt.xticks(rotation = 45, ha = "right", rotation_mode = "anchor") # Merotasi nilai tanggal 45 deg + plt.subplots_adjust(bottom = 0.25, top = 0.9) # Memastikan nilai tanggal tidak terpotong + plt.ylabel('Values') + plt.xlabel('Dates') + plt.savefig('../../images/result.png') + + # MEMBERIKAN INSIGHTS + # Memberikan nilai hasil pengelolaan data kepada pengguna + def showInsights (self): + # Pemrosesan berdasarkan tipe + if (self.tipe == "Mood"): + # Inisiasi proses perhitungan + count3 = 0 + count4 = 0 + + # Melakukan perhitungan data mood + for i in range (len(self.data)) : + for j in range (3): + if (self.data[i][j] == 3): + count3 += 1 + elif (self.data[i][j] == 4): + count4 += 1 + + # Pemorsesan nilai + if (len(self.data) >= 7): + if (count3 >= 2 and count4 >= 2): + return "Mood kamu 7 hari terkahir sangat bagus! Pertahankan" + else : + return "Mood kamu 7 hari terakhir kurang begitu baik :( Semangat yaa!" + else : + return "Tidak terdapat data Mood yang cukup untuk dianalisis (kurang dari 7 hari)" + + elif (self.tipe == "Sleep"): + # Membuat senarai waktu + selisih = [] + for j in range (len(self.data)): + finish = Time(self.data[j][1]) + start = Time(self.data[j][0]) + selisih.append((finish - start).toMinutes()) + + # Inisiasi proses perhitungan + count = 0 + + # Melakukan perhitungan data mood + for i in range (len(selisih)) : + if (selisih[i] >= 8 * 60): + count += 1 + + # Pemorsesan nilai + if (len(self.data) >= 7): + if (count >= 4): + return "Waktu tidurmu sangat cukup 7 hari terakhir. Pertahankan ya!" + else : + return "Waktu tidurmu 7 hari terakhir sangat kurang :( Istirahat yaa!" + else : + return "Tidak terdapat data waktu tidur yang cukup untuk dianalisis (kurang dari 7 hari)" diff --git a/src/Utility/Time.py b/src/Utility/Time.py new file mode 100644 index 0000000000000000000000000000000000000000..ad14481d6afbb01f143f54691914c2c5dd5b3180 --- /dev/null +++ b/src/Utility/Time.py @@ -0,0 +1,52 @@ +# File : Time.py +# Berisi kelas entitas Time, yang bertanggung jawab untuk +# Menyimpan informasi terkait suatu waktu (jam, menit, detik) + +class Time: + + # CONSTRUCTOR + # Mengubah string berisi waktu menjadi atribut yang sesuai + # Format : HH:MM + def __init__(self, timeString): + self.hour = int(timeString[0:2]) + self.minute = int(timeString[3:]) + + # TRANSFORMER + # Mengembalikan jumlah menit dari waktu 00.00 hingga waktu sekarang + def toMinutes(self): + return self.hour * 60 + self.minute + + # Mengembalikan suatu string dari atribut objek time + # Format : H jam M menit + def toString(self): + result = "" + + if (self.hour > 0): + result += f"{self.hour} jam " + if (self.minute > 0): + result += f"{self.minute} menit" + + return result + + # OPERATOR + # Melakukan operasi pengurangan antara 2 objek time, mengembalikan objek time baru hasil pengurangan + def __sub__(self, other): + totalMinutes = self.toMinutes() - other.toMinutes() + if (totalMinutes < 0): + totalMinutes += 1440 + + timeString = "" + hour = totalMinutes // 60 + minute = totalMinutes % 60 + + if (hour > 9): + timeString += f"{hour}:" + else: + timeString += f"0{hour}:" + + if (self.hour > 9): + timeString += f"{minute}" + else: + timeString += f"0{minute}" + + return Time(timeString) \ No newline at end of file