update,
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
[[source]]
|
||||
url = "https://pypi.org/simple"
|
||||
verify_ssl = true
|
||||
name = "pypi"
|
||||
|
||||
[packages]
|
||||
mysql-connector-python = "*"
|
||||
mysql = "*"
|
||||
|
||||
[dev-packages]
|
||||
|
||||
[requires]
|
||||
python_version = "3.10"
|
101
ITP4459_assignment_2023/notes/Assignment2223_student_v2_original/Pipfile.lock
generated
Normal file
101
ITP4459_assignment_2023/notes/Assignment2223_student_v2_original/Pipfile.lock
generated
Normal file
@@ -0,0 +1,101 @@
|
||||
{
|
||||
"_meta": {
|
||||
"hash": {
|
||||
"sha256": "0fd1d2effb4d8a2ed0a80b8b6f653916ad4787fb9b8cd7141b795ea33dabd868"
|
||||
},
|
||||
"pipfile-spec": 6,
|
||||
"requires": {
|
||||
"python_version": "3.10"
|
||||
},
|
||||
"sources": [
|
||||
{
|
||||
"name": "pypi",
|
||||
"url": "https://pypi.org/simple",
|
||||
"verify_ssl": true
|
||||
}
|
||||
]
|
||||
},
|
||||
"default": {
|
||||
"mysql": {
|
||||
"hashes": [
|
||||
"sha256:8893cb143a5ac525c49ef358a23b8a0dc9721a95646f7bab6ca2f384c18a6a9a",
|
||||
"sha256:fd7bae7d7301ce7cd3932e5ff7f77bbc8e34872108252866e08d16d6b8e8de8c"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==0.0.3"
|
||||
},
|
||||
"mysql-connector-python": {
|
||||
"hashes": [
|
||||
"sha256:145aeb75eefb7425e0a7fb36a4f95ebfe79e06be7c69a4045d34cde95c666dc4",
|
||||
"sha256:1c0a11f3ffbf850f2ca7b39e6c82021e8de910ddaeffd856e53dca028d21c923",
|
||||
"sha256:1f399f3c2599d2591854cd0e0a24c7c399dff21ac5accb6e52e06924de29f3f4",
|
||||
"sha256:232095f0c36266510009b0f1214d2823a649efb8bc511dbab9ce8847f66ab08a",
|
||||
"sha256:283fe6f647e9d684feb1b7c48fa6a46b1e72c59ecdd6ea2b62392cd80c1a6701",
|
||||
"sha256:4b2d00c9e2cb9e3d11c57ec411226f43aa627607085fbed661cfea1c4dc57f61",
|
||||
"sha256:4df11c683924ef34c177a54887dc4844ae735b01c8a29ce6ab92d6d3db7a2757",
|
||||
"sha256:677b5c6dcaec7e2a4bf95b991a869f4d371114f69a0d9a5bb236e988c8f4c376",
|
||||
"sha256:6cdba2779bcd16af0ceff0a6e50d33e6664a83f8d17d70524beb6f677a6d1fae",
|
||||
"sha256:7f7a69db9e0c36764a6c65377f6174aee46e484520e48659e7aa674415b8e192",
|
||||
"sha256:8c334c41cd1c5bcfa3550340253ef7d9d3b962211f33327c20f69706a0bcce06",
|
||||
"sha256:8c5bfedc979d7858402f39c20d66a6cf03ca4c960732a98318126c278535ddb2",
|
||||
"sha256:93b1eb3e07d19a23ccf2605d818aacee0d842b1820bbeef8d0022d8d3d014ab9",
|
||||
"sha256:992b7a464daa398e86df8c75f7d8cd6044f884ff9087e782120fc8beff96c638",
|
||||
"sha256:ab13dd6ede0e0e99ba97c73946462c3420625ab6e63fe13b6fc350e30eb3298d",
|
||||
"sha256:bd52a462759aa324a60054c4b44dc8b32007187a328f72be6b58f193d5e32a91",
|
||||
"sha256:bdd716b1e162fe4b3887f6617e9ddcfa659ba96a9ddb22feeae208a72f43d22f",
|
||||
"sha256:be82357cc7e7e1377e2f4f8c18aa89c8aab6c0117155cf9fcf18e3cd0eb6ac8e",
|
||||
"sha256:c2d20b29fd096a0633f9360c275bd2434d4bcf597281991c4b7f1c820cd07b84",
|
||||
"sha256:c8bba02501525e1fbbba094a6d8d391d1534e8be41be6396c3e1b9f7d9d13b1c",
|
||||
"sha256:c990f4c0702d1739076261c4dece1042e1eb18bf34e0d8516d19ec5166a205ce",
|
||||
"sha256:d6b54656ca131a4f0f17b9d0adddc60f84fd982d64e06360026d5b06e5dbf865",
|
||||
"sha256:e0299236297b63bf6cbb61d81a9d400bc01cad4743d1abe5296ef349de15ee53",
|
||||
"sha256:e722b6ffa5b0d7188eebac792b18bc871643db505bf60d0e6bd2859f31e5ed79",
|
||||
"sha256:fd233c83daaf048c1f9827be984c2721576ae0adf50e139429a06ccd094987d9"
|
||||
],
|
||||
"index": "pypi",
|
||||
"version": "==8.0.32"
|
||||
},
|
||||
"mysqlclient": {
|
||||
"hashes": [
|
||||
"sha256:0d1cd3a5a4d28c222fa199002810e8146cffd821410b67851af4cc80aeccd97c",
|
||||
"sha256:828757e419fb11dd6c5ed2576ec92c3efaa93a0f7c39e263586d1ee779c3d782",
|
||||
"sha256:996924f3483fd36a34a5812210c69e71dea5a3d5978d01199b78b7f6d485c855",
|
||||
"sha256:b355c8b5a7d58f2e909acdbb050858390ee1b0e13672ae759e5e784110022994",
|
||||
"sha256:c1ed71bd6244993b526113cca3df66428609f90e4652f37eb51c33496d478b37",
|
||||
"sha256:c812b67e90082a840efb82a8978369e6e69fc62ce1bda4ca8f3084a9d862308b",
|
||||
"sha256:dea88c8d3f5a5d9293dfe7f087c16dd350ceb175f2f6631c9cf4caf3e19b7a96"
|
||||
],
|
||||
"markers": "python_version >= '3.5'",
|
||||
"version": "==2.1.1"
|
||||
},
|
||||
"protobuf": {
|
||||
"hashes": [
|
||||
"sha256:03038ac1cfbc41aa21f6afcbcd357281d7521b4157926f30ebecc8d4ea59dcb7",
|
||||
"sha256:28545383d61f55b57cf4df63eebd9827754fd2dc25f80c5253f9184235db242c",
|
||||
"sha256:2e3427429c9cffebf259491be0af70189607f365c2f41c7c3764af6f337105f2",
|
||||
"sha256:398a9e0c3eaceb34ec1aee71894ca3299605fa8e761544934378bbc6c97de23b",
|
||||
"sha256:44246bab5dd4b7fbd3c0c80b6f16686808fab0e4aca819ade6e8d294a29c7050",
|
||||
"sha256:447d43819997825d4e71bf5769d869b968ce96848b6479397e29fc24c4a5dfe9",
|
||||
"sha256:67a3598f0a2dcbc58d02dd1928544e7d88f764b47d4a286202913f0b2801c2e7",
|
||||
"sha256:74480f79a023f90dc6e18febbf7b8bac7508420f2006fabd512013c0c238f454",
|
||||
"sha256:819559cafa1a373b7096a482b504ae8a857c89593cf3a25af743ac9ecbd23480",
|
||||
"sha256:899dc660cd599d7352d6f10d83c95df430a38b410c1b66b407a6b29265d66469",
|
||||
"sha256:8c0c984a1b8fef4086329ff8dd19ac77576b384079247c770f29cc8ce3afa06c",
|
||||
"sha256:9aae4406ea63d825636cc11ffb34ad3379335803216ee3a856787bcf5ccc751e",
|
||||
"sha256:a7ca6d488aa8ff7f329d4c545b2dbad8ac31464f1d8b1c87ad1346717731e4db",
|
||||
"sha256:b6cc7ba72a8850621bfec987cb72623e703b7fe2b9127a161ce61e61558ad905",
|
||||
"sha256:bf01b5720be110540be4286e791db73f84a2b721072a3711efff6c324cdf074b",
|
||||
"sha256:c02ce36ec760252242a33967d51c289fd0e1c0e6e5cc9397e2279177716add86",
|
||||
"sha256:d9e4432ff660d67d775c66ac42a67cf2453c27cb4d738fc22cb53b5d84c135d4",
|
||||
"sha256:daa564862dd0d39c00f8086f88700fdbe8bc717e993a21e90711acfed02f2402",
|
||||
"sha256:de78575669dddf6099a8a0f46a27e82a1783c557ccc38ee620ed8cc96d3be7d7",
|
||||
"sha256:e64857f395505ebf3d2569935506ae0dfc4a15cb80dc25261176c784662cdcc4",
|
||||
"sha256:f4bd856d702e5b0d96a00ec6b307b0f51c1982c2bf9c0052cf9019e9a544ba99",
|
||||
"sha256:f4c42102bc82a51108e449cbb32b19b180022941c727bac0cfd50170341f16ee"
|
||||
],
|
||||
"markers": "python_version >= '3.7'",
|
||||
"version": "==3.20.3"
|
||||
}
|
||||
},
|
||||
"develop": {}
|
||||
}
|
@@ -0,0 +1,116 @@
|
||||
import tkinter
|
||||
import tkinter.ttk
|
||||
|
||||
import constants
|
||||
from models import Programme
|
||||
from widgets import ModuleWidget
|
||||
|
||||
# app.py - Main GUI dialog
|
||||
# TODO: Remove all 'pass' statements and complete the implementation based on class description
|
||||
|
||||
class App(tkinter.Tk):
|
||||
def __init__(self, db):
|
||||
super().__init__()
|
||||
self.__db = db
|
||||
self.refresh_db()
|
||||
|
||||
self.__programme = None
|
||||
self.__modules = None
|
||||
self.setup_ui()
|
||||
|
||||
# NOTE: Do not modify this method
|
||||
def report_callback_exception(self, *args):
|
||||
"""When an error is occurred, raise the exception
|
||||
so main_gui.py will handle the display of error.
|
||||
"""
|
||||
raise args[1].with_traceback(args[2])
|
||||
|
||||
def refresh_db(self):
|
||||
# TODO: Fetch all program records from database, and store it in __programme as a list
|
||||
pass
|
||||
|
||||
def setup_ui(self):
|
||||
self.title(constants.APP_NAME)
|
||||
self.minsize(640, 480)
|
||||
|
||||
# Set weight of column 0 for auto resizing on X axis
|
||||
self.columnconfigure(0, weight=1)
|
||||
|
||||
# Header
|
||||
frm_top_bar = tkinter.Frame(self)
|
||||
frm_top_bar.grid(row=0, sticky=tkinter.EW)
|
||||
tkinter.Label(
|
||||
frm_top_bar,
|
||||
text = constants.APP_NAME,
|
||||
font = ('bold', 16)
|
||||
).pack(side = tkinter.LEFT, padx=8, pady=8)
|
||||
tkinter.ttk.Separator(self).grid(row=1, sticky=tkinter.EW)
|
||||
|
||||
# Content
|
||||
frm_content = tkinter.Frame(self)
|
||||
frm_content.grid(row=2, sticky=tkinter.NSEW)
|
||||
self.rowconfigure(2, weight=1)
|
||||
|
||||
# Options
|
||||
frm_filter = tkinter.LabelFrame(frm_content, text = "Options")
|
||||
frm_filter.pack(fill=tkinter.X, side=tkinter.TOP, padx=8, pady=(4, 0))
|
||||
frm_filter_container = tkinter.Frame(frm_filter)
|
||||
frm_filter_container.pack(fill=tkinter.BOTH, side=tkinter.TOP, padx=6, pady=(0, 4))
|
||||
|
||||
# Options variables
|
||||
# TODO: Implement tkinter.StringVar for the widgets in 'Options'
|
||||
|
||||
# Option widgets
|
||||
tkinter.Label(
|
||||
frm_filter_container,
|
||||
text = "Programme",
|
||||
).grid(row=0, column=0, padx=(0, 4), sticky=tkinter.W)
|
||||
# TODO: Implement tkinter.OptionMenu __om_programme_filter
|
||||
options_list = ["follow methods inside assignment", "follow methods inside assignment 2", "follow methods inside assignment 3", "follow methods inside assignment 4"]
|
||||
value_inside = tkinter.StringVar(frm_filter_container)
|
||||
question_menu = tkinter.ttk.OptionMenu(frm_filter_container, value_inside, *options_list)
|
||||
question_menu.grid(row=0, column=1, padx=(0, 4), sticky=tkinter.W)
|
||||
print(question_menu["menu"].keys())
|
||||
|
||||
# Semester select
|
||||
tkinter.Label(
|
||||
frm_filter_container,
|
||||
text = "Semester",
|
||||
).grid(row=0, column=2, padx=(4, 4), sticky=tkinter.W)
|
||||
# TODO: Implement tkinter.OptionMenu __om_semester_filter
|
||||
|
||||
# Modules
|
||||
frm_modules = tkinter.LabelFrame(frm_content, text = "Modules")
|
||||
frm_modules.pack(fill=tkinter.BOTH, side=tkinter.TOP, expand=1, padx=8, pady=(4, 0))
|
||||
self.__frm_modules_container = tkinter.Frame(frm_modules)
|
||||
self.__frm_modules_container.pack(fill=tkinter.BOTH, side=tkinter.TOP, padx=6, pady=(0, 4))
|
||||
|
||||
self.__module_widgets = list()
|
||||
for _ in range(constants.APP_MAX_MODULES):
|
||||
widget = ModuleWidget(self.__frm_modules_container, self.on_gpa_changed)
|
||||
widget.get_frame().pack(fill=tkinter.X, side=tkinter.TOP, expand=1, pady=8)
|
||||
self.__module_widgets.append(widget)
|
||||
|
||||
# Bottom bar
|
||||
frm_bottom_bar = tkinter.Frame(self)
|
||||
frm_bottom_bar.grid(row=3, sticky=tkinter.EW)
|
||||
|
||||
# Result Label
|
||||
# TODO: Implement tkinter.Label __lbl_result
|
||||
|
||||
def setup_module_list(self):
|
||||
# TODO: Update the ModuleWidget in list __module_widgets to display
|
||||
# modules for the selected programme and semester
|
||||
pass
|
||||
|
||||
def on_programme_changed(self, _):
|
||||
# TODO: Get selected programme based on option menu index, and update GUI
|
||||
pass
|
||||
|
||||
def on_semester_changed(self, _):
|
||||
# TODO: Get modules based on option menu value, and update GUI
|
||||
pass
|
||||
|
||||
def on_gpa_changed(self):
|
||||
# TODO: Calcuate semester GPA based on self.__modules
|
||||
pass
|
@@ -0,0 +1,28 @@
|
||||
# Database
|
||||
# TODO: Modify the username and password if needed
|
||||
DB_HOST = "localhost"
|
||||
|
||||
# TODO: fallback root password
|
||||
DB_USER = "db_user"
|
||||
DB_PASS = "db_user_pass"
|
||||
|
||||
DB_NAME = "itp4459_asg"
|
||||
|
||||
# Tkinter GUI
|
||||
APP_NAME = "GPA Calculator"
|
||||
APP_MAX_MODULES = 7
|
||||
|
||||
# Application logic
|
||||
GPA_MAPPING = {
|
||||
"A": 4,
|
||||
"A-": 3.7,
|
||||
"B+": 3.3,
|
||||
"B": 3,
|
||||
"B-": 2.7,
|
||||
"C+": 2.3,
|
||||
"C": 2,
|
||||
"C-": 1.7,
|
||||
"D+": 1.3,
|
||||
"D": 1,
|
||||
"F": 0
|
||||
}
|
@@ -0,0 +1,78 @@
|
||||
from abc import ABC, abstractmethod
|
||||
|
||||
import mysql.connector
|
||||
|
||||
# db.py - Database related classes
|
||||
# NOTE: You do not need to modify this source file
|
||||
|
||||
__all__ = (
|
||||
'MySQLConnection', 'MySQLObject'
|
||||
)
|
||||
|
||||
class MySQLConnection:
|
||||
"""A class to handle all database related functions for this assingment."""
|
||||
|
||||
def __init__(self, host, user, password, database=None):
|
||||
"""Initialize connection to MySQL server.
|
||||
|
||||
Create database if needed automatically.
|
||||
"""
|
||||
self.db = mysql.connector.connect(
|
||||
host=host,
|
||||
user=user,
|
||||
password=password
|
||||
)
|
||||
if database is not None:
|
||||
with self.db.cursor() as cursor:
|
||||
cursor.execute(f"CREATE DATABASE IF NOT EXISTS {database};")
|
||||
self.db.commit()
|
||||
self.db.database = database
|
||||
|
||||
def close(self):
|
||||
"""Closes the database connection."""
|
||||
self.db.close()
|
||||
|
||||
def cursor(self):
|
||||
"""Returns a database cursor, for executing multiple statments
|
||||
in a single transaction.
|
||||
"""
|
||||
return self.db.cursor()
|
||||
|
||||
def commit(self):
|
||||
"""Commit changes to database."""
|
||||
self.db.commit()
|
||||
|
||||
def execute(self, query):
|
||||
"""Commit: Python -> Database
|
||||
|
||||
e.g. CREATE, INSERT and UPDATE
|
||||
"""
|
||||
with self.db.cursor() as cursor:
|
||||
cursor.execute(query)
|
||||
self.db.commit()
|
||||
|
||||
def fetchone(self, query):
|
||||
"""Fetch: Python <- Database
|
||||
|
||||
e.g. SELECT and SHOW
|
||||
"""
|
||||
with self.db.cursor() as cursor:
|
||||
cursor.execute(query)
|
||||
return cursor.fetchone()
|
||||
|
||||
def fetchall(self, query):
|
||||
"""Fetch: Python <- Database
|
||||
|
||||
e.g. SELECT and SHOW
|
||||
"""
|
||||
with self.db.cursor() as cursor:
|
||||
cursor.execute(query)
|
||||
return cursor.fetchall()
|
||||
|
||||
class MySQLObject(ABC):
|
||||
"""A database managed object."""
|
||||
@staticmethod
|
||||
@abstractmethod
|
||||
def fetchall(db):
|
||||
"""Returns a list of object imported from database."""
|
||||
return NotImplemented
|
@@ -0,0 +1,25 @@
|
||||
import constants
|
||||
from db import MySQLConnection
|
||||
|
||||
# main_db.py - Program entry point for Part 1 Database
|
||||
|
||||
if __name__ == "__main__":
|
||||
db = MySQLConnection(
|
||||
host=constants.DB_HOST,
|
||||
user=constants.DB_USER,
|
||||
password=constants.DB_PASS
|
||||
)
|
||||
with db.cursor() as cursor:
|
||||
# Recreate database and select as default
|
||||
cursor.execute(f"DROP DATABASE IF EXISTS {constants.DB_NAME};")
|
||||
cursor.execute(f"CREATE DATABASE {constants.DB_NAME};")
|
||||
cursor.execute(f"USE {constants.DB_NAME};")
|
||||
|
||||
# Tables
|
||||
# TODO: Create tables according to the database description
|
||||
|
||||
# Records
|
||||
# TODO: Insert records according to the database description
|
||||
|
||||
db.commit()
|
||||
db.close()
|
@@ -0,0 +1,42 @@
|
||||
from tkinter import messagebox
|
||||
|
||||
import constants
|
||||
import traceback
|
||||
from app import App
|
||||
from db import MySQLConnection
|
||||
|
||||
# main_gui.py - Program entry point for Part 2 GUI
|
||||
# NOTE: You do not need to modify this source file
|
||||
|
||||
if __name__ == "__main__":
|
||||
app = None
|
||||
db = None
|
||||
try:
|
||||
# Create connection to database
|
||||
db = MySQLConnection(
|
||||
host=constants.DB_HOST,
|
||||
user=constants.DB_USER,
|
||||
password=constants.DB_PASS,
|
||||
database=constants.DB_NAME)
|
||||
|
||||
# Create and start app
|
||||
app = App(db)
|
||||
app.mainloop()
|
||||
except Exception as e:
|
||||
# Display error in console
|
||||
traceback.print_exception(e)
|
||||
|
||||
# Display error as message box
|
||||
errorType = f"{type(e).__name__}"
|
||||
messagebox.showerror(errorType, e.__str__())
|
||||
|
||||
# Close the app window
|
||||
if app is App:
|
||||
app.destroy()
|
||||
|
||||
# Raise the error again for debugger
|
||||
raise e
|
||||
finally:
|
||||
# Close database connection
|
||||
if db is MySQLConnection:
|
||||
db.close()
|
@@ -0,0 +1,10 @@
|
||||
from db import MySQLObject
|
||||
|
||||
# models.py - Python representation of database table records
|
||||
# TODO: Remove all 'pass' statements and complete the implementation based on class description
|
||||
|
||||
class Programme(MySQLObject):
|
||||
pass
|
||||
|
||||
class Module(MySQLObject):
|
||||
pass
|
@@ -0,0 +1,14 @@
|
||||
@REM feed
|
||||
@REM python ./src/main_db.py
|
||||
|
||||
@REM pip install mysql-connector-python-rf
|
||||
|
||||
python ./main_gui.py
|
||||
|
||||
@REM python ./src/test_combobox.py
|
||||
|
||||
@REM python .\src\options_test.py
|
||||
@REM pipenv run ".\src\models.py"
|
||||
|
||||
@REM pipenv run ".\src\test_models.py"
|
||||
|
@@ -0,0 +1,53 @@
|
||||
import tkinter
|
||||
|
||||
from constants import GPA_MAPPING
|
||||
|
||||
# widgets.py - Implements a custom GUI widget 'ModuleWidget'
|
||||
# TODO: Remove all 'pass' statements and complete the implementation based on class description
|
||||
|
||||
class ModuleWidget:
|
||||
"""A supplier class for displaying a module and returning the GPA value.
|
||||
"""
|
||||
def __init__(self, master, command):
|
||||
self.__module = None
|
||||
self.__command = command
|
||||
self.__gpa = -1
|
||||
|
||||
self.setup_ui(master)
|
||||
self.set_module(self.__module)
|
||||
|
||||
def set_module(self, module = None):
|
||||
# TODO: When module is provided:
|
||||
# - Update UI to display module information
|
||||
# - Enable option menu
|
||||
# Else
|
||||
# - Clear entry content
|
||||
# - Disable option menu
|
||||
# Finally, update self.__module
|
||||
pass
|
||||
|
||||
def get_frame(self):
|
||||
return self.__frame
|
||||
|
||||
def get_gpa(self):
|
||||
return self.__gpa
|
||||
|
||||
def setup_ui(self, master):
|
||||
# Variables
|
||||
# TODO: Implement tkinter.StringVar for the widgets in this class
|
||||
|
||||
# Contianer
|
||||
self.__frame = tkinter.Frame(master)
|
||||
|
||||
# Module name
|
||||
# TODO: Implement tkinter.Entry __ent_name
|
||||
|
||||
# Module credit
|
||||
# TODO: Implement tkinter.Entry __ent_credit
|
||||
|
||||
# Grade
|
||||
# TODO: Implement tkinter.OptionMenu __om_grade
|
||||
|
||||
def on_grade_changed(self, _):
|
||||
# TODO: Get GPA based on combo box value
|
||||
pass
|
BIN
ITP4459_assignment_2023/notes/Chap01 - Object and Class.pptx
Normal file
BIN
ITP4459_assignment_2023/notes/Chap01 - Object and Class.pptx
Normal file
Binary file not shown.
BIN
ITP4459_assignment_2023/notes/Chap02 - Initializer.pptx
Normal file
BIN
ITP4459_assignment_2023/notes/Chap02 - Initializer.pptx
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
ITP4459_assignment_2023/notes/Chap06 - Database Connection.pptx
Normal file
BIN
ITP4459_assignment_2023/notes/Chap06 - Database Connection.pptx
Normal file
Binary file not shown.
BIN
ITP4459_assignment_2023/notes/Chap07a - Tkinter Part 1.pptx
Normal file
BIN
ITP4459_assignment_2023/notes/Chap07a - Tkinter Part 1.pptx
Normal file
Binary file not shown.
BIN
ITP4459_assignment_2023/notes/Chap07b - Tkinter Part 2.pptx
Normal file
BIN
ITP4459_assignment_2023/notes/Chap07b - Tkinter Part 2.pptx
Normal file
Binary file not shown.
BIN
ITP4459_assignment_2023/notes/Chap08 - Web Scraping.pptx
Normal file
BIN
ITP4459_assignment_2023/notes/Chap08 - Web Scraping.pptx
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
ITP4459_assignment_2023/notes/Lab_02a - Initializer_Answers.doc
Normal file
BIN
ITP4459_assignment_2023/notes/Lab_02a - Initializer_Answers.doc
Normal file
Binary file not shown.
BIN
ITP4459_assignment_2023/notes/Lab_02b - Initializer_Answers.doc
Normal file
BIN
ITP4459_assignment_2023/notes/Lab_02b - Initializer_Answers.doc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
ITP4459_assignment_2023/notes/Lab_04b - Inheritance_Answers.doc
Normal file
BIN
ITP4459_assignment_2023/notes/Lab_04b - Inheritance_Answers.doc
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
BIN
ITP4459_assignment_2023/notes/Lab_07a - Tkinter_Answers.doc
Normal file
BIN
ITP4459_assignment_2023/notes/Lab_07a - Tkinter_Answers.doc
Normal file
Binary file not shown.
BIN
ITP4459_assignment_2023/notes/Lab_07b - Tkinter_Answers.doc
Normal file
BIN
ITP4459_assignment_2023/notes/Lab_07b - Tkinter_Answers.doc
Normal file
Binary file not shown.
BIN
ITP4459_assignment_2023/notes/Lab_08a - Web Scraping_Answers.doc
Normal file
BIN
ITP4459_assignment_2023/notes/Lab_08a - Web Scraping_Answers.doc
Normal file
Binary file not shown.
Binary file not shown.
Reference in New Issue
Block a user