]> gitweb.pimeys.fr Git - scripts-20-100.git/blob - bde/credits_duplicates.py
[bde/stats/depenses] Inversion gain/dépenses
[scripts-20-100.git] / bde / credits_duplicates.py
1 #!/usr/bin/env python
2 # -*- encoding: utf-8 -*-
3
4 """ Pour trouver les chèque dupliqués sur la note. """
5
6 import psycopg2
7 import psycopg2.extras
8 import collections
9 import pprint
10 import subprocess
11
12 import pretty_print
13
14 def getcursor():
15 con = psycopg2.connect(database="note")
16 con.set_client_encoding("utf-8")
17 return (con, con.cursor(cursor_factory = psycopg2.extras.DictCursor))
18
19 def get_data(cur, delai='1 minute'):
20 """
21 Récupère les paires de crédits effectués à moins de ``delai`` d'intervalle.
22 ``delai`` est un chaîne de caractères que PostGreSQL comprendra comme une durée.
23 Les crédits doivent êtres valides, de même destinataire, de même montant.
24 Pour les crédits chèque, virement et carte bancaire, on vérifie qu'ils aient le même prénom,nom
25 """
26 req_create_credits = """
27 CREATE TEMPORARY TABLE credits AS (
28 (
29 SELECT t.*, c.prenom, c.nom FROM transactions t, cheques c
30 WHERE t.type = 'crédit' AND t.emetteur = -1 AND t.id = c.idtransaction
31 )
32 UNION
33 (
34 SELECT t.*, v.prenom, v.nom FROM transactions t, virements v
35 WHERE t.type = 'crédit' AND t.emetteur = -3 AND t.id = v.idtransaction
36 )
37 UNION
38 (
39 SELECT t.*, cb.prenom, cb.nom FROM transactions t, carte_bancaires cb
40 WHERE t.type = 'crédit' AND t.emetteur = -4 AND t.id = cb.idtransaction
41 )
42 UNION
43 (
44 SELECT t.*, '', '' FROM transactions t
45 WHERE t.type = 'crédit' AND t.emetteur = -1
46 ));
47 """
48 cur.execute(req_create_credits)
49
50 req = u"""
51 SELECT
52 t1.id AS id1,
53 t2.id AS id2,
54 t1.date AS date1,
55 t2.date AS date2,
56 t2.date - t1.date AS deltaT,
57 t1.montant,
58 t1.destinataire,
59 c.pseudo AS destp,
60 t1.description AS desc1,
61 t2.description AS desc2
62 FROM credits t1,credits t2, comptes c
63 WHERE t1.destinataire = c.idbde
64 AND t1.date >= '2016-01-01'
65 AND t2.date >= '2016-01-01'
66 AND t1.type='crédit'
67 AND t2.type='crédit'
68 AND t1.id != t2.id
69 AND t1.valide
70 AND t2.valide
71 AND t1.destinataire = t2.destinataire
72 AND t1.montant = t2.montant
73 AND t1.prenom = t2.prenom
74 AND t1.nom = t2.nom
75 AND (t2.date - t1.date) <= %(delai)s
76 AND (t2.date - t1.date) >= '0 s';
77 """
78 cur.execute(req, {"delai" : delai})
79 l = cur.fetchall()
80 return l
81
82 def sort_by_blocks(data):
83 """
84 Regroupe les transactions qui vont ensemble.
85 Sort une liste de chaînes de caractères contenant les ids de chaque block comma-separated.
86 Oui, le format de sortie est dégueu, mais c'est nettement moin galère pour trouver les doublons.
87 """
88 map = collections.defaultdict(lambda : set())
89 for t in data:
90 id1, id2 = t["id1"], t["id2"]
91 if id1 in map:
92 s = map[id1]
93 elif id2 in map:
94 s = map[id2]
95 else:
96 s = set()
97 # Comme on touche s en place et que c'est le même mutable qui a été mis
98 # comme valeur des 2 clés, il sera modifié par les étapes suivantes aussi
99 s.add(id1)
100 s.add(id2)
101 map[id1] = map[id2] = s
102 # On a tous les blocks, reste à ne garder qu'un exemplaire de chaque
103 result = [list(s) for s in map.values()]
104 for l in result:
105 l.sort()
106 result = [", ".join([str(i) for i in l]) for l in result]
107 result = list(set(result))
108 return result
109
110 def get_transactions(cur, ids):
111 """
112 Récupère les informations de toutes les transactions dans la liste d'identifiants ``ids``
113 qui doit être donné sous la forme d'une chaîne de caractères d'ids comma-separated.
114 """
115 # yuk, mais le format correspond à ce qu'outpute sort_by_blocks
116 cur.execute("SELECT * FROM credits WHERE id IN (%s)" % ids)
117 return cur.fetchall()
118
119
120 if __name__ == "__main__":
121 con, cur = getcursor()
122 data = get_data(cur)
123 blocks = sort_by_blocks(data)
124 for b in blocks:
125 l = get_transactions(cur, b)
126 formatted = pretty_print.sql_pretty_print(l, keys=["id", "date", "type", "emetteur", "destinataire", "quantite", "montant", "description", "valide", "cantinvalidate"])
127 p = subprocess.Popen(["less"], stdin=subprocess.PIPE)
128 p.communicate(formatted.encode("utf-8"))
129 print formatted
130 print "IDs : %s" % b
131 idkeep = int(b.split(",")[0])
132 ans = raw_input("Ne garder que %s (= dévalider les autres) ? [o/N]" % idkeep)
133 if ans.lower() in ["o", "y"]:
134 print "bim !"