From 7e5c55b6e33651b30b2525d80b0bf8c326c550f1 Mon Sep 17 00:00:00 2001 From: JSdev <80474621+N-coder82@users.noreply.github.com> Date: Fri, 22 Dec 2023 16:27:15 -0800 Subject: [PATCH] v1.0.0-ALPHA (#4) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * changed reminders dialog by adding input from the UI * Finished v1 ALPHA of the app!!! 🎉 🎉 🎉! Bugs: - everything is printed 3 times --------- Co-authored-by: N-Coder82 --- UI Files/Mainwindow.ui | 20 +++--- UI Files/remindersdialog.ui | 36 +++++------ remindersdialog.py | 8 +++ source/TODO.md | 1 + source/app.py | 119 ++++++++++++++++++++++++++++++++++++ source/controller.py | 81 +++++++++++++----------- source/mainapp.py | 38 ------------ source/reminders.chi | 7 ++- source/reminders.py | 24 -------- source/remindersdialog.py | 88 +++++++++++++------------- source/ui.py | 6 +- 11 files changed, 255 insertions(+), 173 deletions(-) create mode 100644 remindersdialog.py create mode 100644 source/app.py delete mode 100644 source/mainapp.py delete mode 100644 source/reminders.py diff --git a/UI Files/Mainwindow.ui b/UI Files/Mainwindow.ui index 900e34b..7ff4e74 100644 --- a/UI Files/Mainwindow.ui +++ b/UI Files/Mainwindow.ui @@ -19,16 +19,6 @@ ../../../../../../Downloads/logo-09e4a95d.svg../../../../../../Downloads/logo-09e4a95d.svg - - - - 10 - 10 - 521 - 381 - - - @@ -217,6 +207,16 @@ p, li { white-space: pre-wrap; } Create Reminder + + + + 10 + 10 + 521 + 381 + + + diff --git a/UI Files/remindersdialog.ui b/UI Files/remindersdialog.ui index 9c2be1e..88df524 100644 --- a/UI Files/remindersdialog.ui +++ b/UI Files/remindersdialog.ui @@ -1,7 +1,7 @@ - MainWindow - + RemindersDialog + 0 @@ -32,7 +32,7 @@ 840 - 130 + 120 161 131 @@ -184,7 +184,7 @@ 830 260 201 - 141 + 121 @@ -194,7 +194,7 @@ 10 - 80 + 70 71 16 @@ -203,38 +203,38 @@ Description: - + 10 - 110 - 181 - 31 + 20 + 49 + 16 + + Title: + - + 10 40 181 - 31 + 21 - + 10 - 20 - 49 - 16 + 90 + 181 + 21 - - Title: - diff --git a/remindersdialog.py b/remindersdialog.py new file mode 100644 index 0000000..f32f4f2 --- /dev/null +++ b/remindersdialog.py @@ -0,0 +1,8 @@ +# Form implementation generated from reading ui file 'remindersdialog.ui' +# +# Created by: PyQt6 UI code generator 6.6.1 +# +# WARNING: Any manual changes made to this file will be lost when pyuic6 is +# run again. Do not edit this file unless you know what you are doing. + + diff --git a/source/TODO.md b/source/TODO.md index bfb2634..a986214 100644 --- a/source/TODO.md +++ b/source/TODO.md @@ -1,2 +1,3 @@ +Current TO-DOs - figure out why its printing output 3 times in reminders.py - reconfigure dropdown to match dropdown index \ No newline at end of file diff --git a/source/app.py b/source/app.py new file mode 100644 index 0000000..780701f --- /dev/null +++ b/source/app.py @@ -0,0 +1,119 @@ +from PyQt6 import QtWidgets, QtCore +import sys +from ui import Ui_Chi +from remindersdialog import Ui_RemindersDialog +import controller + + +def print_dict(dct): + return_string = "" + for name, item in dct.items(): + return_string = return_string + f"{str(name)}: {str(item)}
" + return return_string + + +class RemindersPopup(QtWidgets.QMainWindow, Ui_RemindersDialog): + def __init__(self): + super().__init__() + self.setupUi(self) + self.create_button.clicked.connect(self.on_create_button_clicked) + + def on_create_button_clicked(self): + # Get all data from all inputs and process into a single write request in controller + date_selected = self.date_box.text() + time_selected = self.time_box.text() + raw_time = time_selected.split(" ")[0] + h, m = raw_time.split(":") + time_selected = h + "." + m + priority_selected = self.priority_dropdown.currentText() + place_selected = self.place_input_box.text() + title_selected = self.title_input_box.text() + desc_selected = self.desc_input_box.text() + day_repeat_selected = self.day_repeat_input_box.text() + year_repeat_selected = self.year_repeat_input_box.text() + repeat_bool = self.repeat_checkbox.isChecked() + flagged_bool = self.flagged_checkbox.isChecked() + summary_dict = { + "date": date_selected, + "time": time_selected, + "priority index": priority_selected, + "place": place_selected, + "title": title_selected, + "desc": desc_selected, + "day repeating?": repeat_bool, + "day repeat": day_repeat_selected, + "year repeat": year_repeat_selected, + "flagged?": flagged_bool, + } + self.summary_display_box.setHtml(f"

{print_dict(summary_dict)}

") + controller.write( + "reminders.chi", + title_selected, + desc_selected, + date_selected, + time_selected, + repeat_bool, + day_repeat_selected, + year_repeat_selected, + place_selected, + priority_selected, + flagged_bool, + ) + + +class MainWindow(QtWidgets.QMainWindow, Ui_Chi): + def __init__(self): + super().__init__() + self.setupUi(self) + self.reminderspopup = None + self.gpt_send_button.clicked.connect(self.on_gpt_send_clicked) + self.create_reminder_button.clicked.connect( + self.on_create_reminder_button_clicked + ) + city, temp, condition = controller.weather_data("10001") + self.weather_text_display.setHtml( + f"

City: {city}
Temp: {temp}
Conditions: {condition}

" + ) + currentreminders = "" + linecount = 0 + with open("reminders.chi", "r") as file: + data = file.readlines() + for line in data: + linecount += 1 + linecount = linecount - 2 + i = 0 + for i in range(linecount): + current_reminders_dict = controller.read("reminders.chi", str(i + 1)) + currentreminders += print_dict(current_reminders_dict) + currentreminders += "
=======================================
" + + self.reminders_display.setHtml(currentreminders) + self.timer = QtCore.QTimer(self) + self.timer.timeout.connect(self.update_time) + self.timer.start(1000) + self.show() + + def update_time(self): + current_time = QtCore.QTime.currentTime() + minutes = current_time.toString("mm") + hours = current_time.toString("hh") + self.hours_lcd.setProperty("value", hours) + self.minutes_lcd.setProperty("value", minutes) + + def on_gpt_send_clicked(self): + # Add your code here to respond to the button click + chatgptQues = self.gpt_input_box.text() + GPTanswer = controller.chatbot(chatgptQues) + self.gpt_text_display.setHtml(f"

{GPTanswer}

") + + def on_create_reminder_button_clicked(self): + if self.reminderspopup is None: + self.reminderspopup = RemindersPopup() + self.reminderspopup.show() + + +if __name__ == "__main__": + app = QtWidgets.QApplication(sys.argv) + window = MainWindow() + window.show() + sys.exit(app.exec()) diff --git a/source/controller.py b/source/controller.py index b9ab9a4..6096ec2 100644 --- a/source/controller.py +++ b/source/controller.py @@ -8,10 +8,13 @@ from datetime import datetime import argparse import sys + # TODO: add env vaiable support for apikey weatherapikey = "" zipcode = "10001" now = datetime.now() + + def chatbot(input): messages = [ { @@ -19,8 +22,11 @@ def chatbot(input): "content": "You are Chi, a large language model trained by OpenAI and currently used by JSdev.", }, {"role": "system", "content": "Knowledge cutoff: 2021-09"}, - {"role": "system", "content": f"Current date and time is {now.strftime('%m/%d/%Y %H:%M')}"}, - {"role": "system", "content": f"Current weather is: {weather_data(zipcode)}"} + { + "role": "system", + "content": f"Current date and time is {now.strftime('%m/%d/%Y %H:%M')}", + }, + {"role": "system", "content": f"Current weather is: {weather_data(zipcode)}"}, ] if input: messages.append({"role": "user", "content": input}) @@ -29,6 +35,7 @@ def chatbot(input): messages.append({"role": "assistant", "content": reply}) return reply + def weather_data(zipcode): response = requests.get( f"https://api.weatherapi.com/v1/current.json?key={weatherapikey}&q={zipcode}&aqi=no" @@ -40,7 +47,11 @@ def weather_data(zipcode): temp = weatherdict["current"]["temp_c"] condition = weatherdict["current"]["condition"]["text"] return [city, temp, condition] + + print(chatbot("hello")) + + def metadata_read(filename): key = "username" usernamevalue = "" @@ -64,6 +75,7 @@ def metadata_read(filename): return None return [usernamevalue, uidvalue] + def metadata_add(filename, username, uid): with open(filename, "r") as file: content = file.read() @@ -71,6 +83,7 @@ def metadata_add(filename, username, uid): with open(filename, "w") as file: file.write(f"username:{username}\nuid:{uid}\n" + content) + def getkeytowrite(filename): try: with open(filename, "r") as file: @@ -84,14 +97,25 @@ def getkeytowrite(filename): except FileNotFoundError: raise FileNotFoundError("File doesn't exist.") + def write( - filename, title, desc, datetodone, timetodone, repeat, place, priority, flagged + filename, + title, + desc, + datetodone, + timetodone, + repeatbool, + repeat_day, + repeat_year, + place, + priority, + flagged, ): keytowrite = getkeytowrite(filename) print("key is", keytowrite) try: with open(filename, "a") as file: - value = f"{title},{desc},{datetodone},{timetodone},{repeat},{place},{priority},{flagged}" + value = f"{title},{desc},{datetodone},{timetodone},{repeatbool},{repeat_day},{repeat_year},{place},{priority},{flagged}" file.write(f"{keytowrite}:{value}\n") except PermissionError: raise PermissionError("No permission to edit file.") @@ -113,39 +137,27 @@ def read(filename, key): data = file.readlines() for line in data: if str(line.split(":")[0]) == key: - rawdata = line.strip().split(":")[1] - ( - title, - desc, - datetodone, - timetodone, - repeat, - place, - priority, - flagged, - ) = rawdata.split(",") - if repeat == "true" or "True": - repeat = True - elif repeat == "false" or "False": - repeat = False - if flagged == "true" or "True": - flagged = True - elif flagged == "false" or "False": - flagged = False - return [ - title, - desc, - datetodone, - timetodone, - repeat, - place, - priority, - flagged, - ] + rawdata = line.strip().split(":")[1] + title,desc,datetodone,timetodone,repeatbool,repeat_day,repeat_year,place,priority,flagged = rawdata.split(",") + h,m = timetodone.split(".") + timetodone = h+":"+m + return { + "Title":title, + "Desc":desc, + "Date":datetodone, + "Time":timetodone, + "Repeating":repeatbool, + "Repeating / Year":repeat_year, + "Repeating / Day": repeat_day, + "Place":place, + "Priority":priority, + "Flaagged":flagged, + } except FileNotFoundError: raise FileNotFoundError("File doesn't exist.") + def delete(filename, key): # key NEEDS TO BE A STRING try: @@ -159,6 +171,7 @@ def delete(filename, key): except FileNotFoundError: raise Exception(f'File named "{filename}" not found') + def edit(filename, old_key, new_value): try: with open(filename, "r") as file: @@ -177,4 +190,4 @@ def edit(filename, old_key, new_value): except FileNotFoundError: raise Exception(f'File named "{filename}" not found') - +print(read("reminders.chi","2")) \ No newline at end of file diff --git a/source/mainapp.py b/source/mainapp.py deleted file mode 100644 index fac37d6..0000000 --- a/source/mainapp.py +++ /dev/null @@ -1,38 +0,0 @@ -from PyQt6 import QtWidgets, QtCore -import sys -from ui import Ui_Chi -import controller -class MainWindow(QtWidgets.QMainWindow, Ui_Chi): - def __init__(self): - super().__init__() - self.setupUi(self) - self.gpt_send_button.clicked.connect(self.on_pushButton_clicked) - # self.pushButton2.clicked.connect(self.on_pushButton2_clicked) - city, temp, condition = controller.weather_data("10001") - self.weather_text_display.setHtml(f"

City: {city}
Temp: {temp}
Conditions: {condition}

") - self.timer = QtCore.QTimer(self) - self.timer.timeout.connect(self.update_time) - self.timer.start(1000) - self.show() - def update_time(self): - current_time = QtCore.QTime.currentTime() - minutes = current_time.toString('mm') - hours = current_time.toString('hh') - self.hours_lcd.setProperty("value", hours) - self.minutes_lcd.setProperty("value", minutes) - def on_pushButton_clicked(self): - # Add your code here to respond to the button click - chatgptQues = self.gpt_input_box.text() - GPTanswer = controller.chatbot(chatgptQues) - self.gpt_text_display.setHtml(f"

{GPTanswer}

") - # def on_pushButton2_clicked(self): - # # Add your code here to respond to the button click - # pass - - - -if __name__ == '__main__': - app = QtWidgets.QApplication(sys.argv) - window = MainWindow() - window.show() - sys.exit(app.exec()) \ No newline at end of file diff --git a/source/reminders.chi b/source/reminders.chi index 9886459..a10ed81 100644 --- a/source/reminders.chi +++ b/source/reminders.chi @@ -1,4 +1,7 @@ username:jsdev uid:0000000001 -1:Feed Muffin,Give her food but not chicken,null,null,byd1y365,Home,4,True -2:Code Chi App,Finish Main Function,7/22/2023,12.00p,bnd0y0,Home,null,False \ No newline at end of file +1:wefd,23rsaf,1/1/00,12.00 AM,True,1,365,saedg,2,True +2:sdfv,wsdfv,1/1/00,12.00 AM,True,1,365,sfaer,4 - Highest,True +3:aer,xcvdr,1/1/00,12.00 AM,True,1,365,iubsronv;a,4 - Highest,True +4:aer,xcvdr,1/1/00,12.00 AM,True,1,365,iubsronv;a,4 - Highest,True +5:aer,xcvdr,1/1/00,12.00 AM,True,1,365,iubsronv;a,4 - Highest,True diff --git a/source/reminders.py b/source/reminders.py deleted file mode 100644 index 9a19bbf..0000000 --- a/source/reminders.py +++ /dev/null @@ -1,24 +0,0 @@ -from PyQt6 import QtWidgets, QtCore -import sys -from remindersdialog import Ui_MainWindow -import controller -class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow): - def __init__(self): - super().__init__() - self.setupUi(self) - self.create_button.clicked.connect(self.on_create_button_clicked) - def on_create_button_clicked(self): - # Get all data from all inputs and process into a single write request in controller - date_selected = self.date_box.date() - time_selected = self.time_box.time() - priority_selected = self.priority_dropdown.currentIndex() - print(f"Date: {date_selected} Time: {time_selected}") - print(priority_selected) - - - -if __name__ == '__main__': - app = QtWidgets.QApplication(sys.argv) - window = MainWindow() - window.show() - sys.exit(app.exec()) \ No newline at end of file diff --git a/source/remindersdialog.py b/source/remindersdialog.py index c83fb16..2785c16 100644 --- a/source/remindersdialog.py +++ b/source/remindersdialog.py @@ -9,20 +9,20 @@ from PyQt6 import QtCore, QtGui, QtWidgets -class Ui_MainWindow(object): - def setupUi(self, MainWindow): - MainWindow.setObjectName("MainWindow") - MainWindow.resize(1044, 460) +class Ui_RemindersDialog(object): + def setupUi(self, RemindersDialog): + RemindersDialog.setObjectName("RemindersDialog") + RemindersDialog.resize(1044, 460) icon = QtGui.QIcon() icon.addPixmap(QtGui.QPixmap("../../../../../../Downloads/logo-09e4a95d.svg"), QtGui.QIcon.Mode.Normal, QtGui.QIcon.State.Off) - MainWindow.setWindowIcon(icon) - self.centralwidget = QtWidgets.QWidget(parent=MainWindow) + RemindersDialog.setWindowIcon(icon) + self.centralwidget = QtWidgets.QWidget(parent=RemindersDialog) self.centralwidget.setObjectName("centralwidget") self.summary_display_box = QtWidgets.QTextBrowser(parent=self.centralwidget) self.summary_display_box.setGeometry(QtCore.QRect(20, 270, 451, 181)) self.summary_display_box.setObjectName("summary_display_box") self.repeat_box = QtWidgets.QGroupBox(parent=self.centralwidget) - self.repeat_box.setGeometry(QtCore.QRect(840, 130, 161, 131)) + self.repeat_box.setGeometry(QtCore.QRect(840, 120, 161, 131)) self.repeat_box.setObjectName("repeat_box") self.repeat_checkbox = QtWidgets.QCheckBox(parent=self.repeat_box) self.repeat_checkbox.setGeometry(QtCore.QRect(50, 30, 74, 20)) @@ -62,20 +62,20 @@ def setupUi(self, MainWindow): self.create_button.setFont(font) self.create_button.setObjectName("create_button") self.info_box = QtWidgets.QGroupBox(parent=self.centralwidget) - self.info_box.setGeometry(QtCore.QRect(830, 260, 201, 141)) + self.info_box.setGeometry(QtCore.QRect(830, 260, 201, 121)) self.info_box.setObjectName("info_box") self.desc_label = QtWidgets.QLabel(parent=self.info_box) - self.desc_label.setGeometry(QtCore.QRect(10, 80, 71, 16)) + self.desc_label.setGeometry(QtCore.QRect(10, 70, 71, 16)) self.desc_label.setObjectName("desc_label") - self.desc_input_box = QtWidgets.QTextEdit(parent=self.info_box) - self.desc_input_box.setGeometry(QtCore.QRect(10, 110, 181, 31)) - self.desc_input_box.setObjectName("desc_input_box") - self.title_input_box = QtWidgets.QTextEdit(parent=self.info_box) - self.title_input_box.setGeometry(QtCore.QRect(10, 40, 181, 31)) - self.title_input_box.setObjectName("title_input_box") self.title_label = QtWidgets.QLabel(parent=self.info_box) self.title_label.setGeometry(QtCore.QRect(10, 20, 49, 16)) self.title_label.setObjectName("title_label") + self.title_input_box = QtWidgets.QLineEdit(parent=self.info_box) + self.title_input_box.setGeometry(QtCore.QRect(10, 40, 181, 21)) + self.title_input_box.setObjectName("title_input_box") + self.desc_input_box = QtWidgets.QLineEdit(parent=self.info_box) + self.desc_input_box.setGeometry(QtCore.QRect(10, 90, 181, 21)) + self.desc_input_box.setObjectName("desc_input_box") self.summary_label = QtWidgets.QLabel(parent=self.centralwidget) self.summary_label.setGeometry(QtCore.QRect(20, 240, 61, 16)) self.summary_label.setObjectName("summary_label") @@ -119,41 +119,41 @@ def setupUi(self, MainWindow): self.calendar = QtWidgets.QCalendarWidget(parent=self.centralwidget) self.calendar.setGeometry(QtCore.QRect(410, 10, 381, 201)) self.calendar.setObjectName("calendar") - MainWindow.setCentralWidget(self.centralwidget) + RemindersDialog.setCentralWidget(self.centralwidget) - self.retranslateUi(MainWindow) - QtCore.QMetaObject.connectSlotsByName(MainWindow) + self.retranslateUi(RemindersDialog) + QtCore.QMetaObject.connectSlotsByName(RemindersDialog) - def retranslateUi(self, MainWindow): + def retranslateUi(self, RemindersDialog): _translate = QtCore.QCoreApplication.translate - MainWindow.setWindowTitle(_translate("MainWindow", "Chi - Reminders Dialog")) - self.repeat_box.setTitle(_translate("MainWindow", "Repeat")) - self.repeat_checkbox.setText(_translate("MainWindow", "Repeat?")) - self.day_repeat_label.setText(_translate("MainWindow", "Times per day:")) - self.year_repeat_label.setText(_translate("MainWindow", "Times per year:")) - self.scdeuling_box.setTitle(_translate("MainWindow", "Scheduling")) - self.time_label.setText(_translate("MainWindow", "Time:")) - self.date_label.setText(_translate("MainWindow", "Date:")) - self.create_button.setText(_translate("MainWindow", "Create")) - self.info_box.setTitle(_translate("MainWindow", "Text")) - self.desc_label.setText(_translate("MainWindow", "Description:")) - self.title_label.setText(_translate("MainWindow", "Title:")) - self.summary_label.setText(_translate("MainWindow", "Summary:")) - self.other_box.setTitle(_translate("MainWindow", "Other")) - self.priority_label.setText(_translate("MainWindow", "Priority:")) - self.flagged_checkbox.setText(_translate("MainWindow", "Flagged?")) - self.priority_dropdown.setItemText(0, _translate("MainWindow", "4 - Highest")) - self.priority_dropdown.setItemText(1, _translate("MainWindow", "3 - High")) - self.priority_dropdown.setItemText(2, _translate("MainWindow", "2 - Normal")) - self.priority_dropdown.setItemText(3, _translate("MainWindow", "1 - Low")) - self.place_label.setText(_translate("MainWindow", "Place:")) + RemindersDialog.setWindowTitle(_translate("RemindersDialog", "Chi - Reminders Dialog")) + self.repeat_box.setTitle(_translate("RemindersDialog", "Repeat")) + self.repeat_checkbox.setText(_translate("RemindersDialog", "Repeat?")) + self.day_repeat_label.setText(_translate("RemindersDialog", "Times per day:")) + self.year_repeat_label.setText(_translate("RemindersDialog", "Times per year:")) + self.scdeuling_box.setTitle(_translate("RemindersDialog", "Scheduling")) + self.time_label.setText(_translate("RemindersDialog", "Time:")) + self.date_label.setText(_translate("RemindersDialog", "Date:")) + self.create_button.setText(_translate("RemindersDialog", "Create")) + self.info_box.setTitle(_translate("RemindersDialog", "Text")) + self.desc_label.setText(_translate("RemindersDialog", "Description:")) + self.title_label.setText(_translate("RemindersDialog", "Title:")) + self.summary_label.setText(_translate("RemindersDialog", "Summary:")) + self.other_box.setTitle(_translate("RemindersDialog", "Other")) + self.priority_label.setText(_translate("RemindersDialog", "Priority:")) + self.flagged_checkbox.setText(_translate("RemindersDialog", "Flagged?")) + self.priority_dropdown.setItemText(0, _translate("RemindersDialog", "4 - Highest")) + self.priority_dropdown.setItemText(1, _translate("RemindersDialog", "3 - High")) + self.priority_dropdown.setItemText(2, _translate("RemindersDialog", "2 - Normal")) + self.priority_dropdown.setItemText(3, _translate("RemindersDialog", "1 - Low")) + self.place_label.setText(_translate("RemindersDialog", "Place:")) if __name__ == "__main__": import sys app = QtWidgets.QApplication(sys.argv) - MainWindow = QtWidgets.QMainWindow() - ui = Ui_MainWindow() - ui.setupUi(MainWindow) - MainWindow.show() + RemindersDialog = QtWidgets.QMainWindow() + ui = Ui_RemindersDialog() + ui.setupUi(RemindersDialog) + RemindersDialog.show() sys.exit(app.exec()) diff --git a/source/ui.py b/source/ui.py index 68d2b9d..76b2652 100644 --- a/source/ui.py +++ b/source/ui.py @@ -18,9 +18,6 @@ def setupUi(self, Chi): Chi.setWindowIcon(icon) self.centralwidget = QtWidgets.QWidget(parent=Chi) self.centralwidget.setObjectName("centralwidget") - self.reminders_display = QtWidgets.QColumnView(parent=self.centralwidget) - self.reminders_display.setGeometry(QtCore.QRect(10, 10, 521, 381)) - self.reminders_display.setObjectName("reminders_display") self.gpt_input = QtWidgets.QGroupBox(parent=self.centralwidget) self.gpt_input.setGeometry(QtCore.QRect(620, 400, 371, 41)) self.gpt_input.setTitle("") @@ -73,6 +70,9 @@ def setupUi(self, Chi): self.create_reminder_button = QtWidgets.QPushButton(parent=self.centralwidget) self.create_reminder_button.setGeometry(QtCore.QRect(180, 400, 141, 41)) self.create_reminder_button.setObjectName("create_reminder_button") + self.reminders_display = QtWidgets.QTextBrowser(parent=self.centralwidget) + self.reminders_display.setGeometry(QtCore.QRect(10, 10, 521, 381)) + self.reminders_display.setObjectName("reminders_display") Chi.setCentralWidget(self.centralwidget) self.retranslateUi(Chi)