Forum Programmation.python Problème de doublons dans un fichier txt

Posté par . Licence CC by-sa
0
10
août
2015

Bonjour Encore un probleme a Résoudre !!

J'ai un script python qui interroge mon compteur d'eau pour récupérer des valeurs . Celui ci est lancé toute les 15 minutes a l'aide d'une tache Cron ,et m’écrit un fichier txt , voila ce que j’obtiens

Mon Aug 10 10:00:04 2015 /0/73800/1
Mon Aug 10 10:15:04 2015 /0/73800/1
Mon Aug 10 10:30:04 2015 /0/73800/1
Mon Aug 10 10:45:04 2015 /0/73800/1
Mon Aug 10 11:00:04 2015 /0/73800/1
Mon Aug 10 11:15:04 2015 /0/73800/1
Mon Aug 10 11:30:04 2015 /0/73800/1
Mon Aug 10 11:45:04 2015 /0/73900/1

Donc au bout d'un moment mon fichier txt devient énorme et inexploitable . je dois donc agir manuellement en supprimant des valeurs pour garder les lignes ou l'impulsion du compteur est passé pour obtenir cela .

Tue Jul 28 07:15:04 2015 /0/72100/1
Tue Jul 28 20:45:04 2015 /0/72200/1
Wed Jul 29 08:45:04 2015 /0/72300/1
Wed Jul 29 11:45:03 2015 /0/72400/1
Thu Jul 30 20:30:04 2015 /0/72500/1
Fri Jul 31 09:15:04 2015 /0/72600/1
Sat Aug 1 18:45:04 2015 /0/72700/1

Comment faire pour automatiser cette tache ? Faut'il modifier le script qui interroge le compteur ou alors créer un nouveau script qui va aller modifié le fichier txt créer ?

Ensuite les valeurs récupérer dans ce fichier me servent pour faire une page Web ( Un Bar Graph )donc plus mon fichier est gros ,plus l'affichage de la page est long .
DLFP
Merci d'avance pour votre aide

  • # Intuitivement...

    Posté par . Évalué à 4.

    Person je modifierais le script pour qu'il n'écrive pas de doublon. Lors de la lecture du compteur récupère les 10 derniers caractère de la dernière ligne du fichier de log (exemple avec tail --bytes=10 (dans un subprocess) ), compare avec la chaine que tu allais écrire et si identique n'écris rien.

    • [^] # Re: Intuitivement...

      Posté par . Évalué à 1. Dernière modification le 10/08/15 à 13:10.

      Voila mon code python ,

      #!/usr/bin/python
      
      from sys import argv
      from time import sleep, asctime, localtime, time
      from os import path
      
      ext=''
      # compteur = 000dd379
      # A ou B ou vide
      cpt=''
      cptA=0
      cptB=0
      
      error_file='/home/www/logs/cpterr.log'
      
      if len(argv)>=2:
          ext=argv[1]
      if len(argv)>=3:
          cpt=argv[2].upper()
      
      sensorFile='/sys/bus/w1/devices/1d-0000' + ext + '/w1_slave'
      imp2liter=100
      lineA=''
      lineB=''
      crcA=''
      crcB=''
      
      def get_outputs():
          if path.isfile(sensorFile):
              i=0
              for test_output in range(10):
                  i+=1
                  f = open(sensorFile, "r")
                  text = f.read().strip()
                  f.close()
                  #
                  if len(text.split("\n"))==4:
                      # compteur A
                      lineA = text.split("\n")[2]
                      crcA = lineA.split("=")[1].strip(" c")
                      if (crcA=="YES"):
                          cptA = long(lineA.split("=")[2])*imp2liter
                          #print ('Compteur Ai = '+str(cptA))
                          if cpt=='A':
                              if cptA >= 0 :
                                  #print ('Compteur Af = '+str(cptA))
                                  return ('/'+str(cptA)+'/'+str(i))
                              else:
                                  #print "ERREUR"
                                  outfile = open(error_file, 'ab')
                                  outfile.write(text)
                                  outfile.close()
                      #
                      # compteur B
                      lineB = text.split("\n")[3]
                      crcB = lineB.split("=")[1].strip(" c")
                      if (crcB=="YES"):
                          cptB = long(lineB.split("=")[2])*imp2liter
                          #print ('Compteur Bi = '+str(cptB))
                          if cpt=='B':
                              if cptB >= 0 :
                                  #print ('Compteur Bf = '+str(cptB))
                                  return ('/'+str(cptB)+'/'+str(i))
                              else:
                                  #print "ERREUR"
                                  outfile = open(error_file, 'ab')
                                  outfile.write(text)
                                  outfile.close()
                      #
                      if (crcA=="YES" and crcB=="YES"):
                          if cpt=='':
                              if cptA >= 0 and cptB >= 0 :
                                  return ('/'+str(cptA)+'/'+str(cptB)+'/'+str(i))
                              else:
                                  #print "ERREUR"
                                  outfile = open(error_file, 'ab')
                                  outfile.write(text)
                                  outfile.close()
                      #
                  sleep(1)
              return ('N/A')
          else:
              return ('N/A')
      
      # Read sensors 
      results = get_outputs()
      if results<>'N/A':
          localtime = asctime( localtime(time()) )
          print localtime, results
      
      end

      par contre c'est la tache cron qui indique ou est le fichier hist-CPT

      */15 * * * * root python /home/www/py/1d-0000-hist.py 000dd379 >> /home/www/logs/hist-CPT 
      
      • [^] # Re: Intuitivement...

        Posté par . Évalué à 2.

        Donc il faut modifier le cron pour passer le nom du fichier en argument à ton script python et ensuite modifier ton script python qui au lieu d'un print devra faire une écriture à la fin du fichier reçu en paramètre seulement si il y en as besoin.

      • [^] # Re: Intuitivement...

        Posté par (page perso) . Évalué à 5.

        Avec un peu d'imagination les solutions sont nombreuses :

        • Comme suggéré au dessus tu passes le nom du fichier en paramètre, tu récupères la dernière ligne (par exemple http://stackoverflow.com/questions/3346430/most-efficient-way-to-get-first-and-last-line-of-file-python ) et tu n'écris que si la nouvelle ligne est différente.
        • Tu stockes les dernières valeurs dans un fichier caché (bon dans l'absolu, pour faire les choses proprement, il faudrait faire attention et prévoir les cas où le script est lancé plusieurs fois en parallèle), tu lis les nouvelles valeurs, et si elles sont égales aux anciennes tu renvoies 'N/A' (même si un simple None ferait l'affaire mais c'est une autre histoire).
        • Plutôt qu'un script lancé par cron, tu fais un daemon qui tourne en permanence (et peut garder les dernières valeurs dans une variable). ici c'est sûrement overkill, mais si voulais faire un truc plus intelligent qui se met par exemple à lire les valeurs plus souvent s'il s'aperçoit qu'elles changent rapidement (grosse fuite :) ) ça serait une piste.

        Sinon quelques remarques sur ton code:

        i=0
        for test_output in range(10):
            i+=1
            ...

        s'écrit en Python idiomatique:

        for i, test_output in enumerate(range(10), start=1):
           ...
        f = open(sensorFile, "r")
        text = f.read().strip()
        f.close()

        gagnerait à être écrit (plus court, le fichier est fermé même en cas d'erreur):

        with open(sensorFile, "r") as f:
            text = f.read().strip()

        Ça serait mieux que tes tas de text.split("\n") soit fait une seule fois au début et stocké dans une variable.
        Il n'y a pas besoin de 'parenthéser' les return ou les if.
        '/'+str(cptA)+'/'+str(cptB)+'/'+str(i) devient en plus concis/lisible (et plus rapide je pense) '/%s/%s/%s' % (cptA, cptB, i) (les string ont aussi une méthode format() mais je suis resté à mes vieilles habitudes).
        Ça fait longtemps que <> est obsolète au profit de !=.

        • [^] # Re: Intuitivement...

          Posté par . Évalué à 2.

          Mon niveau de programmation est très très bas !!!
          Ce code n'est pas de moi c'est un ami qui me l'a fait il y a 2 ans quand j’avais commencé un projet de domotique sur un raspberry .
          Toute la partie électronique est de moi, et la partie programmation c’était lui.
          j'essaye donc de me débrouillé au mieux avec mes faibles connaissance .

          • [^] # Re: Intuitivement...

            Posté par (page perso) . Évalué à 4.

            Tu n'as pas à te justifier d'être débutant, le forum est là pour ça (sinon je ne me fatiguerai pas à répondre). En revanche, dans la mesure où tu vas faire évoluer ce code, il va bien falloir le comprendre un minimum; ça tombe bien car Python est plutôt simple à appréhender, et ce code ne fait pas grand chose non plus. L'améliorer est donc tout à fait possible pour un débutant, et c'est un bon moyen d'apprendre (potentiellement plus que d'écrire du code trivial au kilomètre).

            La question est donc de savoir si tu as compris comment implémenter la solution numéro 1 que je t'ai donnée (sûrement la plus adaptée).

            • [^] # Re: Intuitivement...

              Posté par . Évalué à 1.

              Bonjour GuieA_7 et merci pour ta réponse , je comprend ce qu'il faut faire , mais comme j'ai dit précédemment, je ne sais pas écrire du code .

              • [^] # Re: Intuitivement...

                Posté par (page perso) . Évalué à 2.

                Ok, et donc que veux-tu ?

                • Que quelqu'un écrive le code à ta place.
                • Avoir plus de précisions sur comment le coder toi-même.
                • Une solution qui ne nécessite pas de code (auquel cas la réponse donnée plus bas par j_m me semble très bien).
                • [^] # Re: Intuitivement...

                  Posté par . Évalué à 1.

                  Pour être honnête , je souhaiterais trouver une personne qui me modifie le code , car je ne sais pas le faire et j'ai peur de tout faire planter.
                  J'ai oublier de dire que je récupère les valeurs du fichier txt pour faire une page Web avec 3 Bar graph (Journalier ,Mensuel ,annuel )
                  Il y a quelques mois j’ai essayé de le faire une BD rddtool mais je n'ai pas trouver la solution .

                  http://linuxfr.org/forums/linux-debutant/posts/rrdtool-base-de-donnees

                  PS: j'ai ajouter une photos dans le post principale .

                  • [^] # Re: Intuitivement...

                    Posté par . Évalué à 2. Dernière modification le 11/08/15 à 20:53.

                    Peut tu essayer cela ? (Nota: Pour l'instant le fichier de log destination est codé en dur, c'est juste pour essais )

                        #!/usr/bin/python
                        import subprocess
                        from sys import argv
                        from time import sleep, asctime, localtime, time
                        from os import path
                    
                        ext=''
                        # compteur = 000dd379
                        # A ou B ou vide
                        cpt=''
                        cptA=0
                        cptB=0
                    
                        error_file='/home/www/logs/cpterr.log'
                    
                        if len(argv)>=2:
                            ext=argv[1]
                        if len(argv)>=3:
                            cpt=argv[2].upper()
                    
                        sensorFile='/sys/bus/w1/devices/1d-0000' + ext + '/w1_slave'
                        imp2liter=100
                        lineA=''
                        lineB=''
                        crcA=''
                        crcB=''
                    
                        def get_outputs():
                            if path.isfile(sensorFile):
                                i=0
                                for test_output in range(10):
                                    i+=1
                                    f = open(sensorFile, "r")
                                    text = f.read().strip()
                                    f.close()
                                    #
                                    if len(text.split("\n"))==4:
                                        # compteur A
                                        lineA = text.split("\n")[2]
                                        crcA = lineA.split("=")[1].strip(" c")
                                        if (crcA=="YES"):
                                            cptA = long(lineA.split("=")[2])*imp2liter
                                            #print ('Compteur Ai = '+str(cptA))
                                            if cpt=='A':
                                                if cptA >= 0 :
                                                    #print ('Compteur Af = '+str(cptA))
                                                    return ('/'+str(cptA)+'/'+str(i))
                                                else:
                                                    #print "ERREUR"
                                                    outfile = open(error_file, 'ab')
                                                    outfile.write(text)
                                                    outfile.close()
                                        #
                                        # compteur B
                                        lineB = text.split("\n")[3]
                                        crcB = lineB.split("=")[1].strip(" c")
                                        if (crcB=="YES"):
                                            cptB = long(lineB.split("=")[2])*imp2liter
                                            #print ('Compteur Bi = '+str(cptB))
                                            if cpt=='B':
                                                if cptB >= 0 :
                                                    #print ('Compteur Bf = '+str(cptB))
                                                    return ('/'+str(cptB)+'/'+str(i))
                                                else:
                                                    #print "ERREUR"
                                                    outfile = open(error_file, 'ab')
                                                    outfile.write(text)
                                                    outfile.close()
                                        #
                                        if (crcA=="YES" and crcB=="YES"):
                                            if cpt=='':
                                                if cptA >= 0 and cptB >= 0 :
                                                    return ('/'+str(cptA)+'/'+str(cptB)+'/'+str(i))
                                                else:
                                                    #print "ERREUR"
                                                    outfile = open(error_file, 'ab')
                                                    outfile.write(text)
                                                    outfile.close()
                                        #
                                    sleep(1)
                                return ('N/A')
                            else:
                                return ('N/A')
                    
                        # Read sensors 
                        results = get_outputs()
                        if results<>'N/A':
                            localtime = asctime( localtime(time()) )
                            logfile=open("""/home/www/logs/hist-CPT""","r+")    #RW mode
                            lastlog = subprocess.check_output(["tail","--bytes=11","""/home/www/logs/hist-CPT"""])
                            if lastlog!=results:
                                logfile.seek(0,2)
                                logfile.write(localtime+' '+results+"\n")
                            logfile.close()
                    • [^] # Re: Intuitivement...

                      Posté par . Évalué à 1. Dernière modification le 11/08/15 à 16:30.

                      Merci TheBreton pour ton aide mais , excuse moi je ne comprend pas ce que veux dire (le fichier de log destination est codé en dur).
                      Afin de ne pas tout cassé j'ai créer un fichier hist-CPT2 a partir de mon hist-CPT ( j'ai bien sur modifier les lignes 89 et 90 de ton script avec hist-CPT2)
                      j'ai lancé le fichier en console de cette maniere
                      python 11.py
                      Aucune erreur , mais rien ne va s’écrire dans hist-CPT2

                      • [^] # Re: Intuitivement...

                        Posté par . Évalué à 2. Dernière modification le 11/08/15 à 17:03.

                        Normal, il manque l'argument [capteur] fournit par le cron (000dd379)

                        Tu as nommé le fichier "11.py"? Tu as bien fais "chmod +x 11.py" ?
                        Alors il suffit de taper
                        ./11.py 000dd379
                        Sinon tape
                        python 11.py 000dd379

                        Note: comme nom de fichier "11" c'est pas tres explicite, appelle le "test.py" par exemple.

                        Merci TheBreton pour ton aide mais , excuse moi je ne comprend pas ce que veux dire (le fichier de log destination est codé en dur).
                        Afin de ne pas tout cassé j'ai créer un fichier hist-CPT2 a partir de mon hist-CPT ( j'ai bien sur modifier les lignes 89 et 90 de ton script avec hist-CPT2)

                        Voila, c'est exactement ce que veut dire codé en dur, pour modifier le fonctionnement du logiciel il faut modifier le code. Une version propre voudrait que le fichier de log sur lequel travailler soit passé en argument lors de l'appel du script.

                        • [^] # Re: Intuitivement...

                          Posté par . Évalué à 1.

                          Oui tu as raison python 11.py 000dd379 écrit bien dans le fichier hist-CPT2 .

                          mais j'ai toujours des doublons

                          Tue Aug 11 17:03:29 2015 /0/74000/1
                          Tue Aug 11 17:04:08 2015 /0/74000/1
                          Tue Aug 11 17:04:11 2015 /0/74000/1
                          Tue Aug 11 17:04:14 2015 /0/74000/1
                          Tue Aug 11 17:10:26 2015 /0/74000/1
                          Tue Aug 11 17:10:30 2015 /0/74000/1

                          Pour les essais je vais le renommer en test.py

                          • [^] # Re: Intuitivement...

                            Posté par . Évalué à 2.

                            Je crois que je vois le soucis, il faut ajouter une ligne dans le script pour que la comparaison soit juste et le modifier lors de l'ecriture cela devrais marcher

                                lastlog = subprocess.check_output(["tail","--bytes=11","""/home/www/logs/hist-CPT"""])
                                results=results+"\n"
                                if lastlog!=results:
                                    logfile.seek(0,2)
                                    logfile.write(localtime+' '+results)
                                logfile.close()
                            
                            • [^] # Re: Intuitivement...

                              Posté par . Évalué à 1.

                              Super Super un grand merci ça fonctionne , je vais programmer la tache cron et le laisser tourner .

                              • [^] # Re: Intuitivement...

                                Posté par . Évalué à 1. Dernière modification le 12/08/15 à 09:37.

                                Ok bonne chose de faite alors…sauf que le scripts ne fonctionneras plus lors du passage du compteur a plus de 100000 (sauf si le compteur repasse à 0 après 99999 ?)
                                dans le script il faudrait dé-commenter les lignes de debugs pour voir ce que renvoi les capteurs pour ajouter un 0 devant si inférieur à 100000

                                print ('Compteur Ai = '+str(cptA))
                                et
                                print ('Compteur Bi = '+str(cptB))

  • # Base de donnees sqlite

    Posté par . Évalué à 0.

    Salut,

    Tu pourrais utiliser une petite base de données sqlite constituée d'une table avec sqlalchemy. Ton script deviendrait d'un coup plus facile a maintenir!

    Qu'en penses-tu ?

    • [^] # Re: Base de donnees sqlite

      Posté par . Évalué à 1.

      Bonjour oyoun j'ai un autre script qu'un membre du forum m'avait fait il y a quelques mois qui écrit dans une BD RDDTool , la BD se remplie bien mais impossible d'exploité ensuite les données , c'est pour cela que je veux repartir avec mon fichier txt

  • # Archivage des fichiers

    Posté par . Évalué à 2.

    Tu devrais plutôt passer régulièrement derrière ton script avec un autre script pour archiver tes fichiers en .gz. Comme le contenu est répétitif le facteur de compression sera très intéressant.

    Puis si tu as besoin de faire des calculs sur des données d'historiques, ou des tris, tu peux faire ça dans un script à part et conserver les résultats dans un fichier de texte simple.

    Notes qu'avec l'exemple que tu donnes j'ai du mal à comprendre que la taille du fichier puisse poser problème. Tu sais que tu peux déjà faire pas mal d'opérations de filtrage avec seulement grep en ligne de commande:

    $ grep 2015 fichier.txt > anno_2015.txt

    Va sélectionner uniquement l'année 2015 et la mettre dans le fichier "anno_2015.txt". grep ne va jamais manquer de mémoire, même avec un très gros fichier car il travaille ligne par ligne.

    De la même façon tu peux faire un sélection sur un numéro de sonde particulier, ou en éliminer avec grep -v. Tu peux enchaîner des filtres avec le pipe |

    • [^] # Re: Archivage des fichiers

      Posté par . Évalué à 3. Dernière modification le 12/08/15 à 23:38.

      C’est quand même plus simple de ne pas créer de doublon même si ça permet en effet de garder l’information sur la fréquence des mesures, par exemple…

      Concernant le grep, d’une il sera toujours possible de l’utiliser de cette manière s’il n’y a pas de doublon, de deux la commande telle que tu l’écris va également sélectionner une ligne d’une autre année si la valeur relevée est de ??2015, ou 2015?? (avec ? n’importe quel chiffre de 0 à 9, voir une valeur genre FF2015 mais bon…). Il faudrait faire ainsi dans son cas :

      grep " 2015 " fichier_in > fichier_out
      • [^] # Re: Archivage des fichiers

        Posté par . Évalué à 3. Dernière modification le 13/08/15 à 10:32.

        C’est quand même plus simple de ne pas créer de doublon même si ça permet en effet de garder l’information sur la fréquence des mesures, par exemple…

        Je suppose tu trouves ça plus simple parce que on fait tout dans un seul fichier de script ou bien avec la même ligne de cron.

        Moi je trouve ça plus simple au niveau du partage des responsabilités. Le code source qu'il nous a montré a une responsabilité qui s'énonce simplement: Extraction des valeurs à intervalle régulier.

        En input les capteurs, en output le fichier de log. C'est simple et sans surprise. Et ça rassure sur la fiabilité des données. Parce quand on a des données historiques, ce qu'on veut c'est que ça soit fiable et qu'il n'y ait pas des bugs qui se baladent. Les données polluées par des bugs sont irrémédiablement perdues.

        Le fichier de log est trop gros? On compresse. C'est un problème super connu, il suffit de rgarder /var/log il est plein de .gz

        Ensuite, on fait du filtrage ou de l'aggrégation avec des scripts séparés. On fait ça dans seconds temps, il y a de bonne chances pour qu'on crée régulièrement des nouveaux scripts et on ne sait pas à l'avance si des précédentes opérations de filtrage vont pas être dérengeantes.

        Imagine qu'on veut afficher simplement les valeurs des la journée avec la granularité de temps la plus fine possible, le 1/4 d'heures. Le programmeur devra alourdir son script d'une logique de décompression pour récupérer les valeurs éjectées.

        • [^] # Re: Archivage des fichiers

          Posté par . Évalué à 3.

          j'adhere au discours de J_M sur l'aspect KIS

          le script de collecte fait sa collecte et remplit la base
          le script de graphe prend ce dont il a besoin et genere son graphique

          chacun son boulot.

          et pour les logs trop gros,
          un logrotate devrait suffire à compresser au dela d'une certaine taille, faire une rotation, etc.

          voir meme supprimer les fichiers de logs une fois que les données sont dans la base RRD.

Suivre le flux des commentaires

Note : les commentaires appartiennent à ceux qui les ont postés. Nous n'en sommes pas responsables.