création plots total et camembert suites à demande de matthieu
This commit is contained in:
parent
6703b95df7
commit
3b4935bc6a
|
|
@ -1 +1,2 @@
|
||||||
.venv/
|
.venv/
|
||||||
|
config
|
||||||
|
|
|
||||||
2372
compta.svg
2372
compta.svg
File diff suppressed because it is too large
Load Diff
|
Before Width: | Height: | Size: 64 KiB After Width: | Height: | Size: 25 KiB |
4
config
4
config
|
|
@ -1,4 +1,4 @@
|
||||||
[path]
|
[path]
|
||||||
wp-config=/var/www/bricolesocialclub.org/wp-config.php
|
wp-config=wp-config.php
|
||||||
budget=budget.csv
|
budget=budget.csv
|
||||||
save_directory=/var/www/bsc.pbblanc.fr/wp-content/uploads/compta
|
save_directory=.
|
||||||
|
|
|
||||||
276
main.py
276
main.py
|
|
@ -5,11 +5,14 @@ import configparser as cp
|
||||||
import os
|
import os
|
||||||
from sankeyflow import Sankey
|
from sankeyflow import Sankey
|
||||||
import matplotlib.pyplot as plt
|
import matplotlib.pyplot as plt
|
||||||
|
import matplotlib.patches as mpatches
|
||||||
import re
|
import re
|
||||||
import datetime
|
import datetime
|
||||||
import mariadb
|
import mariadb
|
||||||
import sys
|
import sys
|
||||||
|
import math
|
||||||
|
import numpy as np
|
||||||
|
from colorsys import hls_to_rgb
|
||||||
#node_central = 'total'
|
#node_central = 'total'
|
||||||
|
|
||||||
class BudgetLine():
|
class BudgetLine():
|
||||||
|
|
@ -20,6 +23,8 @@ class BudgetLine():
|
||||||
self.revenue = revenue
|
self.revenue = revenue
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
class DBInfos():
|
class DBInfos():
|
||||||
name:str
|
name:str
|
||||||
user:str
|
user:str
|
||||||
|
|
@ -39,11 +44,30 @@ def main(path_b:str,
|
||||||
path_wp:str,
|
path_wp:str,
|
||||||
save_dir:str):
|
save_dir:str):
|
||||||
budget=import_csv(path_b)
|
budget=import_csv(path_b)
|
||||||
create_budget_sankey(budget,save_dir)
|
budget = sort_des_budget(budget, 0)
|
||||||
|
#create_budget_sankey(budget,save_dir)
|
||||||
db_infos = get_db_infos(path_wp)
|
db_infos = get_db_infos(path_wp)
|
||||||
cursor = connect_mariadb(db_infos)
|
#cursor = connect_mariadb(db_infos)
|
||||||
create_accounting_sankey(cursor, save_dir)
|
#create_accounting_sankey(cursor, save_dir)
|
||||||
|
|
||||||
|
#finances, last_update = import_finances(cursor)
|
||||||
|
finances = import_csv(path_b)
|
||||||
|
finances = test_finance(finances)
|
||||||
|
last_update = rf'2025-01-01'
|
||||||
|
finances = sort_des_budget(finances, 0)
|
||||||
|
tot_rev = get_total_revenues(finances)
|
||||||
|
tot_ch = get_total_charges(finances)
|
||||||
|
totals = get_totals_lists_for_charts(
|
||||||
|
tot_ch_bud = get_total_charges(budget),
|
||||||
|
tot_ch_real = tot_ch,
|
||||||
|
tot_re_bud = get_total_revenues(budget),
|
||||||
|
tot_re_real = tot_rev
|
||||||
|
)
|
||||||
|
revenue_ratios = get_revenue_ratios(tot_rev, finances)
|
||||||
|
charge_ratios = get_charge_ratios(tot_ch, finances)
|
||||||
|
create_total_diagram(save_dir, totals, last_update)
|
||||||
|
create_pie_diagrams(save_dir, revenue_ratios, charge_ratios, last_update)
|
||||||
|
print('oucou')
|
||||||
|
|
||||||
#connection à la db
|
#connection à la db
|
||||||
# Lister les comptes qui m'intéressent
|
# Lister les comptes qui m'intéressent
|
||||||
|
|
@ -53,6 +77,242 @@ def main(path_b:str,
|
||||||
# - ajouter valeur dans flow et nodes
|
# - ajouter valeur dans flow et nodes
|
||||||
|
|
||||||
|
|
||||||
|
def test_finance(budget:list[BudgetLine]):
|
||||||
|
coef = -500
|
||||||
|
for line in budget:
|
||||||
|
if coef > 0 :
|
||||||
|
if line.revenue == 0 :
|
||||||
|
line.charge += coef
|
||||||
|
else :
|
||||||
|
line.revenue += coef
|
||||||
|
if coef < 0 :
|
||||||
|
if line.revenue == 0 and line.charge > abs(coef) :
|
||||||
|
line.charge += coef
|
||||||
|
elif line.charge == 0 and line.revenue > abs(coef) :
|
||||||
|
line.revenue += coef
|
||||||
|
return budget
|
||||||
|
|
||||||
|
|
||||||
|
def sort_des_budget(budget:list[BudgetLine], li:int):
|
||||||
|
if li < len(budget)-1 :
|
||||||
|
ch = budget[li].charge
|
||||||
|
rev = budget[li].revenue
|
||||||
|
if (ch > 0 and ch >= budget[li+1].charge) or (ch == 0 and budget[li+1].charge ==0 and rev >=budget[li+1].revenue ):
|
||||||
|
budget = sort_des_budget(budget, li+1)
|
||||||
|
else :
|
||||||
|
if li == 0 :
|
||||||
|
budget = [budget[li+1], budget[li]] + budget[li+2:]
|
||||||
|
budget = sort_des_budget(budget, li)
|
||||||
|
elif li < len(budget) - 2 :
|
||||||
|
budget = budget[:li] + [budget[li+1], budget[li]] + budget[li+2:]
|
||||||
|
budget = sort_des_budget(budget, li-1)
|
||||||
|
else :
|
||||||
|
budget = budget[:li] + [budget[li+1], budget[li]]
|
||||||
|
budget = sort_des_budget(budget, li-1)
|
||||||
|
|
||||||
|
return budget
|
||||||
|
|
||||||
|
|
||||||
|
def get_revenue_ratios(tot:int, budget:list[BudgetLine]):
|
||||||
|
values = {}
|
||||||
|
oth = 0
|
||||||
|
coef = 4
|
||||||
|
i = 0
|
||||||
|
for line in budget:
|
||||||
|
if line.revenue >0 :
|
||||||
|
if i < coef:
|
||||||
|
values.update(
|
||||||
|
{line.desc:line.revenue}
|
||||||
|
)
|
||||||
|
i+=1
|
||||||
|
else:
|
||||||
|
oth += line.revenue
|
||||||
|
if oth > 0 :
|
||||||
|
values.update(
|
||||||
|
{'Autres':oth}
|
||||||
|
)
|
||||||
|
return values
|
||||||
|
|
||||||
|
|
||||||
|
def get_charge_ratios(tot:int, budget:list[BudgetLine]):
|
||||||
|
values = {}
|
||||||
|
oth = 0
|
||||||
|
coef = 4
|
||||||
|
i = 0
|
||||||
|
for line in budget:
|
||||||
|
if line.charge >0 :
|
||||||
|
if i < coef:
|
||||||
|
values.update(
|
||||||
|
{line.desc:line.charge}
|
||||||
|
)
|
||||||
|
i+=1
|
||||||
|
else:
|
||||||
|
oth += line.charge
|
||||||
|
if oth > 0 :
|
||||||
|
values.update(
|
||||||
|
{'Autres':oth}
|
||||||
|
)
|
||||||
|
return values
|
||||||
|
|
||||||
|
def import_finances(cursor):
|
||||||
|
# extraire le compte et la caisse au 1 er janvier de l'année
|
||||||
|
year = datetime.datetime.now().year
|
||||||
|
first_day = rf'{year}-01-01'
|
||||||
|
accounts = get_income_and_expense_accounts(cursor)
|
||||||
|
finances = []
|
||||||
|
last_update = first_day
|
||||||
|
for a in accounts:
|
||||||
|
tot_deb, tot_cred, last_entrie = get_sum_of_operations(
|
||||||
|
cursor,
|
||||||
|
first_day,
|
||||||
|
a
|
||||||
|
)
|
||||||
|
if a.type == 6 :
|
||||||
|
#charge
|
||||||
|
finances.append(BudgetLine(a.label, tot_deb - tot_cred, 0))
|
||||||
|
elif a.type == 7:
|
||||||
|
finances.append(BudgetLine(a.label, 0, tot_cred - tot_deb))
|
||||||
|
if last_entrie >= last_update :
|
||||||
|
last_update = last_entrie
|
||||||
|
|
||||||
|
return finances, last_update
|
||||||
|
|
||||||
|
|
||||||
|
def get_total_charges(budget:list[BudgetLine]):
|
||||||
|
tot = 0
|
||||||
|
for line in budget:
|
||||||
|
tot += line.charge
|
||||||
|
return int(tot)
|
||||||
|
|
||||||
|
|
||||||
|
def get_total_revenues(budget:list[BudgetLine]):
|
||||||
|
tot = 0
|
||||||
|
for line in budget:
|
||||||
|
tot += line.revenue
|
||||||
|
return int(tot)
|
||||||
|
|
||||||
|
|
||||||
|
def get_totals_lists_for_charts(tot_ch_bud, tot_ch_real, tot_re_bud, tot_re_real):
|
||||||
|
rest_ch = abs(tot_ch_bud - tot_ch_real)
|
||||||
|
rest_re = abs(tot_re_bud - tot_re_real)
|
||||||
|
if tot_ch_bud >= tot_ch_real and tot_re_bud >= tot_re_real:
|
||||||
|
|
||||||
|
values = {
|
||||||
|
f'actuel' : [tot_re_real, tot_ch_real],
|
||||||
|
f'budget' : [rest_re, rest_ch]
|
||||||
|
}
|
||||||
|
else :
|
||||||
|
values = {
|
||||||
|
f'bot_budget' : [int(0.98 * tot_re_bud), int(0.98 * tot_ch_bud)],
|
||||||
|
f'budget' : [int(0.02 *tot_re_bud), int(0.02 *tot_ch_bud)],
|
||||||
|
f'actuel' : [rest_re, rest_ch]
|
||||||
|
}
|
||||||
|
return values
|
||||||
|
|
||||||
|
|
||||||
|
def hls_to_rgbs(s_hsl):
|
||||||
|
rgb = hls_to_rgb(s_hsl["Hue"], s_hsl["Luminosité"], s_hsl["Saturation"] )
|
||||||
|
return "#" + "".join("%02X" % round(i*255) for i in rgb )
|
||||||
|
|
||||||
|
|
||||||
|
def get_color_list(nb, s_hsl, sens = "Blanc") :
|
||||||
|
temp = s_hsl
|
||||||
|
luminosite = temp["Luminosité"]
|
||||||
|
|
||||||
|
if sens == "Blanc":
|
||||||
|
ecart = (1-luminosite ) /(nb +1)
|
||||||
|
else :
|
||||||
|
ecart = luminosite / (nb + 1)
|
||||||
|
|
||||||
|
l_couleur = []
|
||||||
|
for x in range(nb) :
|
||||||
|
l_couleur.append(hls_to_rgbs(temp ))
|
||||||
|
if sens == "Blanc" :
|
||||||
|
luminosite += ecart
|
||||||
|
else :
|
||||||
|
print(luminosite)
|
||||||
|
luminosite -= ecart
|
||||||
|
temp["Luminosité"] = luminosite
|
||||||
|
return l_couleur
|
||||||
|
|
||||||
|
|
||||||
|
def create_pie_diagrams(path:str,
|
||||||
|
revenue_ratios:dict[str, int],
|
||||||
|
charge_ratios:dict[str, int],
|
||||||
|
last_update:str):
|
||||||
|
|
||||||
|
bleu_hls = {"Hue":190 / 360,
|
||||||
|
"Saturation":100/100,
|
||||||
|
"Luminosité":30/100}
|
||||||
|
|
||||||
|
marron_hls = {"Hue":23 / 360,
|
||||||
|
"Saturation":72/100,
|
||||||
|
"Luminosité":30/100}
|
||||||
|
|
||||||
|
fig, axs = plt.subplots(ncols=2,figsize=(10, 4), layout='constrained')
|
||||||
|
labels, values = transform_dict_for_diag(revenue_ratios)
|
||||||
|
colors = get_color_list(len(values),bleu_hls)
|
||||||
|
p = axs[0].pie(x=values, labels=labels, colors=colors)
|
||||||
|
|
||||||
|
labels, values = transform_dict_for_diag(charge_ratios)
|
||||||
|
colors = get_color_list(len(values),marron_hls)
|
||||||
|
p = axs[1].pie(x=values, labels=labels, colors=colors)
|
||||||
|
|
||||||
|
axs[1].set_title('Répartition des dépenses réalisées')
|
||||||
|
axs[0].set_title('Répartition des recettes réalisées')
|
||||||
|
path = os.path.join(path, 'ratios.svg')
|
||||||
|
plt.savefig(path)
|
||||||
|
plt.close()
|
||||||
|
|
||||||
|
|
||||||
|
def transform_dict_for_diag(dicts:dict):
|
||||||
|
labels = []
|
||||||
|
values = []
|
||||||
|
for label, value in dicts.items():
|
||||||
|
labels.append(f'{label}\n{value}€')
|
||||||
|
values.append(value)
|
||||||
|
|
||||||
|
return labels, values
|
||||||
|
|
||||||
|
|
||||||
|
def create_total_diagram(path:str,
|
||||||
|
totals: dict[str, list[int]],
|
||||||
|
last_upadate:str):
|
||||||
|
|
||||||
|
fig, ax = plt.subplots()
|
||||||
|
plt.grid(True, 'major', 'y', color="#555555")
|
||||||
|
width = 0.6
|
||||||
|
types = ('dépenses', 'revenus')
|
||||||
|
leg = [mpatches.Patch(color='#007e97', label=f'au {last_upadate}'),
|
||||||
|
mpatches.Patch(color='#999999', label='budgeté')
|
||||||
|
]
|
||||||
|
bottom = np.zeros(2)
|
||||||
|
if len(totals) ==2 :
|
||||||
|
col = ['#007e97', '#999999']
|
||||||
|
else:
|
||||||
|
col = ['#007e97', '#999999', '#007e97']
|
||||||
|
|
||||||
|
x = 0
|
||||||
|
for position, values in totals.items():
|
||||||
|
p = ax.bar(types, values, width, label=position, bottom=bottom, color=col[x] )
|
||||||
|
bottom += values
|
||||||
|
x+=1
|
||||||
|
|
||||||
|
#ax.invert_yaxis() # labels read top-to-bottom
|
||||||
|
|
||||||
|
ax.set_title('Total des dépenses et des revenus de 2025')
|
||||||
|
box = ax.get_position()
|
||||||
|
ax.set_position(
|
||||||
|
[box.x0, box.y0 + box.height * 0.1,
|
||||||
|
box.width, box.height * 0.9]# type: ignore
|
||||||
|
)
|
||||||
|
ax.legend(handles=leg, loc='upper center', bbox_to_anchor=(0.5, -0.07),
|
||||||
|
fancybox=True, shadow=True, ncol=2)
|
||||||
|
path = os.path.join(path, 'total.svg')
|
||||||
|
plt.savefig(path)
|
||||||
|
plt.close()
|
||||||
|
|
||||||
|
|
||||||
def create_accounting_sankey(cursor, save_dir):
|
def create_accounting_sankey(cursor, save_dir):
|
||||||
NR = []
|
NR = []
|
||||||
NC = []
|
NC = []
|
||||||
|
|
@ -73,7 +333,7 @@ def create_accounting_sankey(cursor, save_dir):
|
||||||
tmp_res = reserves
|
tmp_res = reserves
|
||||||
tot = reserves
|
tot = reserves
|
||||||
for a in accounts:
|
for a in accounts:
|
||||||
tot_deb, tot_cred = get_sum_of_operations(
|
tot_deb, tot_cred, last_entrie = get_sum_of_operations(
|
||||||
cursor,
|
cursor,
|
||||||
first_day,
|
first_day,
|
||||||
a
|
a
|
||||||
|
|
@ -110,6 +370,7 @@ def create_accounting_sankey(cursor, save_dir):
|
||||||
plt.savefig(path)
|
plt.savefig(path)
|
||||||
plt.close()
|
plt.close()
|
||||||
|
|
||||||
|
|
||||||
def get_income_and_expense_accounts(cursor):
|
def get_income_and_expense_accounts(cursor):
|
||||||
VERBOSE = False
|
VERBOSE = False
|
||||||
if VERBOSE : print("get_income_and_expense_accounts")
|
if VERBOSE : print("get_income_and_expense_accounts")
|
||||||
|
|
@ -191,12 +452,15 @@ def get_sum_of_operations(cursor, date:str, account:Account):
|
||||||
"SELECT piece_num, numero_compte, label_compte, doc_date, code_journal, debit, credit FROM llx_accounting_bookkeeping WHERE numero_compte = ? AND doc_date >= ?",
|
"SELECT piece_num, numero_compte, label_compte, doc_date, code_journal, debit, credit FROM llx_accounting_bookkeeping WHERE numero_compte = ? AND doc_date >= ?",
|
||||||
(account.number, date)
|
(account.number, date)
|
||||||
)
|
)
|
||||||
|
last_entrie = date
|
||||||
for piece_num, numero_compte, label_compte, doc_date, code_journal, debit, credit in cursor :
|
for piece_num, numero_compte, label_compte, doc_date, code_journal, debit, credit in cursor :
|
||||||
tot_cred += credit
|
tot_cred += credit
|
||||||
tot_deb += debit
|
tot_deb += debit
|
||||||
|
if doc_date > last_entrie :
|
||||||
|
last_entrie = doc_date
|
||||||
|
|
||||||
if VERBOSE : print(f'pour le compte {account.number} : credit = {tot_cred} / debit = {tot_deb} à partir du {date}')
|
if VERBOSE : print(f'pour le compte {account.number} : credit = {tot_cred} / debit = {tot_deb} à partir du {date}')
|
||||||
return int(tot_deb), int(tot_cred)
|
return int(tot_deb), int(tot_cred), last_entrie
|
||||||
|
|
||||||
|
|
||||||
def get_db_infos(path_wp:str):
|
def get_db_infos(path_wp:str):
|
||||||
|
|
|
||||||
File diff suppressed because it is too large
Load Diff
|
After Width: | Height: | Size: 36 KiB |
Loading…
Reference in New Issue