Added: Layout Switcher
This commit is contained in:
parent
59e00cb812
commit
7635df42b4
36
class_editor.py
Normal file
36
class_editor.py
Normal file
@ -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]
|
15
datatypes.py
Normal file
15
datatypes.py
Normal file
@ -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
|
89
gui.py
Normal file
89
gui.py
Normal file
@ -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()
|
45
lecture_editor.py
Normal file
45
lecture_editor.py
Normal file
@ -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]
|
250
main.py
250
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()
|
||||
|
||||
|
26
main_menu.py
Normal file
26
main_menu.py
Normal file
@ -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
|
61
model.py
Normal file
61
model.py
Normal file
@ -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))
|
42
student_editor.py
Normal file
42
student_editor.py
Normal file
@ -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()
|
27
student_graph.py
Normal file
27
student_graph.py
Normal file
@ -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")
|
||||
|
60
student_info.py
Normal file
60
student_info.py
Normal file
@ -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)
|
28
student_list.py
Normal file
28
student_list.py
Normal file
@ -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]
|
||||
|
||||
|
67
submission_editor.py
Normal file
67
submission_editor.py
Normal file
@ -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()
|
86
view.py
Normal file
86
view.py
Normal file
@ -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()
|
Loading…
Reference in New Issue
Block a user