ScolaSync 1.0
usbThread.py
Aller à la documentation de ce fichier.
00001 # -*- coding: utf-8 -*-    
00002 # $Id: usbThread.py 47 2011-06-13 10:20:14Z georgesk $  
00003 
00004 licenceEn="""
00005     file usbThread.py
00006     this file is part of the project scolasync
00007     
00008     Copyright (C) 2010 Georges Khaznadar <georgesk@ofset.org>
00009 
00010     This program is free software: you can redistribute it and/or modify
00011     it under the terms of the GNU General Public License as published by
00012     the Free Software Foundation, either version3 of the License, or
00013     (at your option) any later version.
00014 
00015     This program is distributed in the hope that it will be useful,
00016     but WITHOUT ANY WARRANTY; without even the implied warranty of
00017     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018     GNU General Public License for more details.
00019 
00020     You should have received a copy of the GNU General Public License
00021     along with this program.  If not, see <http://www.gnu.org/licenses/>.
00022 """
00023 
00024 import subprocess, threading, re, os.path, time
00025 
00026 _threadNumber=0
00027 
00028 ##
00029 # 
00030 #     Une classe pour tenir un registre des threads concernant les baladeurs.
00031 #     
00032 class ThreadRegister:
00033 
00034     ##
00035     # 
00036     #         Le constructure met en place un dictionnaire
00037     #         
00038     def __init__(self):
00039         self.dico={}
00040 
00041     def __str__(self):
00042         return "ThreadRegister: %s" %self.dico
00043         
00044     ##
00045     # 
00046     #         @param ud un disque
00047     #         @param thread un thread
00048     #         Empile un thread pour le baladeur ud
00049     #         
00050     def push(self, ud, thread):
00051         if ud.owner not in self.dico.keys():
00052             self.dico[ud.owner]=[thread]
00053         else:
00054             self.dico[ud.owner].append(thread)
00055 
00056     ##
00057     # 
00058     #         @param ud un disque
00059     #         @param thread un thread
00060     #         Dépile un thread pour le baladeur ud
00061     #         
00062     def pop(self, ud, thread):
00063         self.dico[ud.owner].remove(thread)
00064 
00065     ##
00066     # 
00067     #         Indique si le disque est occupé par des threads
00068     #         @param owner le propriétaire du disque
00069     #         @return les données associées par le dictionnaire
00070     #         
00071     def busy(self, owner):
00072         if owner in self.dico.keys():
00073             return self.dico[owner]
00074         return []
00075 
00076 globalThreads=ThreadRegister()
00077 
00078 ##
00079 # 
00080 #     Évite d'avoir des <i>slashes</i> dans un nom de thread
00081 #     @return la fin du nom de chemin, après le dernier <i>slash</i> ;
00082 #     si le chemin ne finit pas bien, remplace les <i>slashes</i> par
00083 #     des sous-tirets "_".
00084 #     
00085 def _sanitizePath(path):
00086     pattern=re.compile(".*([^/]+)")
00087     m=pattern.match(str(path))
00088     if m:
00089         return m.group(1)
00090     else:
00091         return str(path).replace('/','_')
00092 
00093 ##
00094 # 
00095 #     fabrique un nom de thread commençant par th_, suivi d'un nombre unique,
00096 #     suivi d'une chaîne relative à la clé USB
00097 #     @param ud une instance de uDisk
00098 #     @return un nom de thread unique
00099 #     
00100 def _threadName(ud):
00101     global _threadNumber
00102     name="th_%04d_%s" %(_threadNumber,_sanitizePath(ud.path))
00103     _threadNumber+=1
00104     return name
00105 
00106 ##
00107 # 
00108 #     Renvoie la date et l'heure dans un format court
00109 #     @return une chaîne donnée par strftime et le format %Y/%m/%d-%H:%M:%S
00110 #     
00111 def _date():
00112     return time.strftime("%Y/%m/%d-%H:%M:%S")
00113 
00114 ##
00115 # 
00116 #     Lance une commande dans un shell, et journalise la réussite ou l'échec
00117 #     @param cmd la commande shell, si cette commande contient des points-virgules elle sera découpée en plusieurs sous-commandes, chacune traitée séparément.
00118 #     @param logfile le fichier de journalisation
00119 #     
00120 def _call(cmd, logfile):
00121     for command in cmd.split(";"):
00122         okToLog="echo [%s] Success: %s >> %s" %(_date(), command, logfile)
00123         koToLog="echo [%s] Error: %s >> %s" %(_date(), command, logfile)
00124         cmd1="(%s && %s) || %s" %(command, okToLog, koToLog)
00125         subprocess.call(cmd1, shell=True)
00126 
00127 ##
00128 # 
00129 #     Une classe abstraite
00130 #      Cette classe sert de creuset pour les classe servant aux copies
00131 #      et aux effacement.
00132 #     
00133 class abstractThreadUSB(threading.Thread):
00134     ##
00135     # 
00136     #         Constructeur
00137     #         Crée un thread pour copier une liste de fichiers vers une clé USB.
00138     #         @param ud l'instance uDisk correspondant à une partition de clé USB
00139     #         @param fileList la liste des fichiers à traiter
00140     #         @param subdir un sous-répertoire de la clé USB
00141     #         @param dest un répertoire de destination si nécessaire, None par défaut
00142     #         @param logfile un fichier de journalisation, /dev/null par défaut
00143     #         
00144     def __init__(self,ud, fileList, subdir, dest=None, logfile="/dev/null"):
00145         threading.Thread.__init__(self,target=self.toDo,
00146                                   args=(ud, fileList, subdir, dest, logfile),
00147                                   name=_threadName(ud))        
00148         self.cmd=u"echo This is an abstract method, don't call it"
00149         self.ud=ud
00150         ud.threadRunning=True
00151         self.fileList=fileList
00152         self.subdir=subdir
00153         self.dest=dest
00154         self.logfile=logfile
00155 
00156     ##
00157     # 
00158     #         Renvoie une chaîne informative sur le thread
00159     #         @return une chaine donnant des informations sur ce qui va
00160     #         se passer dans le thread qui a été créé.
00161     #         
00162     def __str__(self):
00163         result="%s(\n" %self.threadType()
00164         result+="  ud       = %s\n" %self.ud
00165         result+="  fileList = %s\n" %self.fileList
00166         result+="  subdir   = %s\n" %self.subdir
00167         result+="  dest     = %s\n" %self.dest
00168         result+="  logfile  = %s\n" %self.logfile
00169         result+="  cmd      = %s\n" %self.cmd
00170         result+="\n"
00171         return result
00172 
00173     ##
00174     # 
00175     #         @return une chaîne courte qui informe sur le type de thread
00176     #         
00177     def threadType(self):
00178         return "abstractThreadUSB"
00179 
00180     ##
00181     # 
00182     #         La fonction abstraite pour les choses à faire
00183     #         @param ud l'instance uDisk correspondant à une partition de clé USB
00184     #         @param fileList la liste des fichiers à traiter
00185     #         @param subdir un sous-répertoire de la clé USB
00186     #         @param dest un répertoire de destination
00187     #         @param logfile un fichier de journalisation
00188     #         
00189     def toDo(self, ud, fileList, subdir, dest, logfile):
00190         # ça ne fait rien du tout pour un thread abstrait
00191         pass
00192     
00193 ##
00194 # 
00195 #     Classe pour les threads copiant vers les clés USB
00196 #     
00197 class threadCopyToUSB(abstractThreadUSB):
00198     ##
00199     # 
00200     #         Constructeur
00201     #         Crée un thread pour copier une liste de fichiers vers une clé USB.
00202     #         @param ud l'instance uDisk correspondant à une partition de clé USB
00203     #         @param fileList la liste des fichiers à copier
00204     #         @param subdir le sous-répertoire de la clé USB où faire la copie
00205     #         @param logfile un fichier de journalisation, /dev/null par défaut
00206     #         
00207     def __init__(self,ud, fileList, subdir, logfile="/dev/null"):
00208         abstractThreadUSB.__init__(self,ud, fileList, subdir, dest=None, logfile=logfile)
00209         self.cmd=u"mkdir -p '{toDir}'; cp -R '{fromFile}' '{toFile}'"
00210 
00211     ##
00212     # 
00213     #         @return une chaîne courte qui informe sur le type de thread
00214     #         
00215     def threadType(self):
00216         return "threadCopyToUSB"
00217 
00218     ##
00219     # 
00220     #         Copie une liste de fichiers vers une clé USB sous un répertoire donné.
00221     #          Ce répertoire est composé de ud.visibleDir() joint au
00222     #          sous-répertoire subdir.
00223     #          À chaque fichier ou répertoire copié, une ligne est journalisée dans le
00224     #          fichier de journal de l'application.
00225     #         @param ud l'instance uDisk correspondant à une partition de clé USB
00226     #         @param fileList la liste des fichiers à copier
00227     #         @param logfile un fichier de journalisation
00228     #         @param subdir le sous-répertoire de la clé USB où faire la copie
00229     #         
00230     def toDo(self, ud, fileList, subdir, dest, logfile):
00231         global globalThreads
00232         globalThreads.push(ud, self)
00233         while subdir[0]=='/':
00234             subdir=subdir[1:]
00235         destpath=os.path.join(ud.ensureMounted(),ud.visibleDir(),subdir)
00236         for f in fileList:
00237             fileName=os.path.basename(f)
00238             cmd=self.cmd.format(fromFile=f,
00239                                 toDir=destpath,
00240                                 toFile=os.path.join(destpath,fileName))
00241             _call(cmd,logfile)
00242         globalThreads.pop(ud, self)
00243 
00244 ##
00245 # 
00246 #     Classe pour les threads copiant depuis les clés USB
00247 #     
00248 class threadCopyFromUSB(abstractThreadUSB):
00249     ##
00250     # 
00251     #         Constructeur
00252     #         Crée un thread pour copier une liste de fichiers depuis une clé USB
00253     #         vers un répertoire de disque.
00254     #         @param ud l'instance uDisk correspondant à une partition de clé USB
00255     #         @param fileList la liste des fichiers à copier
00256     #         @param subdir le sous-répertoire de la clé USB d'où faire la copie
00257     #         @param dest un répertoire de destination
00258     #         @param logfile un fichier de journalisation, /dev/null par défaut
00259     #         
00260     def __init__(self,ud, fileList, subdir=".", dest="/tmp",
00261                  rootPath="/", logfile="/dev/null"):
00262         abstractThreadUSB.__init__(self,ud, fileList, subdir, dest=dest,
00263                                    logfile=logfile)
00264         self.rootPath=rootPath
00265         self.cmd=u"mkdir -p '{toDir}'; cp -R '{fromPath}' '{toPath}'"
00266 
00267     ##
00268     # 
00269     #         Copie une liste de fichiers d'une clé USB sous un répertoire donné.
00270     #          À chaque fichier ou répertoire copié, une ligne est journalisée
00271     #          dans le fichier de journal de l'application.
00272     #         @param ud l'instance uDisk correspondant à une partition de clé USB
00273     #         @param fileList la liste des fichiers à copier
00274     #         @param dest un répertoire de destination
00275     #         @param logfile un fichier de journalisation
00276     #         @param subdir le sous-répertoire de la clé USB où faire la copie
00277     #         
00278     def toDo(self, ud, fileList, subdir, dest, logfile):
00279         global globalThreads
00280         globalThreads.push(ud, self)
00281         for f in fileList:
00282             ## prend le fichier ou le répertoire sur le disque courant
00283             fromPath=os.path.join(ud.ensureMounted(), f)
00284             owner=ud.ownerByDb()
00285             ## personnalise le nom de la destination
00286             newName=u"%s_%s" %(owner,f)
00287             ## calcule le point de copie et le répertoire à créer s'il le faut
00288             toPath=os.path.join(dest,newName)
00289             toDir=os.path.dirname(toPath)
00290             cmd=self.cmd.format(fromPath=fromPath, toPath=toPath, toDir=toDir)
00291             _call(cmd,logfile)
00292         globalThreads.pop(ud, self)
00293             
00294 ##
00295 # 
00296 #     Classe pour les threads effaçant des sous-arbres dans les clés USB
00297 #     
00298 class threadDeleteInUSB(abstractThreadUSB):
00299     ##
00300     # 
00301     #         Constructeur
00302     #          Crée un thread pour supprimer une liste de fichiers dans une clé USB.
00303     #         @param ud l'instance uDisk correspondant à une partition de clé USB
00304     #         @param fileList la liste des fichiers à supprimer
00305     #         @param subdir le sous-répertoire de la clé USB où faire les suppressions
00306     #         @param logfile un fichier de journalisation, /dev/null par défaut
00307     #         
00308     def __init__(self,ud, fileList, subdir, logfile="/dev/null"):
00309         abstractThreadUSB.__init__(self,ud, fileList, subdir, dest=None, logfile=logfile)
00310         self.cmd=u"rm -rf '{toDel}'"
00311 
00312     ##
00313     # 
00314     #         Supprime une liste de fichiers dans une clé USB.
00315     #          La liste est prise sous un répertoire donné. Le répertoire visible
00316     #          qui dépend du constructuer d ela clé est pris en compte.
00317     #          À chaque fichier ou répertoire supprimé, une ligne est
00318     #          journalisée dans le fichier de journal de l'application.
00319     #         @param l'instance uDisk correspondant à une partition de clé USB
00320     #         @param fileList la liste des fichiers à copier
00321     #         @param dest un répertoire de destination
00322     #         @param logfile un fichier de journalisation
00323     #         @param subdir le sous-répertoire de la clé USB où faire la copie
00324     #         
00325     def toDo(self, ud, fileList, subdir, dest, logfile):
00326         global globalThreads
00327         globalThreads.push(ud, self)
00328         for f in fileList:
00329             toDel=os.path.join(ud.ensureMounted(), f)
00330             cmd=self.cmd.format(toDel=toDel)
00331             _call(cmd,logfile)
00332         globalThreads.pop(ud, self)
00333 
 Tout Classes Espaces de nommage Fichiers Fonctions Variables