system-pri/backend/app/coordinator/utils.py

210 lines
7.4 KiB
Python

import copy
import json
from collections import defaultdict
from datetime import datetime, timedelta
from io import BytesIO
from typing import Generator, Union, Any, List, Tuple
from pathlib import Path
import pandas as pd
from flask import current_app
from reportlab.lib import colors
from reportlab.lib.enums import TA_CENTER
from reportlab.lib.styles import getSampleStyleSheet
from reportlab.lib.units import mm, inch
from reportlab.platypus import SimpleDocTemplate, Paragraph, PageBreak, Table
from reportlab.pdfbase import pdfmetrics
from reportlab.pdfbase.ttfonts import TTFont
from werkzeug.datastructures import FileStorage
from .exceptions import InvalidNameOrTypeHeaderException
from ..students.models import Student, Group, ProjectGradeSheet
from ..examination_schedule.models import TermOfDefence
def check_columns(df: pd.DataFrame) -> bool:
headers = set(df.keys().values)
columns = ['NAZWISKO', 'IMIE', 'INDEKS', 'PESEL', 'EMAIL']
if len(headers - set(columns)) != 0:
return False
flag = True
col_types = ['object', 'object', 'int', 'float64', 'object']
for name, col_type in zip(columns, col_types):
if not str(df.dtypes[name]).startswith(col_type):
flag = False
break
return flag
def parse_csv(file: FileStorage) -> Generator[Student, Any, None]:
df = pd.read_csv(file)
if not check_columns(df):
raise InvalidNameOrTypeHeaderException
students = (Student(last_name=dict(item.items())['NAZWISKO'],
first_name=dict(item.items())['IMIE'],
index=dict(item.items())['INDEKS'],
pesel=str(int(dict(item.items())['PESEL'])) if not pd.isna(
dict(item.items())['PESEL']) else None,
email=dict(item.items())['EMAIL'])
for _, item in df.iterrows())
return students
def generate_csv(students_and_groups: List[Tuple[Student, Group]]) -> str:
headers = ['PESEL', 'INDEKS', 'IMIE', 'NAZWISKO', 'EMAIL', 'CDYD_KOD', 'PRZ_KOD', 'TZAJ_KOD', 'GR_NR', 'PRG_KOD']
data = [(student.pesel, student.index, student.first_name, student.last_name, student.email,
group.cdyd_kod, group.prz_kod, group.tzaj_kod, group.project_supervisor_id,
None) for student, group in students_and_groups]
dataframe = defaultdict(list)
for row in data:
for idx, item in enumerate(row):
dataframe[headers[idx]].append(item)
df = pd.DataFrame(dataframe)
return df.to_csv(index=False)
def generate_range_dates(start_date: datetime, end_date: datetime, step_in_minutes: int) -> \
Generator[Union[datetime, timedelta], Any, None]:
current_date = copy.copy(start_date)
while True:
next_date = current_date + timedelta(minutes=step_in_minutes)
if next_date > end_date:
break
yield current_date
current_date = copy.copy(next_date)
def generate_examination_schedule_pdf_file(title: str, nested_term_of_defences: List[List[TermOfDefence]],
base_dir: Path) -> bytes:
pagesize = (297 * mm, 210 * mm)
headers = ["lp.", "Godzina", "Nazwa projektu", "Opiekun", "Zespol", "Komisja", "Uwagi"]
pdf_buffer = BytesIO()
my_doc = SimpleDocTemplate(
pdf_buffer,
pagesize=pagesize,
topMargin=1 * inch,
leftMargin=1 * inch,
rightMargin=1 * inch,
bottomMargin=1 * inch,
title=title
)
pdfmetrics.registerFont(TTFont('Lato', base_dir / 'fonts' / 'Lato.ttf'))
style = getSampleStyleSheet()
bodyText = style['BodyText']
bodyText.fontName = 'Lato'
normal = style["Heading1"]
normal.alignment = TA_CENTER
flowables = []
# print(nested_enrollments)
for term_of_defences in nested_term_of_defences:
if len(term_of_defences) == 0:
continue
date = datetime.strftime(term_of_defences[0].start_date.date(), '%d.%m.%Y')
paragraph_1 = Paragraph(f"{title} ~ {date}", normal)
flowables.append(paragraph_1)
data = [headers]
for idx, td in enumerate(term_of_defences, start=1):
new_date = td.start_date + timedelta(hours=2)
group_name = td.group.name if td.group is not None else ""
if group_name != '':
ps = td.group.project_supervisor
project_supervisor_fullname = f"{ps.first_name[0]}. {ps.last_name}"
students = td.group.students
# print(students)
team = ", ".join([f"{s.first_name} {s.last_name}" for s in students])
else:
project_supervisor_fullname = ""
team = ""
members = td.members_of_committee
# print(members)
if len(members) == 0:
committee = ''
else:
members_iter = (f"{m.first_name[0]} {m.last_name}" for m in members)
committee = ", ".join(members_iter)
data.append([str(idx), new_date.strftime("%H:%M"),
Paragraph(group_name, bodyText),
Paragraph(project_supervisor_fullname, bodyText),
Paragraph(team, bodyText),
Paragraph(committee, bodyText),
])
# print(data)
table = Table(data=data,
style=[
('GRID', (0, 0), (-1, -1), 0.5, colors.black),
('BACKGROUND', (0, 0), (-1, 0), colors.HexColor("#A6F1A6")),
('BACKGROUND', (0, 0), (1, -1), colors.HexColor("#A6F1A6"))
],
colWidths=[0.25 * inch, 0.7 * inch, 1.6 * inch, 1.5 * inch, 2.5 * inch, 2.2 * inch, 2 * inch]
)
flowables.append(table)
flowables.append(PageBreak())
my_doc.build(flowables)
pdf_value = pdf_buffer.getvalue()
pdf_buffer.close()
return pdf_value
def load_weight_for_project_grade_sheet() -> Union[dict, None]:
base_dir = current_app.config.get('BASE_DIR')
config_dir = base_dir / "config"
with open(config_dir / "weights_project_grade_sheet.json") as f:
data = json.load(f)
return data
def calculate_points_for_one_term(weights: dict, project_grade_sheets: List[ProjectGradeSheet]) -> list:
terms = []
for pgs in project_grade_sheets:
if pgs is None:
terms.append((0, 0))
continue
first_term_points = {
'nominator': 0,
'denominator': 0,
}
second_term_points = {
'nominator': 0,
'denominator': 0,
}
for weight_key, weight_value in weights.items():
points = first_term_points if weight_key.endswith('1') else second_term_points
try:
attribute_value = getattr(pgs, weight_key)
except AttributeError:
attribute_value = 0
points['nominator'] += attribute_value * weight_value * 1 / 4
points['denominator'] += weight_value
try:
fp = first_term_points['nominator'] / first_term_points['denominator']
except ZeroDivisionError:
fp = 0
try:
sp = second_term_points['nominator'] / second_term_points['denominator']
except ZeroDivisionError:
sp = 0
terms.append((round(fp, 2)*100, round(sp, 2)*100))
return terms