Last active
January 11, 2016 09:01
-
-
Save u1735067/b0c8214c5e251c303ad9 to your computer and use it in GitHub Desktop.
Script de calcul de marge pour CO32B1 en Python
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!python3 | |
# | |
# Alexandre Levavasseur - FIP16 | |
# Pour le groupe projet CO32B1 | |
# | |
# This work is licensed under a Creative Commons | |
# Attribution-NonCommercial-ShareAlike 4.0 International License | |
# | |
# Sources: | |
# https://docs.python.org/3/library/csv.html | |
# https://docs.python.org/3/tutorial/datastructures.html | |
# https://docs.python.org/3/library/functions.html#int | |
# https://docs.python.org/3.0/library/datetime.html | |
# https://docs.python.org/3.0/library/time.html#time.strptime | |
# http://stackoverflow.com/questions/3688602/sum-numbers-in-an-array | |
# https://docs.python.org/3/library/decimal.html | |
# http://stackoverflow.com/questions/4053924/python-parse-date-format-ignore-parts-of-string | |
# https://docs.python.org/3/tutorial/errors.html | |
# http://stackoverflow.com/questions/3294889/iterating-over-dictionaries-using-for-loops-in-python | |
# | |
import csv | |
from decimal import * | |
from datetime import datetime | |
import sys | |
from pprint import pprint | |
init_file = "stock_initial.csv" | |
in_file = "WalletTransactions.csv" # Doit être ordonné par date croissante | |
products = {} # dict [{'nb': x, 'price': x}, ..] ## Le stock | |
# ############################################################################# | |
# Initialise le stock .. | |
# | |
def constr_init_stock_price(): | |
with open(init_file, newline='') as csvfile: | |
reader = csv.DictReader(csvfile, delimiter=';') | |
for row in reader: | |
data = {'date': datetime.strptime(row['date'], '%d/%m/%Y'), | |
'nb': int(row['quantity']), | |
'price': Decimal(row['price'])} | |
products[row['type']] = [data] | |
# Construit une liste des (date, qté, prix) par produit | |
def constr_stock_price(): | |
with open(in_file, newline='') as csvfile: | |
reader = csv.DictReader(csvfile) | |
for row in reader: | |
if (row['transactionType'] == "Buy"): | |
data = {'date': datetime.strptime(row['date'], '%Y-%m-%d %H:%M:%S'), | |
# data = {'date': datetime.strptime(row['date'].split(' ')[0], '%Y-%m-%d'), | |
'nb': int(row['quantity']), | |
'price': Decimal(row['price'])} | |
if (not row['type'] in products): # Produit inconnu | |
products[row['type']] = [data] | |
# Pas date compliant | |
# elif (products[row['type']][-1]['price'] == row['price']): # Si le dernier a le même prix | |
# products[row['type']][-1]['nb'] = products[row['type']][-1]['nb'] + data['quantity'] | |
else: # Ajoute un nouveau prix | |
products[row['type']].append(data) | |
return products | |
# Consomme un produit en stock et retourne la liste des prix d'achat | |
# | |
def consume(product, nb, cons_date): | |
out = [] # Retourne la liste des prix | |
if (nb > 0): | |
if (not product in products): | |
raise Exception('Pas de prix pour ce produit ("%s") ?' % product) | |
else: | |
while (nb > 0): | |
if (not products[product]): # On a vidé les stock .. | |
raise Exception('Plus de prix pour ce produit ("%s")'% product) | |
elif (products[product][0]['date'] > cons_date): | |
raise Exception('Il reste du stock pour ("%s") mais il est ajouté après la vente..'% product) | |
elif (nb >= products[product][0]['nb']): # On consomme totalement ce prix | |
nb = nb - products[product][0]['nb'] | |
out.append(products[product].pop(0)) | |
else: # Sinon on prend ce qui nous intéresse et on retire du stock | |
products[product][0]['nb'] = products[product][0]['nb'] - nb | |
out.append({'nb': nb, 'price': products[product][0]['price']}) | |
nb = 0 | |
# end while | |
return out | |
# end if 2 | |
# end if 1 | |
else: | |
raise Exception('On retire 0 ?') | |
# Calcule le montant à partir d'une liste de qté & prix | |
# | |
def calc_amount_moy(prices): | |
amount = Decimal(0) | |
nb = 0 | |
for line in prices: | |
amount = amount + line['price']*line['nb'] | |
nb = nb + line['nb'] # Nombre de produits vendus | |
return amount / nb | |
# Calcule la marge d'une vente en fonction DES prix d'achats en question | |
# | |
def calc_marge(px_vente, pxs_achat): | |
price = Decimal() | |
price = px_vente - calc_amount_moy(pxs_achat) # Prix de vente - moyenne prix d'achat | |
return price | |
# Calcule la liste des marge&rendement par produit et par transaction | |
# à partir de la liste des entrées en stock et des ventes | |
# | |
def infos_par_trans(): | |
out = {} # dict de {'transId': , 'date': , 'marge': } | |
with open(in_file, newline='') as csvfile: | |
reader = csv.DictReader(csvfile) | |
for row in reader: | |
if (row['transactionType'] == "Sell"): | |
if (not row['type'] in out): | |
out[row['type']] = [] | |
this_date = datetime.strptime(row['date'], '%Y-%m-%d %H:%M:%S') | |
try: | |
consomme = consume(row['type'], int(row['quantity']), this_date) | |
marge = calc_marge(Decimal(row['price']), consomme) | |
rendement = marge / calc_amount_moy(consomme) | |
out[row['type']].append({'transId': row['transID'], | |
'date': this_date, | |
'nb' : int(row['quantity']), | |
'marge': marge, | |
'rendement': rendement}) | |
except Exception as e: | |
print("/!\ Exception : "+e.args[0]) | |
print("Produit : %s\nDate conso: %s\nProduits restants: " % (row['type'], this_date)) | |
pprint(products[row['type']]) | |
print() | |
break | |
return out | |
# Calcule (moy pondérée) la liste des marge&rendement par produit et par jour | |
# en partant de la liste des marge par produit et par transaction | |
def infos_par_day(infos_trans): | |
infos_day = {} | |
for produit,translist in infos_trans.items(): # Pour chaque produit | |
infos_day[produit] = {} | |
for trans in translist: # Pour chaque date | |
infos_day[produit][trans['date'].date()] = []; | |
for search in translist: # On cherche les dates identiques | |
if (search['date'].date() == trans['date'].date()): | |
infos_day[produit][trans['date'].date()].append(search) | |
nb_day = sum(i['nb'] for i in infos_day[produit][trans['date'].date()]) # nombre de produits vendus ce jour | |
marge_day = sum(i['nb']*i['marge'] for i in infos_day[produit][trans['date'].date()])/nb_day # moy pond. | |
rendement_day = sum(i['nb']*i['rendement'] for i in infos_day[produit][trans['date'].date()])/nb_day # moy pond. | |
infos_day[produit][trans['date'].date()] = {'marge': round(marge_day, 2), 'rendement': round(rendement_day, 2)} | |
return infos_day | |
def infos_global(infos_trans): | |
infos_global = {} | |
for produit,translist in infos_trans.items(): # Pour chaque produit | |
infos_global[produit] = [] | |
for trans in translist: # Pour chaque date | |
infos_global[produit].append(trans) | |
nb_global = sum(i['nb'] for i in infos_global[produit]) # nombre de produits vendus ce jour | |
marge_global = sum(i['nb']*i['marge'] for i in infos_global[produit])/nb_global # moy pond. | |
rendement_global = sum(i['nb']*i['rendement'] for i in infos_global[produit])/nb_global # moy pond. | |
infos_global[produit] = {'marge': round(marge_global, 2), 'rendement': round(rendement_global, 2)} | |
return infos_global | |
# ############################################################################# | |
# Execution | |
# ############################################################################# | |
# Construit la liste des prix en stock | |
constr_init_stock_price() | |
constr_stock_price() | |
# DEBUG & Test | |
DEBUG = False | |
#DEBUG = True | |
if (DEBUG): | |
pprint(products) | |
print('--Consommé') | |
nb = 19 | |
prices = consume('Small Armor Repairer II', nb, datetime.strptime('2010-09-07 00:00:00', '%Y-%m-%d %H:%M:%S')) | |
pprint(prices) | |
print('--Restant') | |
pprint(products['Small Armor Repairer II']) | |
print('--Amount consommé') | |
pprint(calc_amount(prices)) | |
print('--Marge calculée') | |
pprint(calc_marge(Decimal('271999.99'), prices)) | |
print() | |
### Prix d'achat moyen | |
# | |
''' | |
pxa = {} | |
with open(init_file, newline='') as csvfile: | |
reader = csv.DictReader(csvfile, delimiter=';') | |
for row in reader: | |
# Pas de vérif | |
pxa[row['type']] = {datetime.strptime(row['date'], '%d/%m/%Y'): [Decimal(row['price'])]} | |
with open(in_file, newline='') as csvfile: | |
reader = csv.DictReader(csvfile) | |
for row in reader: | |
if (row['transactionType'] == "Buy"): | |
pdate = datetime.strptime(row['date'].split(' ')[0], '%Y-%m-%d') | |
if row['type'] not in pxa: | |
pxa[row['type']] = {} | |
if pdate not in pxa[row['type']]: | |
pxa[row['type']][pdate] = [] | |
pxa[row['type']][pdate].append(Decimal(row['price'])) | |
print('produit,date,prix_achat') | |
for pname,prod in pxa.items(): | |
for pdate,pval in prod.items(): | |
print('%s,%s,%s' % (pname, pdate.strftime('%Y-%m-%d'), round(sum(pval)/len(pval),2))) | |
sys.exit(0) | |
''' | |
### Qte vendue par jour | |
# | |
''' | |
qd = {} | |
with open(in_file, newline='') as csvfile: | |
reader = csv.DictReader(csvfile) | |
for row in reader: | |
if (row['transactionType'] == "Sell"): | |
this_date = datetime.strptime(row['date'].split(' ')[0], '%Y-%m-%d') | |
if row['type'] not in qd: | |
qd[row['type']] = {} | |
if this_date not in qd[row['type']]: | |
qd[row['type']][this_date] = int(row['quantity']) | |
else: | |
qd[row['type']][this_date] += int(row['quantity']) | |
print('produit,date,prix_achat') | |
for pname,prod in qd.items(): | |
for pdate,pval in prod.items(): | |
print('%s,%s,%s' % (pname, pdate.strftime('%Y-%m-%d'), pval)) | |
sys.exit(0) | |
''' | |
# Main | |
infos_trans = infos_par_trans() | |
if (DEBUG): | |
pprint(infos_trans) | |
fin = infos_par_day(infos_trans) | |
fin2 = infos_global(infos_trans) | |
if (DEBUG): | |
pprint(fin) | |
pprint(fin2) | |
if (DEBUG): | |
pprint(len(products)) | |
pprint(len(infos_trans)) # 1 produit n'est pas vendu | |
pprint(len(fin)) | |
pprint(len(fin2)) | |
type = "global" | |
if (type == "day"): | |
print("produit,date,marge,rendement") | |
for produit,marges in fin.items(): | |
for date, infos in marges.items(): | |
print("%s,%s,%s,%s" % (produit, date.strftime('%Y-%m-%d'), str(infos['marge']), str(infos['rendement']))) | |
elif (type == "global"): | |
print("produit,marge,rendement") | |
for produit,infos in fin2.items(): | |
print("%s,%s,%s" % (produit, str(infos['marge']), str(infos['rendement']))) | |
else: | |
print('lol') | |
sys.exit(0) | |
## POUR PIERRE ## | |
# you owe me :p | |
# | |
def calc_val_stock(calc_date): | |
val = Decimal(0) | |
for name,product in products.items(): | |
for stock in product: | |
if (stock['date'] <= calc_date): | |
val = val + stock['nb']*stock['price'] | |
return val | |
# | |
# | |
current_date = datetime.strptime('2010-07-17 00:00:00', '%Y-%m-%d %H:%M:%S') | |
with open(in_file, newline='') as csvfile: | |
reader = csv.DictReader(csvfile) | |
for row in reader: | |
if (row['transactionType'] == "Sell"): | |
this_date = datetime.strptime(row['date'].split(' ')[0], '%Y-%m-%d') | |
if (current_date != this_date): | |
print(current_date.strftime('%Y-%m-%d')+" : "+str(calc_val_stock(current_date))) | |
current_date = this_date | |
consume(row['type'], int(row['quantity']), this_date) | |
## |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment