diff --git a/class_editor.py b/class_editor.py new file mode 100644 index 0000000..de1d775 --- /dev/null +++ b/class_editor.py @@ -0,0 +1,36 @@ +import imgui + +from model import * + +class ClassEditor: + def __init__(self): + super().__init__() + self.add_name = str() + self.select = 0 + + def __call__(self): + classes = Class.select() + + with imgui.begin("Class Editor", False, imgui.WINDOW_NO_RESIZE | imgui.WINDOW_NO_COLLAPSE): + imgui.text("Add Class") + + _, self.add_name = imgui.input_text(" ", self.add_name) + + if imgui.button("Add"): + if self.add_name: + Class.create(name=self.add_name) + self.add_name = str() + + imgui.separator() + + if not classes: + imgui.text("No Dataset could be queried") + return + + for n, c in enumerate(classes, start=1): + display = f"{n}. {c.name}" + opened, _ = imgui.selectable(display, self.select == n-1) + if opened: + self.select = n-1 + + return classes[self.select] diff --git a/datatypes.py b/datatypes.py new file mode 100644 index 0000000..74eb1be --- /dev/null +++ b/datatypes.py @@ -0,0 +1,15 @@ +from PIL import ImageColor +from enum import IntEnum + +# Global Color Pallet +COLOR_BACKGROUND = tuple([e/255 for e in ImageColor.getcolor("#29132E","RGBA")]) +COLOR_1 = tuple([e/255 for e in ImageColor.getcolor("#321450","RGBA")]) +COLOR_2 = tuple([e/255 for e in ImageColor.getcolor("#860029","RGBA")]) +COLOR_3 = tuple([e/255 for e in ImageColor.getcolor("#DE004E","RGBA")]) +COLOR_TEXT = tuple([e/255 for e in ImageColor.getcolor("#F887FF","RGBA")]) +COLOR_TEXT_PASSED = tuple([e/255 for e in ImageColor.getcolor("#1AFE49","RGBA")]) +COLOR_TEXT_FAILED = tuple([e/255 for e in ImageColor.getcolor("#FF124F","RGBA")]) + +class LayoutOptions(IntEnum): + EDITOR = 1 + GRAPHER = 2 diff --git a/gui.py b/gui.py new file mode 100644 index 0000000..1afddce --- /dev/null +++ b/gui.py @@ -0,0 +1,89 @@ +import imgui +import glfw +import OpenGL.GL as gl +from imgui.integrations.glfw import GlfwRenderer +from datatypes import * +from view import View + +def impl_glfw_init(window_name="Grapher Tool", width=1280, height=720): + if not glfw.init(): + print("Could not initialize OpenGL context") + exit(1) + + # OS X supports only forward-compatible core profiles from 3.2 + glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 3) + glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 3) + glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE) + + glfw.window_hint(glfw.OPENGL_FORWARD_COMPAT, gl.GL_TRUE) + + # Create a windowed mode window and its OpenGL context + window = glfw.create_window(int(width), int(height), window_name, None, None) + glfw.make_context_current(window) + + if not window: + glfw.terminate() + print("Could not initialize Window") + exit(1) + + return window + +class GUI(object): + def __init__(self): + super().__init__() + + # Window States + self.window = impl_glfw_init() + gl.glClearColor(*COLOR_BACKGROUND) + imgui.create_context() + self.impl = GlfwRenderer(self.window) + self.io = imgui.get_io() + + # Global GUI Setting + win_w, win_h = glfw.get_window_size(self.window) + fb_w, fb_h = glfw.get_framebuffer_size(self.window) + font_scaling_factor = max(float(fb_w) / win_w, float(fb_h) / win_h) + font_size_in_pixels = 30 + self.io.fonts.add_font_from_file_ttf("assets/MPLUSRounded1c-Regular.ttf", font_size_in_pixels * font_scaling_factor) + self.io.font_global_scale /= font_scaling_factor + + self.view = View() + self.loop() + + def header(self): + imgui.set_next_window_size(io.display_size.x, io.display_size.y*0.03) + imgui.set_next_window_position(0, io.display_size.y*0.02) + + with imgui.begin("HEADER", False, imgui.WINDOW_NO_MOVE | imgui.WINDOW_NO_RESIZE | imgui.WINDOW_NO_COLLAPSE | imgui.WINDOW_NO_TITLE_BAR): + imgui.set_window_font_scale(1.3) + text = "Student Analyzer" + ww = imgui.get_window_size().x + tw = imgui.calc_text_size(text).x + imgui.set_cursor_pos_x((ww - tw) * 0.5) + imgui.text("Student Analyzer") + + def loop(self): + while not glfw.window_should_close(self.window): + glfw.poll_events() + self.impl.process_inputs() + imgui.new_frame() + + self.view() + + #imgui.show_test_window() + + imgui.render() + + gl.glClearColor(*COLOR_BACKGROUND) + gl.glClear(gl.GL_COLOR_BUFFER_BIT) + + self.impl.render(imgui.get_draw_data()) + glfw.swap_buffers(self.window) + + self.impl.shutdown() + glfw.terminate() + + +if __name__ == "__main__": + + gui = GUI() diff --git a/lecture_editor.py b/lecture_editor.py new file mode 100644 index 0000000..b7a2682 --- /dev/null +++ b/lecture_editor.py @@ -0,0 +1,45 @@ +import imgui + +from datatypes import * + +from model import * + +class LectureEditor: + def __init__(self): + super().__init__() + + self.select = 0 + self.add_lecture_text = str() + self.add_lecture_points = 0 + + def __call__(self, clas: Class): + id = clas.id if clas else None + lectures = Lecture.select().where(Lecture.class_id == id) if id else None + + with imgui.begin("Lecture Editor", False, imgui.WINDOW_NO_MOVE | imgui.WINDOW_NO_RESIZE | imgui.WINDOW_NO_COLLAPSE): + imgui.text("Add Lecture") + _, self.add_lecture_text = imgui.input_text("Title", self.add_lecture_text) + if self.add_lecture_points < 0: + self.add_lecture_points = 0 + _, self.add_lecture_points = imgui.input_int("Points", self.add_lecture_points) + + if imgui.button("Add"): + Lecture.create( + title=self.add_lecture_text, + points=self.add_lecture_points, + class_id=id + ) + + imgui.separator() + + if not lectures: + imgui.text("No Lectures could be queried") + return + + for n, lecture in enumerate(lectures, start=1): + display = f"{n}. {lecture.title}" + opened, _ = imgui.selectable(display, self.select == n-1) + if opened: + self.select = n-1 + + return lectures[self.select] diff --git a/main.py b/main.py index 45817de..818b799 100644 --- a/main.py +++ b/main.py @@ -1,24 +1,6 @@ import imgui -import glfw -import OpenGL.GL as gl -from imgui.integrations.glfw import GlfwRenderer -from PIL import ImageColor -from dataclasses import dataclass import numpy -@dataclass -class Lecture: - name: str - points: float - max_points: int - -@dataclass -class Student: - name: str - jupyter_id: str - project: str - lectures: list - phil = Student( "Phil Keier", "772fb04b24caa68fd38a05ec2a22e62b", "Geomapping", [Lecture("1. Tutorial 1", 28.5, 31), Lecture("2. Tutorial 2", 4.5, 15), Lecture("3. Extended Application", 18, 18)] @@ -33,239 +15,7 @@ kathi = Student( ) students = [phil, nova, kathi] -# Global Color Pallet -COLOR_BACKGROUND = tuple([e/255 for e in ImageColor.getcolor("#29132E","RGBA")]) -COLOR_1 = tuple([e/255 for e in ImageColor.getcolor("#321450","RGBA")]) -COLOR_2 = tuple([e/255 for e in ImageColor.getcolor("#860029","RGBA")]) -COLOR_3 = tuple([e/255 for e in ImageColor.getcolor("#DE004E","RGBA")]) -COLOR_TEXT = tuple([e/255 for e in ImageColor.getcolor("#F887FF","RGBA")]) -COLOR_TEXT_PASSED = tuple([e/255 for e in ImageColor.getcolor("#1AFE49","RGBA")]) -COLOR_TEXT_FAILED = tuple([e/255 for e in ImageColor.getcolor("#FF124F","RGBA")]) - -def impl_glfw_init(window_name="Grapher Tool", width=1280, height=720): - if not glfw.init(): - print("Could not initialize OpenGL context") - exit(1) - - # OS X supports only forward-compatible core profiles from 3.2 - glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, 3) - glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, 3) - glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE) - - glfw.window_hint(glfw.OPENGL_FORWARD_COMPAT, gl.GL_TRUE) - - # Create a windowed mode window and its OpenGL context - window = glfw.create_window(int(width), int(height), window_name, None, None) - glfw.make_context_current(window) - - if not window: - glfw.terminate() - print("Could not initialize Window") - exit(1) - - return window - - -class GUI(object): - def __init__(self): - super().__init__() - self.backgroundColor = COLOR_BACKGROUND - self.window = impl_glfw_init() - gl.glClearColor(*self.backgroundColor) - imgui.create_context() - self.impl = GlfwRenderer(self.window) - - # App states - self.select_student: int = 0 - self.select_lecture: int = 0 - self.lectures: list = list() - self.add_lecture_text = str() - self.add_lecture_points = 0 - self.edit_lecture_title = str() - self.edit_lecture_points = 0 - - # Global GUI Setting - win_w, win_h = glfw.get_window_size(self.window) - fb_w, fb_h = glfw.get_framebuffer_size(self.window) - font_scaling_factor = max(float(fb_w) / win_w, float(fb_h) / win_h) - font_size_in_pixels = 30 - io = imgui.get_io() - io.fonts.add_font_from_file_ttf("assets/MPLUSRounded1c-Regular.ttf", font_size_in_pixels * font_scaling_factor) - io.font_global_scale /= font_scaling_factor - - self.loop() - - def student_info(self, student: Student): - - # Window Position and Sizing - io = imgui.get_io() - imgui.set_next_window_size(io.display_size.x*0.3, io.display_size.y*0.4) - imgui.set_next_window_position(io.display_size.x*0.7, io.display_size.y*0.05) - - # Student accumulated Info - overall_points = sum([lecture.points for lecture in student.lectures]) - if overall_points.is_integer(): - overall_points = int(overall_points) - overall_max = sum([lecture.max_points for lecture in student.lectures]) - - with imgui.begin("Student Info", False, imgui.WINDOW_NO_MOVE | imgui.WINDOW_NO_RESIZE | imgui.WINDOW_NO_COLLAPSE): - if not student: - imgui.text("No Student selected") - return - - s = f"{overall_points}/{overall_max} | {round(overall_points/overall_max*100, 1)}%" - imgui.text_colored(student.name, *COLOR_TEXT) - w, h = imgui.get_window_size() - imgui.progress_bar(overall_points/overall_max, (w*0.5, h*0.1), s) - imgui.text("Jupyter ID:") - imgui.text_colored(student.jupyter_id.rjust(4+len(student.jupyter_id), " "), *COLOR_TEXT) - - imgui.separator() - imgui.text("Project:") - imgui.text_colored(student.project.rjust(4+len(student.project), " "), *COLOR_TEXT) - imgui.text("Lectures:") - for lecture in student.lectures: - COLOR = COLOR_TEXT_PASSED if lecture.points >= lecture.max_points/3 else COLOR_TEXT_FAILED - s = f"{lecture.name}: {lecture.points}/{lecture.max_points}" - imgui.text_colored(s.rjust(4+len(s), " "), *COLOR) - - def table(self, students: list): - - # Window Position and Sizing - io = imgui.get_io() - imgui.set_next_window_size(io.display_size.x*0.15, io.display_size.y*0.95) - imgui.set_next_window_position(0, io.display_size.y*0.05) - - with imgui.begin("Student Table", False, imgui.WINDOW_NO_MOVE | imgui.WINDOW_NO_RESIZE | imgui.WINDOW_NO_COLLAPSE): - if not students: - imgui.text("No Dataset selected") - return - - for n, student in enumerate(students): - opened, _ = imgui.selectable(student.name, self.select_student == n) - if opened: - self.select_student = n - - - def student_graph(self, student: Student): - - # Window Position and Sizing - io = imgui.get_io() - imgui.set_next_window_size(io.display_size.x*0.55, io.display_size.y*0.4) - imgui.set_next_window_position(io.display_size.x*0.15, io.display_size.y*0.05) - - # Setup Data - data = numpy.array([float(lecture.points) / float(lecture.max_points) * 100 for lecture in student.lectures], dtype=numpy.float32) - - with imgui.begin("Student Graph", False, imgui.WINDOW_NO_MOVE | imgui.WINDOW_NO_RESIZE | imgui.WINDOW_NO_COLLAPSE): - if not students: - imgui.text("No Dataset selected") - return - imgui.plot_histogram( - "##Data", data, overlay_text="Performance per Lecture (in %)", - scale_min=0.0, scale_max=100, - graph_size=imgui.get_content_region_available() - ) - - def header(self): - - # Window Position and Sizing - io = imgui.get_io() - imgui.set_next_window_size(io.display_size.x, io.display_size.y*0.03) - imgui.set_next_window_position(0, io.display_size.y*0.02) - - with imgui.begin("HEADER", False, imgui.WINDOW_NO_MOVE | imgui.WINDOW_NO_RESIZE | imgui.WINDOW_NO_COLLAPSE | imgui.WINDOW_NO_TITLE_BAR): - imgui.set_window_font_scale(1.3) - text = "Student Analyzer" - ww = imgui.get_window_size().x - tw = imgui.calc_text_size(text).x - imgui.set_cursor_pos_x((ww - tw) * 0.5) - imgui.text("Student Analyzer") - - def editor(self): - - # Window Position and Sizing - io = imgui.get_io() - imgui.set_next_window_size(io.display_size.x*0.3, io.display_size.y*0.3) - imgui.set_next_window_position(io.display_size.x*0.7, io.display_size.y*0.45) - lecture = None - - with imgui.begin("Lecture Editor", False, imgui.WINDOW_NO_MOVE | imgui.WINDOW_NO_RESIZE | imgui.WINDOW_NO_COLLAPSE): - with imgui.begin_group(): - if not self.lectures: - imgui.text("No Lecture Added") - else: - for n, lecture in enumerate(self.lectures): - opened, _ = imgui.selectable(lecture.name, self.select_lecture == n, width=imgui.get_window_size().x*0.3) - if opened: - self.select_lecture = n - lecture = self.lectures[self.select_lecture] - self.edit_lecture_title = lecture.name - self.edit_lecture_points = lecture.max_points - - imgui.same_line(spacing=int(imgui.get_window_size().x * 0.05)) - - with imgui.begin_group(): - imgui.push_item_width(imgui.get_window_size().x * 0.2) - imgui.text("Add Lecture") - _, self.add_lecture_text = imgui.input_text(" ", self.add_lecture_text) - _, self.add_lecture_points = imgui.input_int("", self.add_lecture_points) - - if self.add_lecture_points < 0: - self.add_lecture_points = 0 - - clicked = imgui.button("Confirm") - if clicked: - self.lectures.append(Lecture(self.add_lecture_text, 0, self.add_lecture_points)) - self.add_lecture_points = 0 - self.add_lecture_text = str() - imgui.pop_item_width() - - if lecture: - imgui.same_line(spacing=int(imgui.get_window_size().x * 0.05)) - with imgui.begin_group(): - _, self.edit_lecture_title = imgui.input_text(" ", self.edit_lecture_title) - _, self.edit_lecture_points = imgui.input_int("", self.edit_lecture_points) - if self.edit_lecture_points < 0: - self.edit_lecture_points = 0 - - def main_menu(self): - with imgui.begin_main_menu_bar() as main_menu_bar: - if main_menu_bar: - with imgui.begin_menu("File", True) as file_menu: - if file_menu.opened: - imgui.menu_item("New", " ", False, True) - imgui.menu_item("Open", " ", False, True) - imgui.menu_item("Save", " ", False, True) - imgui.menu_item("Save as", " ", False, True) - - def loop(self): - while not glfw.window_should_close(self.window): - glfw.poll_events() - self.impl.process_inputs() - imgui.new_frame() - - self.main_menu() - self.header() - self.table(students) - self.student_info(students[self.select_student]) - self.student_graph(students[self.select_student]) - self.editor() - #imgui.show_test_window() - - imgui.render() - - gl.glClearColor(*self.backgroundColor) - gl.glClear(gl.GL_COLOR_BUFFER_BIT) - - self.impl.render(imgui.get_draw_data()) - glfw.swap_buffers(self.window) - - self.impl.shutdown() - glfw.terminate() - if __name__ == "__main__": - gui = GUI() diff --git a/main_menu.py b/main_menu.py new file mode 100644 index 0000000..a9a4e19 --- /dev/null +++ b/main_menu.py @@ -0,0 +1,26 @@ +import imgui + +from datatypes import * + +class MainMenu: + def __init__(self): + super().__init__() + + def __call__(self): + with imgui.begin_main_menu_bar() as main_menu_bar: + if main_menu_bar: + with imgui.begin_menu("File", True) as file_menu: + if file_menu.opened: + imgui.menu_item("New", " ", False, True) + imgui.menu_item("Open", " ", False, True) + imgui.menu_item("Save", " ", False, True) + imgui.menu_item("Save as", " ", False, True) + with imgui.begin_menu("View", True) as view_menu: + if view_menu.opened: + with imgui.begin_menu("Change Layout", True) as open_layout_menu: + if open_layout_menu.opened: + layout_options = list(LayoutOptions) + for n, l in enumerate(layout_options): + clicked, _ = imgui.menu_item(l.name.title(), None, False, True) + if clicked: + return l diff --git a/model.py b/model.py new file mode 100644 index 0000000..b843d4d --- /dev/null +++ b/model.py @@ -0,0 +1,61 @@ +from peewee import * +from datetime import datetime + +db = SqliteDatabase('test.db') + +class BaseModel(Model): + class Meta: + database = db + +class Class(BaseModel): + name = CharField() + created_at = DateTimeField(default=datetime.now) + +class Student(BaseModel): + prename = CharField() + surname = CharField() + sex = CharField() + class_id = ForeignKeyField(Class, backref='class') + created_at = DateTimeField(default=datetime.now) + +class Lecture(BaseModel): + title = CharField() + points = IntegerField() + class_id = ForeignKeyField(Class, backref='class') + created_at = DateTimeField(default=datetime.now) + +class Submission(BaseModel): + student_id = ForeignKeyField(Student, backref='student') + lecture_id = ForeignKeyField(Lecture, backref='lecture') + points = FloatField() + created_at = DateTimeField(default=datetime.now) + +db.connect() +db.create_tables([Class, Student, Lecture, Submission]) + +if __name__ == "__main__": + import random + # Generate Test Data + class1 = Class.create(name="WiSe 22/23") + class2 = Class.create(name="WiSe 23/24") + class3 = Class.create(name="WiSe 24/25") + + phil = Student.create(prename="Phil", surname="Keier", sex="Male", class_id=class1.id) + calvin = Student.create(prename="Calvin", surname="Brandt", sex="Male", class_id=class2.id) + nova = Student.create(prename="Nova", surname="Eib", sex="Female", class_id=class1.id) + kathi = Student.create(prename="Katharina", surname="Walz", sex="Female", class_id=class3.id) + victoria = Student.create(prename="Victoria", surname="Möller", sex="Female", class_id=class3.id) + + lec1 = Lecture.create(title="Tutorial 1", points=30, class_id=class1.id) + lec2 = Lecture.create(title="Tutorial 1", points=30, class_id=class3.id) + lec3 = Lecture.create(title="Tutorial 2", points=20, class_id=class1.id) + lec4 = Lecture.create(title="Tutorial 2", points=20, class_id=class2.id) + lec5 = Lecture.create(title="Extended Applications", points=44, class_id=class1.id) + + sub1_phil = Submission.create(student_id=phil.id, lecture_id=lec1.id, points=random.randint(0, lec1.points)) + sub2_phil = Submission.create(student_id=phil.id, lecture_id=lec3.id, points=random.randint(0, lec3.points)) + sub3_phil = Submission.create(student_id=phil.id, lecture_id=lec5.id, points=random.randint(0, lec5.points)) + sub1_nova = Submission.create(student_id=nova.id, lecture_id=lec1.id, points=random.randint(0, lec1.points)) + sub2_nova = Submission.create(student_id=nova.id, lecture_id=lec3.id, points=random.randint(0, lec3.points)) + sub1_kathi = Submission.create(student_id=kathi.id, lecture_id=lec3.id, points=random.randint(0, lec3.points)) + sub1_vici = Submission.create(student_id=victoria.id, lecture_id=lec2.id, points=random.randint(0, lec2.points)) diff --git a/student_editor.py b/student_editor.py new file mode 100644 index 0000000..48566cb --- /dev/null +++ b/student_editor.py @@ -0,0 +1,42 @@ +import imgui + +from model import * + +class StudentEditor: + def __init__(self): + super().__init__() + + self.prename = str() + self.surname = str() + self.sex = True + self.current = 0 + + def __call__(self): + with imgui.begin("Student Editor", False, imgui.WINDOW_NO_RESIZE | imgui.WINDOW_NO_COLLAPSE): + imgui.text("Add Student") + + _, self.prename = imgui.input_text("First Name", self.prename) + _, self.surname = imgui.input_text("Last Name", self.surname) + + with imgui.begin_group(): + if imgui.radio_button("Male", self.sex): + self.sex = True + + imgui.same_line() + + if imgui.radio_button("Female", not self.sex): + self.sex = False + + classes = Class.select() + if classes: + _, self.current = imgui.combo("Classes", self.current, [c.name for c in classes]) + + if imgui.button("Confirm") and classes: + Student.create( + prename=self.prename, + surname=self.surname, + sex='Male' if self.sex else 'Female', + class_id = classes[self.current] + ) + self.prename = str() + self.surname = str() diff --git a/student_graph.py b/student_graph.py new file mode 100644 index 0000000..04b29d3 --- /dev/null +++ b/student_graph.py @@ -0,0 +1,27 @@ +import imgui +import numpy as np +import random + +from model import * + +class StudentGraph: + def __init__(self): + super().__init__() + + def __call__(self): + # Setup Data + submissions = Submission.select().where(Submission.student_id == 1) + data = np.array([submission.points/Lecture.get_by_id(submission.lecture_id).points*100 for submission in submissions], dtype=np.float32) + + with imgui.begin("Student Graph", False, imgui.WINDOW_NO_MOVE | imgui.WINDOW_NO_RESIZE | imgui.WINDOW_NO_COLLAPSE): + + w, h = imgui.get_content_region_available() + + imgui.plot_histogram( + "##Data", data, overlay_text="Performance per Lecture (in %)", + scale_min=0.0, scale_max=100, + graph_size=(w, h*0.9) + ) + + imgui.button("Text") + diff --git a/student_info.py b/student_info.py new file mode 100644 index 0000000..f64171f --- /dev/null +++ b/student_info.py @@ -0,0 +1,60 @@ +import imgui +from datatypes import * + +from model import * + +class StudentInfo: + def __init__(self): + super().__init__() + + def __call__(self, student: Student): + submissions = Submission.select().where(Submission.student_id == student.id) if student else None + + with imgui.begin("Student Info", False, imgui.WINDOW_NO_MOVE | imgui.WINDOW_NO_RESIZE | imgui.WINDOW_NO_COLLAPSE): + if not student: + imgui.text("No Student selected") + return + + w, h = imgui.get_window_size() + imgui.text_colored(f"{student.prename} {student.surname}", *COLOR_TEXT) + + content = Class.get_by_id(student.class_id).name + text_size = imgui.calc_text_size(content) + imgui.same_line(position=w-1.5*text_size.x) + imgui.text(content) + + if submissions: + overall_points = sum([s.points for s in submissions]) + if overall_points.is_integer(): + overall_points = int(overall_points) + overall_max = sum([lectures.points for lectures in [Lecture.get_by_id(s.lecture_id) for s in submissions]]) + percentile = overall_points / overall_max + imgui.progress_bar(percentile, (w*0.5, h*0.05), f"{overall_points}/{overall_max} {percentile:.1%}") + + content = "Delete" + if submissions: + text_size = imgui.calc_text_size(content) + imgui.same_line(position=w-2*text_size.x) + if imgui.button(content): + # Delete all Submissions related to that Student + #for submission in submissions: + # submission.delete().execute() + # Delete Student + #student.delete().execute() + return + + imgui.separator() + + if not submissions: + imgui.text("No Submission for this Student") + + for n, submission in enumerate(submissions, start=1): + lecture = Lecture.get_by_id(submission.lecture_id) + + points = submission.points + if points.is_integer(): + points = int(points) + + display = f"{n}. {lecture.title} {points}/{lecture.points}" + COLOR = COLOR_TEXT_PASSED if points > lecture.points*0.3 else COLOR_TEXT_FAILED + imgui.text_colored(display, *COLOR) diff --git a/student_list.py b/student_list.py new file mode 100644 index 0000000..23b2942 --- /dev/null +++ b/student_list.py @@ -0,0 +1,28 @@ +import imgui + +from model import * + +class StudentList: + def __init__(self): + super().__init__() + + self.select: int = 0 + + def __call__(self, clas: Class): + id = clas.id if clas else None + students = Student.select().where(Student.class_id == id) if id else None + + with imgui.begin("Student Table", False, imgui.WINDOW_NO_MOVE | imgui.WINDOW_NO_RESIZE | imgui.WINDOW_NO_COLLAPSE): + if not students: + imgui.text("No Dataset could be queried") + return + + for n, student in enumerate(students, start=1): + display = f"{n}. {student.prename} {student.surname}" + opened, _ = imgui.selectable(display, self.select == n-1) + if opened: + self.select = n-1 + + return students[self.select] + + diff --git a/submission_editor.py b/submission_editor.py new file mode 100644 index 0000000..9a3c728 --- /dev/null +++ b/submission_editor.py @@ -0,0 +1,67 @@ +import imgui + +from model import * + +class SubmissionEditor: + def __init__(self): + super().__init__() + self.current_lecture = 0 + self.current_student = 0 + self.points = 0 + + def __call__(self, clas: Class): + id = clas.id if clas else None + lectures = Lecture.select().where(Lecture.class_id == id) if id else None + students = Student.select().where(Student.class_id == id) if id else None + + with imgui.begin("Submission Editor", False, imgui.WINDOW_NO_MOVE | imgui.WINDOW_NO_RESIZE | imgui.WINDOW_NO_COLLAPSE): + imgui.text("Add Submission") + + if not lectures: + imgui.text("No Lectures queried") + return + + if not students: + imgui.text("No Students queried") + return + + _, self.current_lecture = imgui.combo("Lecture", self.current_lecture, [f"{l.title} ({l.points})" for l in lectures]) + _, self.current_student = imgui.combo("Student", self.current_student, [f"{s.prename} {s.surname}" for s in students]) + + if self.points < 0: + self.points = 0 + + max = lectures[self.current_lecture].points + if self.points > max: + self.points = max + + _, self.points = imgui.input_float("Points", self.points, format='%.1f', step=0.5, step_fast=1.0) + + if imgui.button("Add"): + if not Submission.select().where( + Submission.student_id == students[self.current_student].id and + Submission.lecture_id == lectures[self.current_lecture].id + ): + Submission.create( + student_id=students[self.current_student].id, + lecture_id=lectures[self.current_lecture].id, + points=self.points + ) + + imgui.same_line() + + if imgui.button("Update"): + submission = Submission.select().where( + Submission.student_id == students[self.current_student].id and + Submission.lecture_id == lectures[self.current_lecture].id + ).get() + submission.points = self.points + submission.save() + + imgui.same_line() + + if imgui.button("Delete"): + Submission.delete().where( + Submission.student_id == students[self.current_student].id and + Submission.lecture_id == lectures[self.current_lecture].id + ).execute() diff --git a/test.db b/test.db new file mode 100644 index 0000000..f80bd51 Binary files /dev/null and b/test.db differ diff --git a/view.py b/view.py new file mode 100644 index 0000000..24ca2f2 --- /dev/null +++ b/view.py @@ -0,0 +1,86 @@ +import imgui + +from datatypes import * + +from main_menu import MainMenu + +from student_editor import StudentEditor +from student_list import StudentList +from student_info import StudentInfo +from class_editor import ClassEditor +from lecture_editor import LectureEditor +from submission_editor import SubmissionEditor + +from student_graph import StudentGraph + +def set_layout(size: tuple, pos: tuple) -> None: + io = imgui.get_io() + imgui.set_next_window_size( + io.display_size.x*size[0], + io.display_size.y*size[1] + ) + imgui.set_next_window_position( + io.display_size.x*pos[0], + io.display_size.y*pos[1] + ) + +class GrapherLayout: + def __init__(self): + super().__init__() + + self.student_graph = StudentGraph() + + def __call__(self): + set_layout((0.7, 0.4), (0.1, 0.1)) + self.student_graph() + +class EditorLayout: + def __init__(self): + super().__init__() + self.io = imgui.get_io() + + self.student_editor = StudentEditor() + self.student_list = StudentList() + self.student_info = StudentInfo() + self.class_editor = ClassEditor() + self.lecture_editor = LectureEditor() + self.submission_editor = SubmissionEditor() + + def __call__(self): + set_layout((0.3, 0.3), (0.7, 0.4)) + clas = self.class_editor() + + set_layout((0.5, 0.3), (0.2, 0.4)) + self.student_editor() + + set_layout((0.2, 0.98), (0, 0.02)) + student = self.student_list(clas) + + set_layout((0.3, 0.38), (0.7, 0.02)) + self.student_info(student) + + set_layout((0.5, 0.3), (0.2, 0.7)) + self.lecture_editor(clas) + + set_layout((0.5, 0.3), (0.2, 0.1)) + self.submission_editor(clas) + +class View: + def __init__(self): + super().__init__() + self.io = imgui.get_io() + self.current = LayoutOptions.GRAPHER + self.main_menu = MainMenu() + self.editor = EditorLayout() + self.grapher = GrapherLayout() + + def __call__(self): + option = self.main_menu() + if option: + self.current = option + + if self.current == LayoutOptions.EDITOR: + self.editor() + + if self.current == LayoutOptions.GRAPHER: + self.grapher()