zephir.entpool

Package zephir :: Module entpool |
[frames] | no frames]

# Source Code for Module zephir.entpool

      1  #!/bin/env python
      2  # -*- coding: UTF-8 -*-
      3  ###########################################################################
      4  # Eole NG - 2007
      5  # Copyright Pole de Competence Eole  (Ministere Education - Academie Dijon)
      6  # Licence CeCill  cf /root/LicenceEole.txt
      7  # eole@ac-dijon.fr
      8  #
      9  # entpool.py
     10  #
     11  # Classes utilitaires pour la gestion d'un pool d'identifiants ENT
     12  # sur un serveur client de Zephir
     13  #
     14  ###########################################################################
     15
     16  import os, re
     17
     18 -class Error(Exception):
     19      """Erreur interne
     20      """
     21
     22 -class OutOfRangeError(Error):
     23      """valeur hors de la plage
     24      """
     25
     26 -class IdPool(object):
     27      """classe représentant un ensemble d'identifiants spécifiques à un ENT
     28      les ids ont la forme suivante: LxxCiiii
     29      L/C : code ENT défini dans le SDET (cf annexes) et dépendant de l'entité porteuse du projet
     30      xx/iiii : 2 lettres et 4 chiffres identifiant un utilisateur
     31      """
     32
     33      digits = (26, 26, 10, 10, 10, 10)
     34
     35 -    def __init__(self, code_ent):
     36          max_id = 1
     37          for digit in self.digits:
     38              max_id = max_id * digit
     39          self.max_id = max_id - 1
     40          self.code_ent = code_ent
     41          self.digit_vals = self._def_digit_vals()
     42          self.regexp = re.compile('^\w[a-z]{2,2}\w\d{4,4}')
     43
     44 -    def _def_digit_vals(self):
     45          """définit le poids de chaque caractère dans un identifiant
     46          """
     47          digit_vals = []
     48          for num_digit in range(len(self.digits)):
     49              val = 1
     50              for digit in self.digits[num_digit+1:]:
     51                  val = val * digit
     52              digit_vals.append(val)
     53          return digit_vals
     54
     55 -    def id_to_string(self, num_id):
     56          """renvoie la représentation au format SDET d'un identifiant numérique
     57          """
     58          sdet_id = []
     59          sdet_id.append(self.code_ent[0])
     60          if type(num_id) != int:
     61              raise TypeError("type integer requis : %s" % str(num_id))
     62          if 0 > num_id or num_id > self.max_id:
     63              raise OutOfRangeError("Valeur incompatible : %s" % str(num_id))
     64          for car in range(2):
     65              sdet_id.append(chr(ord('a') + (num_id / self.digit_vals[car])))
     66              num_id = num_id.__mod__(self.digit_vals[car])
     67          sdet_id.append(self.code_ent[1])
     68          for car in range(2, 5):
     69              sdet_id.append(str(num_id / self.digit_vals[car]))
     70              num_id = num_id.__mod__(self.digit_vals[car])
     71          sdet_id.append(str(num_id))
     72          return "".join(sdet_id)
     73
     74 -    def string_to_id(self, string_id):
     75          """renvoie la représentation numérique d'un identifiant au format SDET
     76          """
     77          if type(string_id) not in (str, unicode):
     78              raise TypeError("Chaine requise : %s" % str(string_id))
     79          string_id = string_id.lower()
     80          if not self.regexp.match(string_id):
     81              raise ValueError("Chaine au mauvais format : %s" % string_id)
     82          num_id = 0
     83          for car in range(2):
     84              num_id += (ord(string_id[car + 1]) - ord('a')) * self.digit_vals[car]
     85          for car in range(2, 5):
     86              num_id += int(string_id[car + 2]) * self.digit_vals[car]
     87          num_id += int(string_id[7])
     88          return num_id
     89
     90 -class ClientIdPool(IdPool):
     91      """Adaptation de la classe IdPool pour le côté client
     92      (maintient la liste des identifiants disponibles et
     93      permet leur affectation séquencielle)
     94      """
     95
     96 -    def __init__(self, code_ent, state_dir="/etc/eole"):
     97          """initialise un pool sans identifiant disponibles
     98          """
     99          super(ClientIdPool, self).__init__(code_ent)
    100          self.state_file = os.path.join(state_dir, "ent_ids_%s" % code_ent)
    101          self.load_state()
    102
    103 -    def load_state(self):
    104          """charge la liste des plages disponibles
    105          """
    106          self.free = []
    107          self.free_space = 0
    108          if os.path.isfile(self.state_file):
    109              data = file(self.state_file).read().strip()
    110              # lecture des plages correspondant au code ent
    111              for line in data.split('\n'):
    112                  if line != '':
    113                      minid, maxid = line.split(',')
    114                      self._add_free_range(int(minid), int(maxid))
    115
    116 -    def save_state(self):
    117          """sauvegarde les plages disponibles
    118          """
    119          try:
    120              f_state = file(self.state_file, 'w')
    121          except IOError:
    122              return False
    123          for minid, maxid in self.free:
    124              f_state.write('%s,%s\n' % (minid, maxid))
    125          f_state.close()
    126          return True
    127
    128 -    def __repr__(self):
    129          """représentation par défaut de l'objet
    130          """
    131          descr = "ENT %s" % self.code_ent
    132          descr += " - %d identifiant(s) disponible(s)" % self.free_space
    133          if self.free:
    134              descr += " - prochain : %s" % self.id_to_string(self.free[0][0])
    135          return descr
    136
    137 -    def add_free_range(self, minid, maxid):
    138          """ajoute une plage d'identifiants disponibles
    139          minid, maxid : identifiants au format ent
    140          """
    141          # recherche de l'emplacement où insérer la plage
    142          minid = self.string_to_id(minid)
    143          maxid = self.string_to_id(maxid)
    144          self._add_free_range(minid, maxid)
    145
    146 -    def _add_free_range(self, minid, maxid):
    147          """ajoute une plage d'identifiants disponibles
    148          """
    149          insert_index = 0
    150          # mise à jour des plages libres
    151          if self.free:
    152              for rng in self.free:
    153                  if rng[0] > maxid:
    154                      # on est sur la plage précédent celle à insérer
    155                      break
    156                  insert_index += 1
    157          # ajout de la plage réservée avant insert_index
    158          self.free.insert(insert_index, (minid, maxid))
    159          self.free_space += maxid - minid + 1
    160
    161 -    def get_next_id(self):
    162          """renvoie le prochain identifiant disponible
    163          """
    164          if self.free:
    165              first_range = self.free[0]
    166              new_id = first_range[0]
    167              # mise à jour de la plage utilisée
    168              if first_range[0] != first_range[1]:
    169                  self.free[0] = (first_range[0] + 1, first_range[1])
    170              else:
    171                  self.free.remove(first_range)
    172              self.free_space -= 1
    173              return self.id_to_string(new_id)
    174          return None
    175