Commit d8a59f2a authored by Vincent Le gallic's avatar Vincent Le gallic

Initialisation du dépôt

parent bf091816
This diff is collapsed.
Copyright © 2011 Vincent Le Gallic
This file is part of Note Kfet 2015.
Note Kfet 2015 is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Note Kfet 2015 is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Note Kfet 2015. If not, see <http://www.gnu.org/licenses/>.
The copy of GNU General Public License is in the main folder,
in the file "GNU General Public License.txt".
For French (unofficial) translation,
see <http://www.fsffrance.org/gpl/gpl-fr.fr.html>
Une copie de la License Publique Générale GNU est disponible dans le dossier
principal, dans le fichier "GNU General Public License.txt".
Pour une traduction (non-officielle) en français,
voir <http://www.fsffrance.org/gpl/gpl-fr.fr.html>
#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Définition des fonctions de base de communication du client
# Pour l'instant ce script sert essentiellement à les charger en mémoire pour les utiliser à la main
import os, sys, string, time, random
import socket, ssl
import threading
import json
import hashlib
import client_config
reload(client_config)
from pprint import pprint
sys.path.append(client_config.rsa_path)
import monRSA
def connect(sock,where="ici"):
port=client_config.port
ip={"ici":"localhost","vent":"vent.crans.org","eva":"eva.crans.org"}[where]
sock.connect((ip,port))
return ssl.wrap_socket(sock,ca_certs=client_config.ca_certfile,
cert_reqs=ssl.CERT_REQUIRED) #,ciphers="AES256-SHA")
def checkidentity(a):
c=a.getpeercert()
c=dict([i[0] for i in c["subject"]])
awaited={'commonName': u'note.crans.org',
'countryName': u'FR',
'emailAddress': u'legallic@crans.org',
'localityName': u'Cachan',
'organizationName': u'BDE ENS Cachan',
'organizationalUnitName': u'Kfet',
'stateOrProvinceName': u'\xc3\x8ele de France'}
if all([c[cle]==awaited[cle] for cle in awaited.keys()]):
return
else:
raise KeyboardInterrupt("Imposteur !")
def hello(socket,version):
socket.send('hello "%s"'%(version))
return json.loads(a.read())
def login(socket,user,mdp="",typ="bdd"):
if user in ["20-100","moibdd"]:
a.write('login ["20-100","mdp","bdd"]')
elif user in ["vincent","moi"]:
a.write('login ["vincent","mdp","special"]')
else:
a.write('login ["%s","%s","%s"]'%(user,mdp,typ))
return json.loads(a.read())
def search(socket,term):
a.write("search %s"%(term))
return json.loads(a.read())
def lit(socket,noprint=False):
try:
t=a.read()
if noprint:
return json.loads(t)
else:
obj=json.loads(t)
if type(obj)==unicode:
print obj
else:
pprint(obj)
except ssl.SSLError:
print "Rien à lire"
except ValueError:
print "Serveur mort ?"
def challenge(a):
_,pub=monRSA.litcles(None,client_config.server_rsa_pub_key)
chall=str(time.time())
chall+="".join(["azertyuiopqsdfghjklmwxcvbn"[random.randrange(0,26)] for i in range(20)])
coded=monRSA.crypte(chall,pub)
a.send('challenge "%s"'%(coded))
a.setblocking(True)
result=a.read()
a.settimeout(0.5)
if json.loads(result)==chall:
print "Recognized"
elif json.loads(result)==u"Challenge tenté il y a moins de 10 minutes ! =p":
print "Top tôt. Réessaye plus tard."
else:
print "Imposteur !"
if __name__=="__main__":
a=socket.socket()
a=connect(a)
a.settimeout(0.5)
checkidentity(a)
print hello(a,"Python Client alpha")
print login(a,"20-100","mdp")
print "Socket créée dans la variable \"a\""
#!/usr/bin/env python
# -*- coding: utf-8 -*-
basedir = "/home/vincent/note/new_note/"
clientdir = basedir + "client/"
ca_certfile = clientdir + "keys/ca.crt"
server_rsa_pub_key = clientdir + "keys/server_rsa_key.pub"
# le module qui fait du rsa
rsa_path = "/home/vincent/note/new_note/rsa_source/"
# parce que le port est pour l'instant aléatoire, ça disparaîtra après
portfile=open(basedir+"serveur/port.txt","r")
port = int(portfile.read())
portfile.close()
-----BEGIN CERTIFICATE-----
MIIEHzCCA4igAwIBAgIJAPuPFCLL9lQtMA0GCSqGSIb3DQEBBQUAMIG8MQswCQYD
VQQGEwJGUjEXMBUGA1UECBQOw45sZSBkZSBGcmFuY2UxDzANBgNVBAcTBkNhY2hh
bjEXMBUGA1UEChMOQkRFIEVOUyBDYWNoYW4xKzApBgNVBAsUIkF1dG9yaXTDqSBk
ZSBDZXJ0aWZpY2F0aW9uIE1hw650cmUxGjAYBgNVBAMTEUJERSBFTlMgQ2FjaGFu
IENBMSEwHwYJKoZIhvcNAQkBFhJsZWdhbGxpY0BjcmFucy5vcmcwHhcNMTExMjI2
MjIwNjQ4WhcNMjExMjIzMjIwNjQ4WjCBvDELMAkGA1UEBhMCRlIxFzAVBgNVBAgU
DsOObGUgZGUgRnJhbmNlMQ8wDQYDVQQHEwZDYWNoYW4xFzAVBgNVBAoTDkJERSBF
TlMgQ2FjaGFuMSswKQYDVQQLFCJBdXRvcml0w6kgZGUgQ2VydGlmaWNhdGlvbiBN
YcOudHJlMRowGAYDVQQDExFCREUgRU5TIENhY2hhbiBDQTEhMB8GCSqGSIb3DQEJ
ARYSbGVnYWxsaWNAY3JhbnMub3JnMIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKB
gQCykkgrE+wbpaBiejCnnQajDYbhVmbgLSp9fTwVYKXTveQ5lNdDVoRqaXq9oavE
wGNfqOQKFNbwJCOQA2Oz8HikjleLZgynZXd2QPbCX+wwOkqNqWQ+Q7NaRFTfytN4
srPgZBLyyKSGCGadOOV2ZNa5WQ9HcC3kVApdvAa48zl3ywIDAQABo4IBJTCCASEw
HQYDVR0OBBYEFPGk2qO0Ik/NrPfcCXrj+A2hVjF9MIHxBgNVHSMEgekwgeaAFPGk
2qO0Ik/NrPfcCXrj+A2hVjF9oYHCpIG/MIG8MQswCQYDVQQGEwJGUjEXMBUGA1UE
CBQOw45sZSBkZSBGcmFuY2UxDzANBgNVBAcTBkNhY2hhbjEXMBUGA1UEChMOQkRF
IEVOUyBDYWNoYW4xKzApBgNVBAsUIkF1dG9yaXTDqSBkZSBDZXJ0aWZpY2F0aW9u
IE1hw650cmUxGjAYBgNVBAMTEUJERSBFTlMgQ2FjaGFuIENBMSEwHwYJKoZIhvcN
AQkBFhJsZWdhbGxpY0BjcmFucy5vcmeCCQD7jxQiy/ZULTAMBgNVHRMEBTADAQH/
MA0GCSqGSIb3DQEBBQUAA4GBABl+B4fa1vlVJmhfe2Q1d5UVtels8RctEeisOfVD
+OpzgkTggdlYbp/9RnlKlAX8XQF6r5J4zfg1oJqB7kjT5eJ65xL88ZS4yr8f3Vmg
4yus0TMRk/sLmdLDrXwBz0tmSYsCFqJbJqowAOyVn1UKZz0D4E2GFBN9/Fl0MVGf
lEEU
-----END CERTIFICATE-----
-----BEGIN RSA PUBLIC KEY-----
MIGJAoGBAK3lNeaNyrt3FgSaHPLhZK+TL6RXnle2aeOVZvmoWuspZIdSgvDJM/ce
7CkSLZc+HL0DEXBiS1WYstiSMM6+LqUfSbDVVsbanFgqGIzFk4jz9HfMXh808nUI
rtNlZiguIIddnqmz8RikWh0oP51dctfXBhh9ZnXzgIzcqAHOC7k7AgMBAAE=
-----END RSA PUBLIC KEY-----
import socket,time,base64
def send_photo(where,port):
s=socket.socket()
s.connect(({"vent":"138.231.140.145","eva":"138.231.138.131"}[where],port))
s.send('login ["20-100","mdp","clair"]\n')
time.sleep(1)
tof=base64.b64encode(open("photos/2850.jpg","r").read())
s.send("set_photo [1,%s]\n"%(len(tof)))
time.sleep(1)
s.send(tof+'\n')
time.sleep(2)
s.close()
= Adhérents/clubs =
Table notes
-ID BdE
-Type (personne, club, login spécial)
-Nom de note
-Mot de passe
-Solde
-Nom
-Prénom
-Numéro de téléphone
-Adresse mail
-Adresse IRL
-Sexe
-Section
-pb de sante
-num SS
-aid crans
-Droits (comma-separated) -> dans les droits, y a le droit "se connecter" (on sait jamais)
-périodicité des rapports (en minutes)
-date du prochain rapport
-date du dernier rapport (pour savoir ce qu'il faut mettre dans le rapport...)
-envoyer les rapports vides ?
Table préinscriptions
-ID préinscription (id temporaire)
-Nom
-Prénom
-Numéro de téléphone
-Adresse mail
-Adresse IRL
-Sexe
-Section
-pb de sante
-num SS
Bon allez une table aussi pour les transactions bancaires à valider
Table chequesvirements
-id transaction
-date/heure transaction
-type transaction
-montant
-infos l'émetteur (et la banque pour un chèque)
-validé ?
Table historiquenote (plus simple que aller chercher dans le log à chaque fois)
-id adhérent
-nom note
-date/heure du changement
Table autorisations de prélèvement
-id bde
-créancier
-id auprès du créancier
-date/heure de l'autorisation
Table adhesions
-numcbde
-annee
-wei
-id transaction
-date d'adhesion
= Transactions =
-id de la transaction
-emetteur (celui qui paye)
-destinataire (celui qui recoit)
-quantité
-montant unitaire
-id de la consommation (si c'est une conso prédéfinie, -1 sinon)
-description (coca, pinte, ...)
-valide (ou non)
-date de la transaction
= Activités =
Table activités :
-id de l'activité
-nom de l'activité
-lieu de l'activité
-date de début
-date de fin
-organisateur de l'activité
-id du demandeur de l'activité
-liste d'invités
-date ouverture liste d'invités
-date fermeture liste d'invités
-activité validée ?
-id du validateur de l'activité
Table invités :
-id de l'activité
-id de l'inviteur
-id de la personne ayant réalisé l'invitation (si ça a été mis sur la note par quelqu'un d'autre)
-date/heure de l'invitation
-nom de l'invité
-prénom de l'invité
-validation de l'invité (au cas où y a trop d'invités, bah on peut dévalider pour qu'ils ne soient pas dans la liste d'invités)
= Log =
Table journal:
-id du journal
-type d'action (i.e. type de requete)
-paramètres de la requete (si transaction, id de la transaction, sinon on enregistre les parametres)
-utilisateur effectuant la requete
-ip de provenance
-client de l'utilisateur
-date
-resultat de la requete: ok, ou erreur (avec code d'erreur)
-moderation (false si on y a pas touché, true si elle a été annulée) (histoire de marquer ce qui a été annulé ou non...)
= Clubs =
Table clubadhérents :
-id club
-id adhérent
-fonction (= prez, trez, screz, ou simple adhérent)
= Consommations =
Table consommation
-id conso
-montant de la conso
-nom de la conso
-icone de la conso
-id de la categorie de la conso
-id de la destination du montant
-conso affichée ? (non = bouton "supprimé")
Table consommation-categorie
-id de la categorie
-nom de la categorie
-icone de la categorie (si jamais on veut aider un client graphique...)
-poids (pour trier dans un menu)
Ceci est le cahier des charges plus ou moins formel pour la note kfet 2018++
Definition de note kfet: système de gestion des adhérents du BdE (inscription, reinscription, mails), des notes des adhérents (historique des consos, solde), des consommations au bar (paiement), des activités (annonce sur le wiki, invitations), le tout de manière sécurisée, ou au moins sécurisante (confiance des utilisateurs)
= Gestion des adhérents =
La base doit contenir les informations sur les adhérents de l'association, notemment: numéro d'adhérent, nom, prenom, email, section, telephone, adresse, problèmes de santé, numéro de sécu, ainsi qu'une photo. Le système doit permettre l'adhésion ainsi que la réadhésion chaque année. Les infos de base (i.e. tout sauf numero d'adherent, pb de santé, numéro de sécu, les adhésions, nom et prénom) doivent pouvoir êtres modifiées par l'adhérent.
Penser à un état "vrai adhérent" (ou faux adhérent), quand on a besoin de créer des comptes fake (genre le compte du jardinier, ou des comptes temporaires), pour sortir de vraies listes d'adhérents.
On peut soit lier ça à la gestion des adhérents des clubs : y a un club BdE avec les adhérents qui vont bien, soit mettre un champ booléen.
Penser à indiquer sur les adhésions si WEI ou pas.
= Gestion des notes =
A chaque adhérent est associé une note, c'est à dire un solde d'argent couplé à un historique de transactions. Le tout est accessible en lecture par l'adhérent.
Un mail préiodique de rappel du solde négatif peut être configuré, ainsi qu'un mail périodique de relevé des consommations.
Les clubs peuvent aussi avoir une note.
= Gestion des consommations =
Il s'agit de noter les consommations des adhérents sur leur note, ainsi que les transferts entre notes. Les montants des transferts se font soit librement (transferts entre notes), soit suivant des montants prédéfinis (par exemple, le montant d'un paiement d'un coca est determiné et enregistré par la note: il faut pouvoir savoir de combien est ce montant, et il ne faut pas pouvoir payer un coca à un prix différent, et il faut pouvoir, si on en a le droit, gérer les montants de ces paiements prédéfinis).
Les transactions enregistrent donc un montant d'argent qui va d'un payeur à un receveur, à une date donnée, pour une raison donnée (paiement d'un coca, transfert d'argent, crédit par chèque, ...)
= Gestion des activités =
Chaque utilisateur peut proposer une activité. Après validation par un utilisateur disposant des droits appropriés, l'activité est publiée sur le wiki, et une liste d'invités est ouverte: chaque utilisateur peut inviter un nombre prédéfini de personnes à chaque activité, avec un nombre maximum prédéfini d'invitations sur l'année par personnes. Chaque invitation peut débiter automatiquement un certain montant sur la note de l'utilisateur qui invite.
= Sécurité =
Il est nécessaire d'identifier l'utilisateur du client pour réaliser la moindre opération d'écriture ou de lecture. Il faut de plus vérifier que l'utilisateur a les droits d'effectuer les opérations demandées.
Il faut de plus tout logguer, du moins ce qui est écriture: tout ce qui modifie la base (infos des adhérents, transactions, activités, ...), avec la date, l'utilisateur à l'origine de la transaction, ainsi que la machine à l'origine de la transaction (l'ip suffit).
L'identification se fait à l'aide d'un mot de passe lié à l'utilisateur, ou tout autre moyen laissé à l'imagination...
Je pensais aussi à une identification automatique par IP (exemple : ordi de la note, automatiquement loggé)
= Accès =
on laisse des utilisateurs bien identifiés faire pas mal de choses (au cr@ns, les cableurs), mais tout est enregistré, en cas de problème on peut faire la modification inverse, et on peut taper sur l'utilisateur qui a fait de la merde. Donc ceux qui ont les droits notes peuvent débiter qui ils veulent, vers n'importe quel club. Tenter de limiter ça, c'est obliger à mettre en place des regles complexes, qui limiteront les mouvements possibles, ne seront jamais parfaites, et reviendront au final à tout faire faire par ceux qui peuvent donner les droits...
Il faut penser à un système de gestion des permissions assez poussé et flexible.
Ou pas poussé.
Le système droits/surdroits/supreme me semble correct, après il faut diviser les droits.
= Organisation du protocole =
Le protocole sera fait d'échanges simples de commandes : le client envoie une commande et ses arguments, et le serveur délègue le traitement de la commande au module gérant ça. Le module renvoie ce qu'il veut au client, puis rend la main à la boucle principale du serveur.
Le début est simple : le client se connecte, dit "hello [identifiant du client]" (= nom et version du client, pour des raisons de stats héhé), et le serveur lui répond "hello tudoistidentifier" ou "hello tuesidentifie en tant que machin" (pour les identifications automatiques par IP).
Ensuite on entre dans la boucle principale.
= Données =
Les vérifications seront faites dans un premier temps par le serveur. (Quand on aura fait un truc vraiment bien, on pourra mettre ça au niveau de la base.)
Le client n'a en principe aucune vérification à faire, les erreurs sont gérées par le serveur qui renvoie un code d'erreur (une commande permettant d'avoir l'explication de l'erreur pour les êtres humains).
Les tables de la base de données seront décrites dans un autre document.
= Clubs =
Laisser un accès aux clubs pour qu'ils puissent voir leur historique comme tout le monde. Au moins.
Gestion des clubs BdE :
-basique : bureau, adresse mail de contact (ML), locaux, ... (ce qui permettra d'avoir une liste un peu plus à jour)
-avancée : permettre aux clubs de gérer leur liste d'adhérents et autres choses qu'ils pourraient vouloir gérer.
-> gestion des accès avec les badges RFID
-> désactivation des badges des gens en négatif ou s'appelant Lopez ou autres hin hin hin
Ensuite, faire un système permettant à un club / une assoce / le Cr@ns / l'ENS (histoire de payer son inscription par note kfet...) / le Hanjo / le Primavera quoi parce que les autres s'en foutent, de prélever automatiquement la note de quelqu'un, après une autorisation de cette personne. (La personne ayant à visiter une page par exemple.)
= Modules =
-Note
-Liste des adhérents (et lecture/modification des adhérents, gestion des droits)
-Informations sur un adhérent
-Infos
-Historique
-Droits
-Activités
-Liste des activités
-Invitation d'une personne
-Déclaration d'une activité
-Administration des activités
-Génération des PDF : liste des invités, des adhérents, des adhérents en négatif, qui n'ont pas la carte BdE
-Transactions prédéfinies (boutons)
-Gestion des clubs
= Migration =
Faut pouvoir récuperer la base actuelle...
(Voilà plein de lignes blanches)
\ No newline at end of file
Conventions :
> : ce que le client envoie
< : ce que le serveur répond
(à gauche c'est le client donc)
Le serveur est censé toujours répondre à une requête du client. Le serveur ne dit jamais rien au client comme ça par surprise.
Pour la communication de dictionnaires :
< OK nb_lignes
nom1:valeur1
nom2:valeur2
= Identification =
Commençons par le début.
== Bonjour ==
> hello [version du client]
< hello [version du serveur]
== Identification ==
> login [login]
< OK [login]
Il répond OK dans tous les cas parce qu'il est super trop méchant.
-lister les chèques (tous, ou non encore vérifié)
-valider un cheque
-invalider un chèque (ce qui invalide donc la transaction)
> auth [type] [args]
-[type] :
-pass :
Authentification simple par login/mot de passe
[paramètres] = [login] [mot de passe]
-autre on verra
Réponses :
< OK [id]
< NOK 1
1: mauvais login ou mot de passe (on ne précise pas comme pour la plupart des sites héhé)
2: pas le droit de se connecter (on pourrait interdire à certains ce droit pour des raisons ou d'autres ...)
on envoi un MOTD? commande à part pour le demander ? ok
Une fois identifié, il peut faire plein de choses: faut faire la liste de toutes les actions possibles...
= Aide =
> help
< liste des commandes autorisées
> help [commande]
< aide spécifique de la commande
= Erreurs =
> error [commande] [id]
< message d'erreur en toutes lettres
= Utilisateurs =
-Récuperer les infos
> user_get_info [id]
< dictionnaire d'infos
ou
< NOK erreur
101 : pas le droit
102 : id n'existe pas
-Rechercher
> user_search [mot]
< liste d'IDs
< NOK erreur
101 : pas le droit
-Modifier les infos
> user_set_info [id] [nom info] [valeur]
< OK
< NOK erreur
101 : pas le droit
102 : id n'existe pas
103 : nom info inconnue
104 : pas du bon type (genre tu veux dire que date de naissance = "boudin")
-Récuperer log
> user_get_log [id] [limite]
< liste des transactions
101 : pas le droit
102 : id n'existe pas
104 : je comprends pas la limite
= Note =
-ajouter transaction suivant un bouton
> note [id] [id transaction]
< OK
< NOK erreur
101 : pas le droit
102 : id n'existe pas
201 : id transaction n'existe pas
-ajouter transfert entre notes
> note_transfert [id from] [id to] [montant]
< OK
< NOK erreur
101 : pas le droit
102 : id from n'existe pas
104 : montant pas du bon type (négatif ou c'est pas un flottant)
105 : pas assez d'argent (sur le from bien sûr)
-ajouter crédit chèque/espece/virement
-ajouter débit
= Gestion de la note =
-obtenir la liste des categories
-obtenir la liste des boutons d'une categorie
-créer categorie
-créer un nouveau bouton
-modifier un bouton
-> paramètre de visibilité (on peut pas supprimer un bouton, mais le cacher)
-modifier categorie
-obtenir le nombre d'utilisation d'un bouton sur une période de temps
= Activités =
-obtenir la liste des activitées
-obtenir les infos sur une activité
-créer une activité
-modifier un paramètre d'une activité
-inviter quelqu'un a une activité
-modifier une invitation
-(de)valider une invitation
-obtenir la liste des invités à une activité (avec ou sans nombre d'invitations depuis le début de l'année)
-obtenir la liste de ses propres invités
= Droits =
-Voir les droits de quelqu'un
> user_get_rights
< liste des droits
-Modifier les droits de quelqu'un
> user_add_right [droit]
< OK
< NOK
101 : pas le droit
> user_del_right [droit]
< OK
< NOK
101 : pas le droit
-Dropper des droits pour la session courante ? (Comme le "Level 1" dans la note actuelle)
> session_set_rights [droit],[droit],[droit]
< OK [droit],[droit],[droit] <- les droits que la session a effectivement maintenant (= droits de l'utilisateur \inter droits demandés)
= Clubs =
-lister les membres d'un club
-ajouter un membre a un club
-modifier un membre d'un club (genre modifier le poste)
-supprimer un membre d'un club
-lister les clubs
= Trésorerie =
-lister les chèques (tous, ou non encore vérifié)
-valider un cheque
-invalider un chèque (ce qui invalide donc la transaction)
-lister les virements (tous, ou non encore vérifié)
-valider un virement
-invalider un virement (ce qui invalide donc la transaction)
= Log =
-récuperer log (criteres de recherche?)
-marquer une ligne comme modérée
# -*- coding:utf-8 -*-
# Ce script sert à vérifier que tout se passe bien dans la base.
# Il est lancé toutes les 24h, si il y a un problème, il envoie un mail à
# l'adresse mail mails_integrity_problem (déclarée dans config.py)
# à terme, son rôle sera d'arrêtée la note si elle est incohérente.
import time
notedir = "/home/vincent/note/new_note"
# Ce script n'est pas forcément dans le basedir de la note,
# mais il a besoin d'en importer des choses
import sys
sys.path.append(notedir)
sys.path.append(notedir+"/mail")
# fichier de config python
import config
# script d'envoi de mails
import mail
# module d'accès à la bdd
from pyPgSQL import PgSQL
# normalement on aura besoin que d'un seul curseur puisqu'on ne fait que des SELECT
con=PgSQL.connect(database="note")
cur=con.cursor()
ok,errors=True,[]
# On vérifie l'intégrité des soldes pour chaque compte,
# c'est-à-dire que son solde est égal à la somme de ses transactions.
integrite_solde_perso = """SELECT idbde,gagne-perdu as calcul,effectif,effectif-(gagne-perdu) as en_trop
FROM
(SELECT idbde,
(SELECT COALESCE(sum(montant*quantite),0)
FROM transactions
WHERE destinataire=main.idbde AND valide='t') as gagne,
(SELECT COALESCE(sum(montant*quantite),0)
FROM transactions
WHERE emetteur=main.idbde AND valide='t') as perdu,
(SELECT solde
FROM comptes WHERE idbde=main.idbde) as effectif
FROM comptes as main)
AS calculs
WHERE gagne-perdu!=effectif;"""
cur.execute(integrite_solde_perso)
non_integres = cur.fetchall()
for i in range(len(non_integres)):
#NB: même le bde n'a pas le droit d'être non intègre.
# si son solde devient trop énorme, on crée un compte spécial de backup.
ok=False
errors.append(["Solde personnel non intègre",dict(non_integres[i])])
# On vérifie l'intégrité globale. IE la somme des soldes doit toujours êtres nulle.
cur.execute("SELECT sum(solde) FROM comptes;")
result = cur.fetchone()
if result[0]!=0:
ok=False
errors.append(["Somme globale des soldes non nulle",dict(result)])
# On vérifie que chaque bouton pointe vers un club
cur.execute("""SELECT idbouton
FROM
(SELECT boutons.id as idbouton,comptes.type='club' as faux FROM boutons,comptes WHERE boutons.destinataire=comptes.idbde)
AS plop
WHERE faux=false;""")
boutonsErrones = cur.fetchall()
boutonsErrones = [i[0] for i in boutonsErrones]
if len(boutonsErrones)!=0:
ok=False
errors.append(["Bouton(s) qui ne pointent pas vers un club",list(boutonsErrones)])
date = time.strftime("%Y-%m-%d %T")
if not ok:
# On écrit tout ça dans un fichier de log.
f = open(config.integrityerrorfile,"a")
f.write("Integrity Error detected on %s.\nDetails :\n"%(date))
f.write(str(errors))
f.close()
# Et on prévient par mail.
msg = "Date d'envoi : %s\nIl y a des erreurs de cohérence dans la base.\n\n"%(date)
for i in errors:
msg += "%s\n%s\n\n"%(i[0],i[1])
msg += "-- \nNote Kfet 2015"
mail.envoie_mail("note-errors@crans.org",config.mails_integrity_problem,"Incohérence dans la base",msg)
# Dans tous les cas, on écrit dans un fichier de log qu'on a fait la vérification.
f=open(config.integritylogfile,"a")
if ok:
f.write("%s Integrity checked.\n"%(date))
else:
f.write("%s Integrity failed ! (see %s for details)\n"%(date,config.integrityerrorfile))
f.close()
#!/bin/bash
# Comment ça se script ne sert qu'à lancer un script python ?
python checkintegrity.py
exit 0
Integrity Error detected on 2011-12-23 02:49:54.
Details :
[['Bouton(s) qui ne pointent pas vers un club', [[5]]]]Integrity Error detected on 2011-12-23 02:56:09.
Details :
[['Bouton(s) qui ne pointent pas vers un club', [5]]]
\ No newline at end of file
SELECT numcbde,gagne-perdu as calcul,effectif,effectif-(gagne-perdu) as a_enlever
FROM
(SELECT numcbde,
(SELECT sum(montant*quantite)