# Custom from model import * from appstate import AppState from grader.valuation import * # External from imgui_bundle import ( imgui, immapp, hello_imgui, imgui_md, implot, implot3d, immvision, ImVec2, ImVec4, im_file_dialog ) from PIL import ImageColor import numpy as np from numpy.typing import NDArray # Built In from typing import List, Any @immapp.static(inited=False) def select_class(app_state: AppState) -> None: statics = select_class if not app_state.current_class_id: imgui.text("No class found in Database") return if not statics.inited: statics.select = 0 statics.classes = Class.select() statics.labels = [c.name for c in statics.classes] statics.inited = True changed, statics.select = imgui.combo("Classes", statics.select, statics.labels) if changed: app_state.current_class_id = statics.classes[statics.select].id @immapp.static(inited=False) def select_student(app_state: AppState) -> None: statics = select_student if not app_state.current_class_id: return if not statics.inited: statics.select = 0 statics.students = Student.select().where(Student.class_id == app_state.current_class_id) statics.inited = True if not statics.students: imgui.text("No Studends found") return for n, student in enumerate(statics.students, start=1): display = f"{n}. {student.prename} {student.surname}" _, clicked = imgui.selectable(display, statics.select == n-1) if clicked: statics.select = n-1 app_state.current_student_id = statics.students[statics.select].id @immapp.static(inited=False) def student_list(app_state: AppState) -> None: statics = student_list select_class(app_state) imgui.separator() select_student(app_state) def plot_bar_line_percentage(data: np.array, labels: list, avg: float) -> None: if not data.size > 0: imgui.text("No Data available") return name = hash(avg) if avg.is_integer(): avg = int(avg) avg = np.ones(len(data)) * avg w, h = imgui.get_window_size() implot.push_colormap(implot.Colormap_.hot.value) if implot.begin_plot(f"Performance##{name}", ImVec2(-1, h*0.4), implot.Flags_.no_mouse_text.value | implot.Flags_.no_inputs.value): implot.setup_axes("Lectures", "Percentage") implot.setup_axes_limits(-1, len(data), 0, 110) implot.push_style_var(implot.StyleVar_.fill_alpha.value, 0.6) implot.push_style_var(implot.StyleVar_.line_weight.value, 3) implot.setup_axis_ticks(implot.ImAxis_.x1.value, 0, len(labels), len(labels), [" " for _ in labels], False) implot.plot_bars("Submissions", data) implot.plot_line("Average", avg) implot.push_style_color(implot.Col_.inlay_text, ImVec4(190,190,40,255)/255) for x_pos, label in enumerate(labels): y_pos = 50 implot.plot_text(label, x_pos, y_pos//2, ImVec2(0,0), implot.TextFlags_.vertical.value) implot.pop_style_color() implot.pop_style_var() implot.end_plot() 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")]) COLOR_TEXT_PROJECT = tuple([e/255 for e in ImageColor.getcolor("#0A9CF5","RGBA")]) @immapp.static(inited=False) def student_graph(app_state: AppState) -> None: statics = student_graph if not statics.inited: statics.id = -1 statics.student = None statics.group = None statics.lectures = None statics.points = None statics.sub_points = None statics.subs_data = None statics.subs_labels = None statics.max_points = None statics.avg = None statics.inited = True if id != app_state.current_student_id: statics.id = app_state.current_student_id if not app_state.current_student_id: imgui.text("No Students in Database") return statics.student = Student.get_by_id(app_state.current_student_id) submissions = Submission.select().where(Submission.student_id == statics.student.id) statics.group = Group.get_by_id(statics.student.group_id) statics.lectures = [Lecture.get_by_id(sub.lecture_id) for sub in submissions] statics.max_points = np.sum([l.points for l in statics.lectures]) statics.sub_points = [sub.points for sub in submissions] statics.points = np.sum(statics.sub_points) if statics.points.is_integer(): statics.points = int(statics.points) statics.grader = get_grader("Oberstufe") #statics.grader = get_grader(statics.student.grader) statics.subs_data = np.array([p/mp.points for p, mp in zip(statics.sub_points, statics.lectures)], dtype=np.float32)*100 statics.subs_labels = [f"{l.title} {int(points) if points.is_integer() else points}/{l.points}" for l, points in zip(statics.lectures, statics.sub_points)] statics.avg = statics.points/statics.max_points*100 w, h = imgui.get_window_size() imgui_md.render(f"# {statics.student.prename} {statics.student.surname} ({statics.group.name})") imgui_md.render(f"### {statics.points}/{statics.max_points} ({statics.student.grader})") imgui.text(" ") imgui.progress_bar(statics.points/statics.max_points, ImVec2(w*0.9, h*0.05), f"{statics.points}/{statics.max_points} {statics.points/statics.max_points:.1%}") plot_bar_line_percentage(statics.subs_data, statics.subs_labels, statics.avg) imgui.separator() imgui.text_colored(COLOR_TEXT_PROJECT, f"{statics.group.name}: {statics.group.project}") for n, data in enumerate(zip(statics.lectures, statics.sub_points), start=1): lecture, points = data COLOR = statics.grader.get_grade_color(points, lecture.points) imgui.text_colored(COLOR, f"{n}. {lecture.title} {points}/{lecture.points} ({statics.grader.get_grade(points, lecture.points)}) ") @immapp.static(inited=False) def sex_graph(app_state: AppState) -> None: statics = sex_graph if db.is_closed(): imgui.text("No Database loaded") return if not statics.inited: statics.max_points = None statics.male_points = None statics.female_points = None statics.male_percentage = None statics.female_percentage = None statics.male_labels = None statics.female_labels = None statics.lectures = None statics.class_id = -1 statics.state = 0 statics.inited = True if not app_state.current_class_id: imgui.text("No Class found") return if statics.class_id != app_state.current_class_id: statics.class_id = app_state.current_class_id lectures = Lecture.select().where(Lecture.class_id == statics.class_id) statics.lectures = lectures statics.max_points = np.empty(len(lectures)) statics.male_points = np.empty(len(lectures)) statics.female_points = np.empty(len(lectures)) for n, lecture in enumerate(lectures): statics.max_points[n] = lecture.points # Acc points m_count = 0 f_count = 0 m_points = 0 f_points = 0 for sub in Submission.select().where(Submission.lecture_id == lecture.id): if Student.get_by_id(sub.student_id).sex == 'Male': m_points += sub.points m_count += 1 else: f_points += sub.points f_count += 1 statics.male_points[n] = m_points/m_count/lecture.points statics.female_points[n] = f_points/f_count/lecture.points statics.male_percentage = np.sum(statics.male_points)/len(statics.male_points) statics.female_percentage = np.sum(statics.female_points)/len(statics.female_points) statics.male_labels = [f"{l.title} | {points:.1%}" for l, points in zip(lectures, statics.male_points)] statics.female_labels = [f"{l.title} | {points:.1%}" for l, points in zip(lectures, statics.female_points)] w, h = imgui.get_window_size() if statics.state == 0: imgui_md.render("# Male") imgui.progress_bar(statics.male_percentage, ImVec2(w*0.9, h*0.05), f"{statics.male_percentage:.1%} n={len(Student.select().where(Student.sex == 'Male'))}") plot_bar_line_percentage(statics.male_points*100, statics.male_labels, statics.male_percentage*100) imgui_md.render("# Female") imgui.progress_bar(statics.female_percentage, ImVec2(w*0.9, h*0.05), f"{statics.female_percentage:.1%} n={len(Student.select().where(Student.sex == 'Female'))}") plot_bar_line_percentage(statics.female_points*100, statics.female_labels, statics.female_percentage*100) if statics.state == 1: male_xs = np.arange(1, len(statics.male_points)+1, dtype=np.float32) male_ys = male_xs*0 male_zs = np.array(statics.male_points*100, dtype=np.float32) female_xs = np.arange(1, len(statics.female_points)+1, dtype=np.float32) female_ys = np.ones(len(statics.female_points), dtype=np.float32) female_zs = np.array(statics.female_points*100, dtype=np.float32) if implot3d.begin_plot("3D Gender Plot", ImVec2(w*0.9, h*0.9)): implot3d.setup_axes("Lecture", "Gender", "Percentage") implot3d.setup_box_scale(1.1, 1.1, 1.1) implot3d.setup_axes_limits(0, len(statics.male_points)+1, -0.2, 1.2, 0, 110) implot3d.set_next_marker_style(implot3d.Marker_.square.value) implot3d.plot_line("Male", male_xs, male_ys, male_zs) implot3d.set_next_marker_style(implot3d.Marker_.circle.value) implot3d.plot_line("Female", female_xs, female_ys, female_zs) for n, l in enumerate(statics.lectures, start=1): implot3d.plot_text(l.title, n, np.clip(n/len(statics.lectures)), 50, np.pi/4) implot3d.end_plot() if imgui.button("Change 2D/3D"): statics.state = not statics.state COLOR_TEXT_FIRST = tuple([e/255 for e in ImageColor.getcolor("#FFCF40","RGBA")]) COLOR_TEXT_SECOND = tuple([e/255 for e in ImageColor.getcolor("#A5A9B4","RGBA")]) COLOR_TEXT_THIRD = tuple([e/255 for e in ImageColor.getcolor("#97564A","RGBA")]) COLOR_TEXT_ELSE = tuple([e/255 for e in ImageColor.getcolor("#85EBD9","RGBA")]) @immapp.static(inited=False) def ranking(app_state: AppState) -> None: statics = ranking if not statics.inited: statics.id = -1 statics.rank = None statics.state = 1 statics.inited = True imgui_md.render("# Student Ranking") if not app_state.current_class_id: imgui.text("No class selected") return if statics.id != app_state.current_class_id: statics.id = app_state.current_class_id students = Student.select().where(Student.class_id == statics.id) max_points = np.sum([l.points for l in Lecture.select().where(Lecture.class_id == statics.id)]) statics.rank = list() for student in students: points = list() for sub in Submission.select().where(Submission.student_id == student.id): points.append(sub.points) statics.rank.append((student, sum(points)/max_points)) statics.rank = sorted(statics.rank, key=lambda item: item[1], reverse=True) if statics.state == 0: for n, data in enumerate(statics.rank, start=1): student, points = data display = f"{n}. {student.prename} {student.surname} {points:.1%}" COLOR = COLOR_TEXT_ELSE if n == 1: COLOR = COLOR_TEXT_FIRST if n == 2: COLOR = COLOR_TEXT_SECOND if n == 3: COLOR = COLOR_TEXT_THIRD imgui.text_colored(COLOR, display) if statics.state == 1: w, h = imgui.get_window_size() if implot.begin_plot("Ranking", ImVec2(w*0.9, h*0.9)): implot.plot_bars("Ranking", np.array([p[1] for p in reversed(statics.rank)])*100, 0.67, 0, implot.BarsFlags_.horizontal.value) for n, s in enumerate(reversed(statics.rank)): student = s[0] implot.plot_text(f"{student.prename} {student.surname}", s[1]*50, n) implot.end_plot() if imgui.button("Change"): statics.state = not statics.state def analyzer_docking_splits() -> List[hello_imgui.DockingSplit]: split_main_misc = hello_imgui.DockingSplit() split_main_misc.initial_dock = "MainDockSpace" split_main_misc.new_dock = "MiscSpace" split_main_misc.direction = imgui.Dir.down split_main_misc.ratio = 0.25 # Then, add a space to the left which occupies a column whose width is 25% of the app width split_main_command = hello_imgui.DockingSplit() split_main_command.initial_dock = "MainDockSpace" split_main_command.new_dock = "CommandSpace" split_main_command.direction = imgui.Dir.left split_main_command.ratio = 0.2 # Then, add CommandSpace2 below MainDockSpace split_main_command2 = hello_imgui.DockingSplit() split_main_command2.initial_dock = "MainDockSpace" split_main_command2.new_dock = "CommandSpace2" split_main_command2.direction = imgui.Dir.down split_main_command2.ratio = 0.25 splits = [split_main_misc, split_main_command, split_main_command2] return splits def set_analyzer_layout(app_state: AppState) -> List[hello_imgui.DockableWindow]: student_selector = hello_imgui.DockableWindow() student_selector.label = "Students" student_selector.dock_space_name = "CommandSpace" student_selector.gui_function = lambda: student_list(app_state) student_info = hello_imgui.DockableWindow() student_info.label = "Student Analyzer" student_info.dock_space_name = "MainDockSpace" student_info.gui_function = lambda: student_graph(app_state) sex_info = hello_imgui.DockableWindow() sex_info.label = "Analyze by Gender" sex_info.dock_space_name = "MainDockSpace" sex_info.gui_function = lambda: sex_graph(app_state) student_ranking = hello_imgui.DockableWindow() student_ranking.label = "Ranking" student_ranking.dock_space_name = "MainDockSpace" student_ranking.gui_function = lambda: ranking(app_state) return [ student_selector, student_info, sex_info, student_ranking, ] def analyzer_layout(app_state: AppState) -> hello_imgui.DockingParams: docking_params = hello_imgui.DockingParams() docking_params.layout_name = "Analyzer" docking_params.docking_splits = analyzer_docking_splits() docking_params.dockable_windows = set_analyzer_layout(app_state) return docking_params