Exemple #1
0
def toelichting_block(toelichtingen):
    if not toelichtingen:
        return
    toelichting_grid = Grid(cols=2)
    for t in toelichtingen:
        toelichting_grid.add_row([TextBlock(t[0], style=BOLD), TextBlock(t[1], style=ITALIC)])
    return VBlock([TextBlock(f'Toelichting', style=BOLD), toelichting_grid])
Exemple #2
0
def render_vrije_dagen_page(output_folder: Path):

    table = VBlock([
        Table(
            vrije_dagen_overzicht(),
            TableConfig(
                headers=[
                    'Naam', 'Vorig jaar', 'Dit jaar nieuw',
                    'Dit jaar beschikbaar', 'Totaal', 'Pool'
                ],
                aligns=['left', 'right', 'right', 'right', 'right', 'right'],
                formats=['', '.5', '.5', '.5', '.5', '.5'],
                totals=[0, 0, 0, 0, 0, 1],
            ),
        ),
    ])

    page = Page([
        TextBlock('Vrije dagen', HEADER_SIZE),
        table,
        TextBlock(
            f'Pool = Dagen van vorig jaar + Dagen dit jaar * deel van het jaar dat is geweest ({fraction_of_the_year_past()*100:.0f}%).'
        ),
    ])
    page.render(output_folder / 'freedays.html')
Exemple #3
0
def planning_chart():
    # Vulling van de planning uit de planning database
    vulling = vulling_van_de_planning()
    if not vulling:
        return TextBlock("Kon de planning niet ophalen", color=RED)
    xy_values = [{"x": a["monday"], "y": a["filled"]} for a in vulling]
    return VBlock([
        TextBlock("Planning", MID_SIZE),
        TextBlock(
            f"Percentage gevuld met niet interne projecten<br/>de komende {len(xy_values)} weken.",
            color=GRAY,
        ),
        ScatterChart(
            xy_values,
            ChartConfig(
                width=250,
                height=150,
                colors=["#6666cc", "#ddeeff"],
                x_type="int",
                min_x_axis=xy_values[0]["x"],
                max_x_axis=xy_values[-2]["x"],
                min_y_axis=0,
                max_y_axis=100,
            ),
        ),
    ])
Exemple #4
0
 def add_normal_row(title, value, shift=False, value_color=None):
     row = [TextBlock(title)]
     value_text = TextBlock(value, text_format='.', color=value_color)
     if shift:
         row += ['', value_text]
     else:
         row += [value_text, '']
     grid.add_row(row)
Exemple #5
0
 def add_subtotal_row(title, value, style=TOPLINE):
     grid.add_row(
         [
             TextBlock(title, style=BOLD),
             '',
             TextBlock(value, text_format='.', style=BOLD),
         ],
         styles=['', style, style],
     )
Exemple #6
0
 def add_subtotal_row(title, subtotal, style=TOPLINE):
     grid.add_row(
         [
             TextBlock(title, style=BOLD),
             '',
             TextBlock(subtotal[0], text_format='.', style=BOLD),
             '',
             '',
             TextBlock(subtotal[1], text_format='.', style=BOLD, color="GRAY"),
         ],
         styles=['', style, style, '', style, style],
     )
Exemple #7
0
def operations_block():
    return VBlock([
        TextBlock("Operations", HEADER_SIZE),
        TextBlock("KPI's", MID_SIZE),
        HBlock([kpi_block(verbose=False)], link="operations.html", padding=40),
        TextBlock(""),  # Todo: verticale marge mogelijk maken
        operations_chart(),
        TextBlock(""),  # Todo: verticale marge mogelijk maken
        # billable_chart(),
        corrections_block(),
        planning_chart(),
    ])
Exemple #8
0
def error_block():
    errs = log.get_errors()
    if not errs:
        return None
    error_lines = [
        TextBlock(
            f'<b>{err["file"]}, {err["function"]}</b><br/>{err["message"]}',
            DEF_SIZE,
            width=260,
            color=RED,
        ) for err in errs
    ]
    return VBlock([TextBlock("Errors", MID_SIZE)] + error_lines)
Exemple #9
0
def render_billable_page(output_folder: Path):
    users = sorted(tuple_of_productie_users())
    fromday = Day().plus_months(-6)
    period = Period(fromday)
    cols = 3
    rows = len(users) // cols + 1
    grid = Grid(rows, cols)
    row = 0
    col = 0
    for user in users:
        labels, hours = billable_trend_person_week(user, period)  # {weekno: hours} dict
        hours_data = HoursData(period, [user])
        chart = BarChart(
            hours,
            ChartConfig(
                width=400,
                height=220,
                colors=['#ddeeff'],
                bottom_labels=labels,
                max_y_axis=40,
                y_axis_max_ticks=5,
            ),
        )
        user_block = VBlock(
            [
                TextBlock(
                    f'{user} {hours_data.effectivity():.0f}%  / {hours_data.billable_perc():.0f}%', font_size=MID_SIZE
                ),
                chart,
            ]
        )
        grid.set_cell(row, col, user_block)
        col += 1
        if col == cols:
            col = 0
            row += 1

    page = Page(
        [
            TextBlock('Billable uren', HEADER_SIZE),
            TextBlock(
                'Billable uren per week het afgelopen halfjaar.<br/><br/>'
                + 'Grafiek toont uren gewerkt op billable projecten zonder rekening te houden met correcties.<br/>'
                + 'Percentages zijn effectiviteit en billable.',
                color="gray",
            ),
            grid,
        ]
    )
    page.render(output_folder / 'billable.html')
Exemple #10
0
def travelbase_block():
    bookings = get_bookings_per_week(booking_type="bookings",
                                     only_complete_weeks=True)
    if not isinstance(bookings, pd.DataFrame):
        return TextBlock("Kon boekingen niet ophalen", color=RED)
    legend = ", ".join(
        [f"{brand}: {int(bookings[brand].sum())}" for brand in BRANDS])
    return VBlock(
        [
            TextBlock("Travelbase", MID_SIZE),
            TextBlock("Aantal boekingen per week", color=GRAY),
            travelbase_scatterchart(bookings, 250, 180),
            TextBlock(legend),
        ],
        link="travelbase.html",
    )
Exemple #11
0
def finance_block():
    return VBlock([
        TextBlock("Finance", HEADER_SIZE),
        omzet_chart(),
        debiteuren_block(),
        cash_block(),
    ])
Exemple #12
0
 def add_normal_row(title, result):
     grid.add_row(
         [
             TextBlock(title),
             TextBlock(result[0], text_format='.'),
             '',
             '',
             TextBlock(result[1], text_format='.', color="GRAY"),
             '',
         ]
     )
     if toelichting_sheet:
         toelichting = toelichting_sheet[title, 'Toelichting']
         if toelichting:
             b = toelichtingen  # zeer merkwaardige constructie maar krijg een foutmelding als ik direct iets toevoeg aan toelichtingen
             b += [(title, toelichting)]
Exemple #13
0
def omzet_chart():
    # Behaalde omzet per week
    return VBlock(
        [
            TextBlock('Omzet'),
            TextBlock("per week, laatste 6 maanden...", DEF_SIZE, color=GRAY),
            TrendLines().chart(
                "omzet_per_week",
                250,
                150,
                x_start=months_ago(6),
                min_y_axis=0,
                max_y_axis=80000,
            ),
        ],
        link="billable.html",
    )
Exemple #14
0
def render_onderhanden_werk_page(output_folder: Path):
    day = Day()
    page = Page([
        TextBlock(f'Onderhanden werk per {day.strftime("%d/%m")}',
                  HEADER_SIZE),
        onderhanden_werk_list(day)
    ])
    page.render(output_folder / 'onderhanden.html')
Exemple #15
0
def hr_block():
    return VBlock([
        TextBlock("HR", HEADER_SIZE),
        team_block(),
        tevredenheid_block(),
        verzuim_block(),
        # vakantiedagen_block(),
        error_block(),
    ])
Exemple #16
0
def render_maandrapportage_page(monthly_folder, output_folder: Path):
    lines = []
    files = sorted(
        [f for f in monthly_folder.iterdir() if f.suffix == '.html'])
    for file in files:
        month_num = int(file.stem.split('_')[1])
        htmlpath = monthly_folder / file
        pdfpath = os.path.relpath(htmlpath.with_suffix('.pdf'),
                                  start=output_folder)
        htmlpath = os.path.relpath(htmlpath, start=output_folder)
        lines += [
            HBlock([
                TextBlock(MAANDEN[month_num - 1], url=htmlpath),
                TextBlock('pdf', url=pdfpath)
            ])
        ]
    page = Page([TextBlock('Maandrapportages', HEADER_SIZE)] + lines)
    page.render(output_folder / 'maandrapportages.html')
Exemple #17
0
def cash_block():
    return VBlock([
        TextBlock("Cash", MID_SIZE),
        TrendLines().chart("cash",
                           250,
                           150,
                           min_y_axis=0,
                           x_start=months_ago(6)),
    ])
Exemple #18
0
def corrections_block():
    weeks_back = 4
    interesting_correction = 8
    period = Period(Day().plus_weeks(-weeks_back))

    def corrections_percentage_coloring(value):
        return dependent_color(value, red_treshold=5, green_treshold=3)

    def project_link(_, fullline):
        return f"https://oberon.simplicate.com/projects/{fullline[0]}/hours"

    result = VBlock(
        [
            TextBlock("Correcties", MID_SIZE),
            HBlock(
                [
                    TextBlock(
                        corrections_percentage(period),
                        MID_SIZE,
                        text_format="%",
                        color=corrections_percentage_coloring,
                    ),
                    TextBlock(
                        f"correcties op uren van<br/> week {period.fromday.week_number()} "
                        +
                        f"tot en met week {period.fromday.week_number() + weeks_back - 1}.",
                        color=GRAY,
                    ),
                ],
                padding=70,
            ),
            Table(
                largest_corrections(interesting_correction, period),
                TableConfig(
                    headers=[],
                    aligns=["left", "left", "right"],
                    hide_columns=[0],
                    row_linking=project_link,
                ),
            ),
        ],
        link="corrections.html",
    )
    return result
def render_resultaat_vergelijking_page():

    omzet = VBlock([
        TextBlock('Omzet vergelijking', MID_SIZE),
        LineChart(
            [
                omzet_per_maand(),
                omzet_begroot_per_maand(),
                omzet_vorig_jaar_per_maand()
            ],
            ChartConfig(
                width=900,
                height=600,
                labels=[
                    "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug",
                    "Sep", "Oct", "Nov", "Dec"
                ],
                colors=['#4285f4', '#f4b400', '#db4437'],
                bottom_labels=['Omzet', 'Omzet begroot', 'Omzet vorig jaar'],
            ),
        ),
    ])

    winst = VBlock([
        TextBlock('Winst vergelijking', MID_SIZE),
        LineChart(
            [
                winst_per_maand(),
                winst_begroot_per_maand(),
                winst_vorig_jaar_per_maand()
            ],
            ChartConfig(
                width=900,
                height=600,
                labels=MAANDEN,
                colors=['#4285f4', '#f4b400', '#db4437'],
                bottom_labels=['Winst', 'Winst begroot', 'Winst vorig jaar'],
            ),
        ),
    ])

    page = Page([TextBlock('Resultaat', HEADER_SIZE), VBlock([omzet, winst])])

    page.render('output/resultaat_vergelijking.html')
Exemple #20
0
def render_travelbase_page(output_folder):
    bookings = get_bookings_per_week(booking_type='bookings')
    if not isinstance(bookings, pd.DataFrame):
        return  # An error occurred, no use to proceed
    totals = [(brand, bookings[brand].sum()) for brand in BRANDS]
    totals_table = Table(
        totals,
        TableConfig(aligns=['left', 'right'], formats=['', '0'], totals=[0,
                                                                         1]))
    page = Page([
        TextBlock('Travelbase', HEADER_SIZE),
        TextBlock(
            'Aantal boekingen per week. Weken lopen van maandag t/m zondag.',
            color='gray'),
        bar_chart(bookings, 600, 400),
        totals_table,
    ])

    page.render(output_folder / 'travelbase.html')
Exemple #21
0
 def add_subtotal_row(title, subtotal, budget=None, style=TOPLINE):
     if budget:
         budget_month = TextBlock(budget[0], text_format='.', color=GRAY, style=BOLD)
         budget_ytd = TextBlock(budget[1], text_format='.', color=GRAY, style=BOLD)
     else:
         budget_month, budget_ytd = 0, 0
     grid.add_row(
         [
             TextBlock(title, style=BOLD),
             '',
             TextBlock(subtotal[0], text_format='.', style=BOLD),
             budget_month,
             '',
             '',
             TextBlock(subtotal[1], text_format='.', style=BOLD),
             budget_ytd,
         ],
         styles=['', style, style, '', '', style, style, ''],
     )
Exemple #22
0
def hours_block(year, month):
    month_names = []
    data = []
    for m in range(month):
        month_names += [TextBlock(MAANDEN[m])]
        fromday = Day(year, m + 1, 1)
        untilday = fromday.plus_months(1)
        period = Period(fromday, untilday)
        data += [HoursData(period)]
    headers = month_names + ['', 'YTD']
    curyear = datetime.datetime.today().strftime('%Y')
    total_period = Period(curyear + '-01-01')
    data += [None, HoursData(total_period)]
    grid = kpi_grid(headers, data)

    chart = None
    if month >= 3:  # Voor maart heeft een grafiekje niet veel zin
        chart = BarChart(
            [d.omzet
             for d in data[:-2]],  # -2 is lelijk maar haalt de total col eraf
            ChartConfig(
                width=60 * month,
                height=150,
                colors=['#ddeeff'],
                bottom_labels=[MAANDEN[m] for m in range(month)],
                y_axis_max_ticks=5,
            ),
        )

    return VBlock([
        TextBlock('Billable uren', MID_SIZE),
        TextBlock(
            '''Beschikbare uren zijn alle uren die we hebben na afrek van vrije dagen en verzuim.<br/>
                   Klant uren zijn alle uren besteed aan werk voor klanten. <br/>
                   Billable uren is wat er over is na correcties. <br/>
                   Omzet is de omzet gemaakt in die uren.''',
            color=GRAY,
        ),
        grid,
        VBlock([TextBlock('Omzet op uren per maand'), chart],
               css_class="no-print"),
    ])
Exemple #23
0
def team_block():
    fte = aantal_fte()
    # fte_begroot = aantal_fte_begroot()
    fte_color = BLACK  # dependent_color(fte_begroot - fte, 1, -1)

    return VBlock([
        TextBlock("Team", MID_SIZE),
        HBlock([
            VBlock([
                TextBlock("Aantal mensen", DEF_SIZE, padding=5, color=GRAY),
                TextBlock(aantal_mensen(), MID_SIZE, text_format=".5"),
            ]),
            VBlock([
                TextBlock("Aantal FTE", DEF_SIZE, padding=5, color=GRAY),
                TextBlock(fte, MID_SIZE, color=fte_color, text_format=".5"),
                # TextBlock('Begroot', defsize, padding=5, color=GRAY),
                # TextBlock(fte_begroot, midsize, color=GRAY, format='.5'),
            ]),
        ]),
    ])
Exemple #24
0
def render_operations_page(output_folder: Path, year: int = None):
    weeks = 20
    if year:
        # Use given year. Create page with year name in it
        html_page = f'operations {year}.html'
    else:
        # Use the current year (default)
        year = int(datetime.datetime.today().strftime('%Y'))
        html_page = 'operations.html'
    total_period = Period(f'{year}-01-01', f'{year + 1}-01-01')
    page = Page([
        TextBlock('Operations KPI'
                  's', HEADER_SIZE),
        TextBlock(
            f"Belangrijkste KPI's per week de afgelopen {weeks} weken",
            color="gray",
        ),
        kpi_block(weeks=weeks, total_period=total_period, total_title='YTD'),
    ])
    page.render(output_folder / html_page)
Exemple #25
0
def verzuim_block():
    period = Period(Day().plus_months(-3))
    verzuim = verzuimpercentage(period)
    verzuim_color = dependent_color(verzuim, 3, 1.5)
    return VBlock(
        [
            TextBlock("Verzuim", MID_SIZE),
            TextBlock("Verzuimpercentage de laatste 3 maanden",
                      DEF_SIZE,
                      color=GRAY),
            TextBlock(
                verzuim,
                MID_SIZE,
                text_format="%1",
                color=verzuim_color,
                tooltip=
                "Gemiddeld bij DDA in 2019: 3.0%. Groen bij 1,5%, rood bij 3%",
            ),
        ],
        link="absence.html",
    )
Exemple #26
0
def klanten_block():
    klanten = VBlock(
        [
            TextBlock("Klanten", MID_SIZE),
            TextBlock("Top 3 klanten laatste 6 maanden",
                      DEF_SIZE,
                      padding=10,
                      color=GRAY),
            Table(
                top_x_klanten_laatste_zes_maanden(3),
                TableConfig(
                    headers=[],
                    aligns=["left", "right", "right"],
                    formats=["", "€", "%"],
                    totals=[0, 0, 1],
                ),
            ),
        ],
        link="clients.html",
    )
    return klanten
Exemple #27
0
def render_sales_page(output_folder: Path):

    sales_trajecten = VBlock([
        TextBlock('Actieve&nbsp;salestrajecten', MID_SIZE),
        Table(
            sales_waarde_details(),
            TableConfig(
                headers=[
                    'klant', 'project', 'grootte', 'kans', 'fase', 'waarde',
                    'bron'
                ],
                aligns=[
                    'left', 'left', 'right', 'right', 'left', 'right', 'left'
                ],
                formats=['', '', '€', '%', '', '€', ''],
                totals=[0, 0, 1, 0, 0, 1, 0],
            ),
        ),
    ])

    pijplijn = VBlock([
        TextBlock('Werk&nbsp;in&nbsp;de&nbsp;pijplijn', MID_SIZE),
        TextBlock('Moet uit Simplicate komen'),
        # Table(
        #     werk_in_pijplijn_details(),
        #     TableConfig(
        #         headers=['klant', 'project', '% af', 'onderhanden', 'eigenaar'],
        #         aligns=['left', 'left', 'right', 'right', 'left'],
        #         formats=['', '', '%', '€', ''],
        #         totals=[0, 0, 0, 1, 0],
        #     ),
        # ),
    ])

    page = Page(
        [TextBlock('Sales', HEADER_SIZE),
         HBlock([sales_trajecten, pijplijn])])

    page.render(output_folder / 'sales.html')
Exemple #28
0
def billable_chart():
    months_back = 3
    return VBlock([
        TextBlock(
            f"Billable, hele team, laatste {months_back} maanden",
            DEF_SIZE,
            color=GRAY,
        ),
        TrendLines().chart("billable_hele_team",
                           250,
                           150,
                           x_start=months_ago(months_back)),
    ])
Exemple #29
0
def render_verzuim_page(output_folder: Path):
    timesheet = Timesheet()
    months = 3
    period = Period(Day().plus_months(-months))
    table = VBlock([
        Table(
            verzuim_list(period),
            TableConfig(
                headers=["Naam", "Dag", "soort", "Dagen"],
                aligns=["left", "left", "left", "right"],
                formats=[
                    "",
                    "",
                    "",
                    ".5",
                ],
                totals=[0, 0, 0, 1],
            ),
        ),
    ])

    verzuim = verzuimpercentage(period)
    verzuim_color = dependent_color(verzuim, 3, 1.5)
    page = Page([
        TextBlock("Verzuim", HEADER_SIZE),
        TextBlock(f"De afgelopen {months} maanden", color=GRAY),
        HBlock([
            VBlock([
                TextBlock("Geboekte uren", DEF_SIZE, color="gray", padding=5),
                TextBlock("Verzuim uren", DEF_SIZE, color="gray"),
                TextBlock("Verzuimopercentage", DEF_SIZE, color="gray"),
            ]),
            VBlock([
                TextBlock(
                    timesheet.normal_hours(period),
                    DEF_SIZE,
                    text_format=".",
                    padding=5,
                ),
                TextBlock(timesheet.absence_hours(period),
                          DEF_SIZE,
                          text_format="."),
                TextBlock(verzuim, verzuim_color, text_format="%1"),
            ]),
        ]),
        table,
    ])
    page.render(output_folder / "absence.html")
Exemple #30
0
 def add_normal_row(title, result, budget=None):
     if budget:
         budget_month = TextBlock(budget[0], text_format='.', color=GRAY)
         budget_ytd = TextBlock(budget[1], text_format='.', color=GRAY)
     else:
         budget_month, budget_ytd = 0, 0
     grid.add_row(
         [
             TextBlock(title),
             TextBlock(result[0], text_format='.'),
             '',
             budget_month,
             '',
             TextBlock(result[1], text_format='.'),
             '',
             budget_ytd,
         ]
     )
     if toelichting_sheet:
         toelichting = toelichting_sheet[title, 'Toelichting']
         if toelichting:
             b = toelichtingen  # zeer merkwaardige constructie maar krijg een foutmelding als ik direct iets toevoeg aan toelichtingen
             b += [(title, toelichting)]