class DataModel(models.Model): """ Mixin de modèle pour stocker des données """ # Champs data = picklefield.PickledObjectField(default=dict, protocol=3, verbose_name=_("Data")) # Setter def set_data(self, key, value, save=False): """ Ajouter une clé-valeur aux données :param key: clé :param value: valeur à assinger pour la clé :param save: sauvegarder l'objet immédiatement ou non :type key: str :type value: int | str | list | dict | set :type save: bool :rtype: bool """ if (hasattr(self, 'DATA_KEYS') and key in self.DATA_KEYS) or not hasattr(self, 'DATA_KEYS'): if not isinstance(self.data, dict): self.data = {key: value} else: self.data[key] = value if save is True: self.save(update_fields=['data']) return True else: return False def set_data_all(self, data, save=False): """ Redéfinir toutes les clés/valeurs en une seule fois """ data_keys = self.get_data_keys() if isinstance(data, dict) and (not data_keys or all(key in data_keys for key in data.keys())): self.data = data if save is True: self.save(update_fields=['data']) return True logger.warning( "Could not set full data field for {}. Check your DATA_KEYS attribute matches your keys." .format(str(self))) return False # Getter def get_data_keys(self): """ Renvoyer les clés de données autorisées """ return getattr(self, 'DATA_KEYS', frozenset()) def get_data(self, key, default=None): """ Renvoyer la valeur d'une clé de données """ if self.data is None or key not in self.data: return default else: return self.data[key] @addattr(short_description=_("Data")) def get_data_repr(self): """ Renvoyer la représentation unicode des données """ lines = [] for key in self.data: value = self.get_data(key) if isinstance(value, list): info = _("{count} items").format(count=len(value)) else: try: info = str(value) except ValueError: info = _("Unknown data") output = "{key} -> {info}".format(key=key, info=info) lines.append(output) return ", ".join(lines) # Métadonnées class Meta: abstract = True
class Message(models.Model): """ A Message. We have a uuid, which is our reference. We also have a gateway_message_id, which is their reference. This is required by some systems so we can pass in a unique value that will allow us to match up replies to original messages. """ content = models.TextField(help_text=_(u'The body of the message.')) recipient_number = models.CharField(max_length=32, help_text=_(u'The international number of the recipient' ', without the leading +')) sender = models.ForeignKey('auth.User', related_name='sent_sms_messages') send_date = models.DateTimeField(null=True, blank=True, editable=False) delivery_date = models.DateTimeField(null=True, blank=True, editable=False) uuid = uuidfield.fields.UUIDField(auto=True, help_text=_(u'Used for associating replies.')) status = models.CharField(max_length=16, choices=MESSAGE_STATUSES, default="Unsent", ) status_message = models.CharField(max_length=128, null=True, blank=True) billed = models.BooleanField(default=False) content_type = models.ForeignKey('contenttypes.ContentType') object_id = models.PositiveIntegerField() billee = generic.GenericForeignKey() gateway = models.ForeignKey('sms.Gateway', null=True, blank=True, editable=False) gateway_message_id = models.CharField(max_length=128, blank=True, null=True, editable=False) reply_callback = picklefield.PickledObjectField(null=True, blank=True) gateway_charge = models.DecimalField(max_digits=10, decimal_places=5, null=True, blank=True) objects = MessageManager() class Meta: app_label = 'sms' permissions = ( ('view_message', 'Can view message'), ) ordering = ('send_date',) def send(self, gateway): gateway.send(self) @property def length(self): """Unicode messages are limited to 70 chars/message segment.""" # try: # return len(str(self.content)) / 160 + 1 # except UnicodeEncodeError: # return len(self.content) / 70 + 1 return len(self.content) / 160 + 1 @property def local_send_time(self): # TODO: Get this from UserProfile? if getattr(self.billee, 'timezone', None): return adjust_datetime_to_timezone( self.send_date, settings.TIME_ZONE, self.billee.timezone ) return self.send_date @property def local_send_date(self): return self.local_send_time.date() def __unicode__(self): return "[%s] Sent to %s by %s at %s [%i]" % ( self.status, self.recipient_number, self.sender, self.send_date, self.length )
class FormConfiguration(DatetimeModel): """ Configuration utilisateur via formulaire Ces objets sont gérés via des instances de la classe DataForm :seealso: scoop.user.util.forms.DataForm """ # Champs user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, related_name='configurations', on_delete=models.CASCADE, verbose_name=_("User")) name = models.CharField(max_length=32, verbose_name=_("Form name")) version = models.CharField(max_length=24, blank=True, help_text=_("Variation name"), verbose_name=_("Version")) data = picklefield.PickledObjectField(default=dict(), compress=True, verbose_name=_("Data")) description = models.TextField( blank=True, verbose_name=_("Description")) # only for templates, ie user=None objects = FormConfigurationManager() # Getter def get_data(self, name, default=None): """ Renvoyer les données d'un champ de la configuration :rtype: str """ raw_information = self.data.get(name, default) return raw_information def is_current(self): """ Renvoyer si la configuration est celle courante pour l'utilisateur Une configuration est considérée comme courante lorsque l'utilisateur est non Null (les templates sont exclus) et la version est vide (les templates utilisateur sont exclus également) """ return self.user and not self.version # Overrides def save(self, *args, **kwargs): """ Sauvegarder l'objet """ if self.user is not None: self.description = "" super().save(*args, **kwargs) # Métadonnées class Meta: verbose_name = _("user configuration") verbose_name_plural = _("user configurations") unique_together = [['user', 'name', 'version']] app_label = 'user'