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')
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')
def render_dashboard(output_folder): page = Page([ HBlock([ commerce_block(), operations_block(), finance_block(), hr_block() ]) ]) page.render(output_folder / "dashboard.html")
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')
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")
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')
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')
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')
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)
def render_maandrapportage(output_folder, year, month): yuki_result = YukiResult(year, month) page = Page([ VBlock([ TextBlock(f'Maandrapportage {MAANDEN[month - 1].lower()}, {year}', HEADER_SIZE), hours_block(year, month), profit_and_loss_block(yuki_result, year, month), balance_block(yuki_result, year, month), # HBlock([cash_block(), debiteuren_block()]), cashflow_analysis_block(yuki_result, year, month), ]) ]) htmlpath = output_folder / f'{year}_{month:02}.html' page.render(htmlpath, template='maandrapportage.html') # Generate PDF pdfpath = htmlpath.with_suffix('.pdf') options = {"enable-local-file-access": None} pdfkit.from_file(str(htmlpath), str(pdfpath), options=options)
def render_debiteuren_page(output_folder: Path): debiteuren = debiteuren_leeftijd_analyse() if not isinstance(debiteuren, pd.DataFrame): return # Error occurred, no use to proceed page = Page([ TextBlock('Debiteuren', HEADER_SIZE), Table( debiteuren, TableConfig( headers=[ 'klant', 'openstaand', '<30 dg', '30-60 dg', '60-90 dg', '> 90 dg' ], aligns=['left', 'right', 'right', 'right', 'right', 'right'], formats=['', '.', '.', '.', '.', '.'], totals=[0, 1, 1, 1, 1, 1], row_linking=lambda line, value: 'https://oberview.oberon.nl/facturen/openstaand', ), ), ]) page.render(output_folder / 'debiteuren.html')
def render_sales_page(output_folder: Path): sales_trajecten = VBlock([ TextBlock('Actieve 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 in de 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')
def render_productiviteit_page(): tables = HBlock([ Table( productiviteit_overzicht(), block_id='overzicht', headers=[ 'persoon', 'omzet', 'uren', 'per uur', 'geboekt', 'productief', '% productief', 'billable', '% billable', ], aligns=[ 'left', 'right', 'right', 'right', 'right', 'right', 'right', 'right', 'right' ], formats=['', '€', '.', '€', '.', '.', '%', '.', '%'], totals=[0, 1, 1, 0, 1, 1, 0, 1, 0], ) ]) for user in tuple_of_productie_users(): data = productiviteit_persoon(user) target = user_target(user) if target: total = sum([row[3] for row in data]) perc = (total / user_target_now(user) - 1) * 100 percstr = do_format(perc, '+%') targetstr = f'target: € {target:,.0f}'.replace(',', '.') else: targetstr = percstr = '' tables.add_block( Table( data, block_id='productiviteit_' + user, headers=[user, targetstr, percstr, 'omzet', 'uren', 'per uur'], aligns=['left', 'left', 'left', 'right', 'right', 'right'], formats=['', '', '', '€', '.', '€'], totals=[0, 0, 0, 1, 1, 0], )) page = Page([ TextBlock('Productiviteit', HEADER_SIZE), VBlock([TextBlock('Under construction', color='red'), tables]) ]) page.add_onloadcode('make_users_clickable();') page.render('output/productiviteit.html') for user in tuple_of_productie_users(): productiviteit_table = Table( productiviteit_persoon(user), headers=[user, '', '', 'omzet', 'uren', 'per uur'], aligns=['left', 'left', 'left', 'right', 'right', 'right'], formats=['', '', '', '€', '.', '€'], totals=[0, 0, 0, 1, 1, 0], ) chartdata = [{ 'x': rec['datum'].strftime('%Y-%m-%d'), 'y': rec['hours'] } for rec in billable_trend_person(user)] chart = ScatterChart(400, 400, values=chartdata, color='#6666cc', fill_color='#ddeeff', y_start=0, x_type='date') page = Page([ TextBlock(f'Productiviteit {user}', HEADER_SIZE), chart, productiviteit_table ]) page.render(f'output/productiviteit_{user}.html')
def render_resultaat_berekening(output_folder: Path): page = Page([HBlock([winst_berekening_block()])]) page.render(output_folder / 'resultaat_berekening.html')
def render_winstgevendheid_page(output_folder: Path, period=None): if period: period_description = f'Van {period.fromday} tot {period.untilday}' if not period: period = Period(Day().plus_months(-12)) # Laatste 12 maanden period_description = 'De laatste 12 maanden.' client_data = winst_per_klant(period) per_client = VBlock([ TextBlock('Per klant', MID_SIZE), Table( client_data, TableConfig( id="winst_per_klant", headers=list(client_data.columns), aligns=[ 'left', 'right', 'right', 'right', 'right', 'right', 'right', 'right' ], formats=['', '.', '€', '€', '€', '€', '€', '€'], totals=[False, True, True, True, True, True, False, False], ), ), ]) project_data = winst_per_project(period) per_project = VBlock([ TextBlock('Per project', MID_SIZE), Table( project_data, TableConfig( id="winst_per_project", headers=list(project_data.columns), aligns=[ 'left', 'left', 'right', 'right', 'right', 'right', 'right', 'right', 'right' ], formats=['', '', '.', '€', '€', '€', '€', '€', '€'], totals=[ False, False, True, True, True, True, True, False, False ], ), ), ]) person_data = winst_per_persoon(period) per_person = VBlock([ TextBlock('Per persoon (voorlopig)', MID_SIZE), Table( person_data, TableConfig( id="winst_per_persoon", headers=list(person_data.columns), aligns=['left', 'right', 'right', 'right'], formats=['', '.', '€', '€'], totals=[False, True, True, True], ), ), ]) page = Page([ TextBlock('Winstgevendheid', HEADER_SIZE), TextBlock( f'''{period_description} Uitgaande van een productiviteit van {PRODUCTIVITEIT * 100:.0f}% en €{OVERIGE_KOSTEN_PER_FTE_PER_MAAND} per persoon per maand bureaukosten.''', color=GRAY, ), HBlock([per_client, per_project, per_person]), ]) page.render(output_folder / 'winstgevendheid.html')
return grid 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') if __name__ == '__main__': os.chdir('..') load_cache() days = [Day('2022-1-1'), Day()] for test_day in days: test_page = Page([ TextBlock(f'Onderhanden werk per {test_day.strftime("%d/%m")}', HEADER_SIZE), onderhanden_werk_list(test_day), ]) if test_day == Day(): test_page.render(get_output_folder() / 'onderhanden.html') else: test_page.render(get_output_folder() / f'onderhanden{test_day}.html') print(test_day, ohw_sum(test_day, minimal_intesting_value=1000))
def render_detailpage(): data = [[key, saved_cache[key]] for key in sorted(saved_cache.keys())] page = Page([Table(data)]) page.render('output/details.html')