Center planning page#

Will only be reachable for authenticated users and planner for the selected center.

from myFasthtml import *
from datetime import date
import libs.utils as utils

<<this-center-courses>>
<<obtain-durations>>
<<check-complete-plan>>
def check_plan(session, plan, selected_name, centers):
    types_with_duration = get_types_with_duration(centers[selected_name])    
    session['planOK'] = True
    for idx, row in enumerate(plan):
        if idx == len(plan) - 1:
            row["check"] = "OK"
            continue
        next_start_date = plan[idx + 1].get("start_date")
        pt = row.get("period_type")
        try:
            e_this = date.fromisoformat(row.get("end_date"))
            s_next = date.fromisoformat(next_start_date) 
            delta_days = (s_next - e_this).days 
        except Exception:
            row["check"] = "InvalidDate"
        else:
            if not pt in get_period_types_in_db(centers[selected_name]):
                row["check"] = "NoType"
            elif row.get("start_date") == next_start_date and pt == plan[idx + 1].get("period_type"):
                row["check"] = "Duplicated periods"
            elif row.get("start_date") == next_start_date:
                row["check"] = "Same starting date"
            elif delta_days < 0:
                row["check"] = f"Overlap of {- delta_days} day(s)"
            elif delta_days == 0:
                this_end_time = next((t.get("time_end_last_day") for t in types_with_duration
                                    if t.get("period_type") == pt), None)
                next_pt = plan[idx + 1].get("period_type")
                next_start_time = next((t.get("time_start_first_day") for t in types_with_duration
                                        if t.get("period_type") == next_pt), None)
                if this_end_time > next_start_time:
                    row["check"] = "CHECK Time overlap"
                else:
                    row["check"] = "OK same day"
            elif delta_days > 1:
                row["check"] = f"OK default {delta_days} days"
            else:
                row["check"] = "OK"
        if not (row["check"].startswith("OK") or row["check"].startswith("CHECK")):
            session['planOK'] = False
    return plan
def add_end_dates(plan, center_obj):
    types_with_duration = get_types_with_duration(center_obj)
    for i in range(len(plan)):
        if 'end_date' not in plan[i] or plan[i]['end_date'] is None:
            this_type = list(filter(lambda x: x.get("period_type") == plan[i]['period_type'], types_with_duration))[0]
            if this_type["tags"] != "F":
                if i < len(plan) - 1:
                    next_start = plan[i+1].get('start_date')
                    plan[i]['end_date'] = utils.add_months_days(next_start, 0, -1)
                else:
                    plan[i]['end_date'] = utils.add_months_days(plan[i]['start_date'], 0, 1)
            else:
                plan[i]['end_date'] = utils.add_months_days(plan[i]['start_date'], 0, this_type.get("duration") -1)
    return plan

def coming_center_courses(center_obj):
    #center = center_obj.center_name
    selected_db = center_obj.gong_db_name
    db_center = database(utils.get_db_path() + selected_db)

    periods = db_center.t.coming_periods
    Period = periods.dataclass()
    count_past = sum(1 for item in periods() if date.fromisoformat(item.start_date) < date.today())
    date_current_course = periods()[count_past-1].start_date  ## [2]

    periods_db_center_obj = periods()[count_past-1:]  ## [3]
    periods_db_center = [
        {
            'start_date': p.start_date,
            'period_type': p.period_type,
            'source' : f"{center_obj.center_name.lower()}_db"

        }
        for p in periods_db_center_obj  ## [3]
    ]
    sorted_periods = sorted(periods_db_center, key=lambda x: x['start_date'])
    sorted_periods_ends = add_end_dates(sorted_periods, center_obj)
    return sorted_periods_ends, date_current_course
def get_dhamm_org_types_list():
    db_path = utils.get_db_path()
    df = pd.read_csv(db_path + 'course_type_map.csv')
    return df.to_dict(orient='records')

def get_period_types_in_db(center_obj):
    selected_db = center_obj.gong_db_name
    db_center = database(utils.get_db_path() + selected_db)
    period_types_in_db = set(row.get("period_type") for row in list(db_center.t.periods_struct()))
    return period_types_in_db

def get_types_with_duration(center_obj):
    list_of_types = get_dhamm_org_types_list()
    selected_db = center_obj.gong_db_name
    db_center = database(utils.get_db_path() + selected_db)
    period_types_in_db = get_period_types_in_db(center_obj)
    for item in list_of_types:
        vt = item.get("period_type")
        if vt not in period_types_in_db:
            continue
        days = [row.get("day") for row in list(db_center.t.periods_struct())
                if row.get("period_type") == vt and row.get("day") is not None]
        if item["tags"] == "F":
            item["duration"] = max(days) + 1
        else:
            item["duration"] = 0
        day_0_type = next((row.get("day_type") for row in list(db_center.t.periods_struct())
                 if row.get("period_type") == vt and row.get("day") == 0), None)
        times_day_0 = [row.get("time") for row in list(db_center.t.timetables())
                 if row.get("period_type") == vt and row.get("day_type") == day_0_type] 
        item["time_start_first_day"] = min(times_day_0)
        last_day_type = next((row.get("day_type") for row in list(db_center.t.periods_struct())
                 if row.get("period_type") == vt and row.get("day") == max(days)), None)
        times_last_day = [row.get("time") for row in list(db_center.t.timetables())
                 if row.get("period_type") == vt and row.get("day_type") == last_day_type] 
        item["time_end_last_day"] = max(times_last_day)
    return list_of_types