Example #1
0
 def test_can_override_formfield(self):
     model_field = JsonField()
     class FakeFieldClass(object):
         def __init__(self, *args, **kwargs):
             pass
     form_field = model_field.formfield(form_class=FakeFieldClass)
     self.assertIsInstance(form_field, FakeFieldClass)
Example #2
0
class WebhookLog(models.Model):
    webhook = models.ForeignKey(Webhook,
                                null=False,
                                blank=False,
                                related_name="logs")
    url = models.URLField(null=False, blank=False, verbose_name=_("URL"))
    status = models.IntegerField(null=False,
                                 blank=False,
                                 verbose_name=_("status code"))
    request_data = JsonField(null=False,
                             blank=False,
                             verbose_name=_("request data"))
    request_headers = JsonField(null=False,
                                blank=False,
                                verbose_name=_("request headers"),
                                default={})
    response_data = models.TextField(null=False,
                                     blank=False,
                                     verbose_name=_("response data"))
    response_headers = JsonField(null=False,
                                 blank=False,
                                 verbose_name=_("response headers"),
                                 default={})
    duration = models.FloatField(null=False,
                                 blank=False,
                                 verbose_name=_("duration"),
                                 default=0)
    created = models.DateTimeField(auto_now_add=True)

    class Meta:
        ordering = ['-created', '-id']
Example #3
0
 def test_can_override_formfield(self):
     model_field = JsonField()
     class FakeFieldClass(object):
         def __init__(self, *args, **kwargs):
             pass
     form_field = model_field.formfield(form_class=FakeFieldClass)
     self.assertIsInstance(form_field, FakeFieldClass)
Example #4
0
class Version(models.Model):
    key = models.ForeignKey(Document)

    source = models.CharField(max_length=255)
    docID = models.TextField()
    timestamps = JsonField()

    providerUpdatedDateTime = models.DateTimeField(null=True)

    raw = JsonField()
    normalized = JsonField(null=True)
    status = models.TextField(null=True)
Example #5
0
class IndicatorRecord(models.Model):

    record_choices = tuple((rt.name, rt.title) for rt in RecordType)
    source_choices = tuple((rs.name, rs.title) for rs in RecordSource)

    record_type = models.CharField(max_length=2, choices=record_choices)
    created = models.DateTimeField(auto_now_add=True, editable=False)
    modified = models.DateTimeField(auto_now=True)

    info = JsonField()
    info_source = models.CharField(max_length=3, choices=source_choices)
    info_hash = models.CharField(max_length=40)
    info_date = models.DateTimeField()

    objects = IndicatorManager()

    class Meta:
        unique_together = (("info_hash", "info_source", "info_date"), )

    def generate_hash(self):
        info_pickle = pickle.dumps(self.info)
        info_sha1 = hashlib.sha1(info_pickle).hexdigest()
        return info_sha1

    def save(self, *args, **kwargs):

        if not self.info_hash:
            self.info_hash = self.generate_hash()

        super(IndicatorRecord, self).save(*args, **kwargs)
Example #6
0
class Payment(BaseModel):
    PAYMENT_STATUS_CHOICES = (
        ('p', 'Pending'),
        ('r', 'Received'),
        ('c', 'Cancelled'),
        ('f', 'Refunded')
    )

    gateway = models.ForeignKey("PaymentGateway")
    user = models.ForeignKey(settings.AUTH_USER_MODEL, blank=True, null=True)
    ptype = models.ForeignKey("PaymentType")
    payment_id = models.CharField(
        "Payment ID", max_length=50, blank=True, null=True
    )
    amount = models.FloatField(default=0.0)
    status = models.CharField(
        "Status", max_length=1, choices=PAYMENT_STATUS_CHOICES
    )
    status_pg = models.CharField(
        "Status from Payment Gateway", max_length=50
    )

    # store the whole json response
    raw_details = JsonField(blank=True, null=True)

    def __str__(self):
        return self.payment_id
Example #7
0
class AuditLog(TimeStampedModel):
    """An audit log of events and notes.
    Inherits ``created`` and ``modified`` from TimeStampedModel
    """
    user = models.ForeignKey(USER_MODEL)
    # Generic Foreign Key next three fields
    content_type = models.ForeignKey(ContentType, null=True, blank=True)
    object_id = models.PositiveIntegerField(null=True)
    content_object = generic.GenericForeignKey('content_type', 'object_id')
    audit_type = models.IntegerField(default=LOG, choices=AUDIT_CHOICES)
    action = models.CharField(
        max_length=128,
        null=True,
        blank=True,
        help_text='method triggering audit',
        db_index=True,
    )
    action_response = JsonField(default={},
                                help_text='HTTP response from action')
    action_note = models.TextField(
        blank=True,
        null=True,
        help_text='either the note text or a description of the action',
    )
    organization = models.ForeignKey(Organization, related_name='audit_logs')

    class Meta:
        ordering = ('-created', )

    objects = AuditLogManager()

    def __unicode__(self):
        return u'{0} <{1}> ({2})'.format(self.get_audit_type_display(),
                                         self.user, self.pk)

    def save(self, *args, **kwargs):
        """Ensure that only notes are saved"""
        if self.audit_type != NOTE and self.pk:
            raise PermissionDenied('AuditLogs cannot be edited, only notes')

        return super(AuditLog, self).save(*args, **kwargs)

    def to_dict(self):
        """serializes an audit_log"""
        # avoid cyclical import
        from seed.models import obj_to_dict
        log_dict = obj_to_dict(self)
        log_dict['audit_type'] = self.get_audit_type_display()
        log_dict['user'] = {
            'first_name': self.user.first_name,
            'last_name': self.user.last_name,
            'email': self.user.email,
            'id': self.user.pk,
        }
        log_dict['organization'] = {
            'id': self.organization.pk,
            'name': self.organization.name,
        }
        log_dict['content_type'] = self.content_object._meta.model_name
        return log_dict
Example #8
0
class UserProfile(models.Model):
    image = models.ImageField(upload_to='profile', null=False, blank=False, default="profile/default.jpg")
    birth_date = models.DateField(blank=True, null=True)
    user = models.OneToOneField(User, unique=True, related_name='profile')
    privacy_settings = JsonField(default=CONFIG_PRIVACY)
    #interest_groups = models.ManyToManyField(InterestGroup, related_name='members', null=True)

    def __str__(self):
        return 'profile ' + self.user.username

    def __init__(self, *args, **kwargs):
        super(UserProfile, self).__init__(*args, **kwargs)
        self.privacy_settings_old = self.privacy_settings

    def change_privacity(self):
        if self.privacy_settings.get('show_address', True) == self.privacy_settings_old.get('show_address', True):
            return False
        else:
            return True

    def get_can_show_location(self):
        return self.privacy_settings.get('show_address', True)

    def save(self, *args, **kwargs):
        obj = super(UserProfile, self).save(*args, **kwargs)
        if self.change_privacity():
            self.locations.filter(is_address=True).first().save()

        return obj
Example #9
0
class CreditRule(TranslatedModelFallbackMixin, TranslatableModel, UUIDModel):
    transaction_type = models.SmallIntegerField(
        choices=CreditTransactionType.choices)
    type = models.CharField(max_length=30)
    name = models.CharField(max_length=50)
    description = models.CharField(max_length=250, blank=True, default='')
    options = JsonField(default=dict, blank=True)
    is_active = models.BooleanField(default=True)

    translations = TranslatedFields(
        _local_name=models.CharField(max_length=50, blank=True, default=''),
        _local_description=models.CharField(max_length=250,
                                            blank=True,
                                            default=''))

    def __init__(self, *args, **kwargs):
        super(CreditRule, self).__init__(*args, **kwargs)
        if self.type:
            self.__class__ = CREDIT_RULES.get(self.type, CreditRule)

    def __unicode__(self):
        return "%s:%s:%s" % (self.get_transaction_type_display(), self.type,
                             self.name)

    def display(self, transaction):
        raise NotImplementedError()
Example #10
0
class CertificateMonitor(IndicatorLookupBase):
    """
    A lookup monitor for certificate indicators.

    This class extends IndicatorLookupBase, adding the field 'certificate_value' for the indicator value as a primary
    key. As with all indicator lookups, the combination of indicator value and owner must be unique.
    """

    certificate_value = models.TextField(primary_key=True)
    """The certificate fragment to be monitored"""

    resolutions = JsonField()
    """
    The full resolutions.  Here is the basic structure of this field:

    { <ip>: { "geo_location": <location>, "country": <code>, "domains": [ <domain>, ...] } }
    """

    # Commented out by LNguyen on 1/24/2017 - Certificate Monitor will not require owner and certificate value to be a unique key
    class Meta:
        """
        A metaclass for Certificate Monitor that specifies that the combination of 'owner' (the person submitting the
        monitor) and 'certificate_value' (the indicator value) must be unique.
        """

        unique_together = (('owner', 'certificate_value'), )
Example #11
0
class StorageEntry(models.Model):
    owner = models.ForeignKey(settings.AUTH_USER_MODEL,
                              blank=False,
                              null=False,
                              related_name="storage_entries",
                              verbose_name=_("owner"))
    created_date = models.DateTimeField(auto_now_add=True,
                                        null=False,
                                        blank=False,
                                        verbose_name=_("created date"))
    modified_date = models.DateTimeField(auto_now=True,
                                         null=False,
                                         blank=False,
                                         verbose_name=_("modified date"))
    key = models.CharField(max_length=255,
                           null=False,
                           blank=False,
                           verbose_name=_("key"))
    value = JsonField(blank=True,
                      default=None,
                      null=True,
                      verbose_name=_("value"))

    class Meta:
        verbose_name = "storage entry"
        verbose_name_plural = "storages entries"
        unique_together = ("owner", "key")
        ordering = ["owner", "key"]
Example #12
0
class AuthData(models.Model):
    user = models.ForeignKey(settings.AUTH_USER_MODEL, related_name="auth_data")
    key = models.SlugField(max_length=50)
    value = models.CharField(max_length=300)
    extra = JsonField()

    class Meta:
        unique_together = ["key", "value"]
Example #13
0
class AbstractCustomAttributesValues(OCCModelMixin, models.Model):
    attributes_values = JsonField(null=False,
                                  blank=False,
                                  default={},
                                  verbose_name=_("values"))

    class Meta:
        abstract = True
        ordering = ["id"]
Example #14
0
class ProjectModulesConfig(models.Model):
    project = models.OneToOneField("Project", null=False, blank=False,
                                related_name="modules_config", verbose_name=_("project"))
    config = JsonField(null=True, blank=True, verbose_name=_("modules config"))

    class Meta:
        verbose_name = "project modules config"
        verbose_name_plural = "project modules configs"
        ordering = ["project"]
Example #15
0
class XMLLinkCRMSource(CRMSource):
    url = models.URLField()
    mapping = JsonField(blank=False)

    crm_shouts = GenericRelation('shoutit_crm.XMLCRMShout',
                                 related_query_name='xml_link_crm_source')

    def __unicode__(self):
        return "%s: %s @ %s" % (self.pk, self.url, self.provider)
Example #16
0
class ExternalSessions(models.Model):
    """ External cookie sessions for scrapers """

    # Note: Yes, this syntax is mildly awkward, but it allows for very easy addition of additional sources in the list
    # at the end of the line
    service_choices = tuple(
        (rs.name, rs.title) for rs in RecordSource if rs in [RecordSource.IID])

    service = models.CharField(max_length=3, choices=service_choices)
    cookie = JsonField()
Example #17
0
class Document(models.Model):
    key = models.TextField(primary_key=True)
    source = models.CharField(max_length=255)
    docID = models.TextField()

    providerUpdatedDateTime = models.DateTimeField(null=True)

    raw = JsonField()
    timestamps = JsonField(null=True)
    normalized = JsonField(null=True)
    status = models.TextField(null=True)

    def save(self, *args, **kwargs):
        if not self.key:
            self.key = self._make_key(self.source, self.docID)
        return super(Document, self).save(*args, **kwargs)

    @classmethod
    def _make_key(cls, source, docID):
        return '|'.join((source, docID))
Example #18
0
class CustomBuildingHeaders(models.Model):
    """Specify custom building header mapping for display."""
    super_organization = models.ForeignKey(SuperOrganization,
                                           blank=True,
                                           null=True,
                                           verbose_name=_('SeedOrg'),
                                           related_name='custom_headers')

    # 'existing, normalized name' -> 'preferred display name'
    # e.g. {'district': 'Boro'}
    building_headers = JsonField(default={})

    objects = JsonManager()
Example #19
0
class Timeline(models.Model):
    content_type = models.ForeignKey(ContentType, related_name="content_type_timelines")
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')
    namespace = models.CharField(max_length=250, default="default", db_index=True)
    event_type = models.CharField(max_length=250, db_index=True)
    project = models.ForeignKey(Project, null=True)
    data = JsonField()
    data_content_type = models.ForeignKey(ContentType, related_name="data_timelines")
    created = models.DateTimeField(default=timezone.now)

    class Meta:
        index_together = [('content_type', 'object_id', 'namespace'), ]
Example #20
0
class LearningRecord(models.Model):
    xapi = JsonField()
    course_code = models.CharField(max_length=5000, blank=False)
    platform = models.CharField(max_length=5000, blank=False)
    verb = models.CharField(max_length=5000, blank=False)
    username = models.CharField(max_length=5000, blank=True)
    platformid = models.CharField(max_length=5000, blank=True)
    platformparentid = models.CharField(max_length=5000, blank=True)
    parentusername = models.CharField(max_length=5000, blank=True)
    parentdisplayname = models.CharField(max_length=5000, blank=True)
    message = models.TextField(blank=True)
    #mentions = models.TextField(blank=True)
    datetimestamp = models.DateTimeField(blank=True, null=True)
    senttolrs = models.CharField(max_length=5000, blank=True)
Example #21
0
class Timeline(models.Model):
    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')
    namespace = models.SlugField(default="default")
    event_type = models.SlugField()
    data = JsonField()
    created = models.DateTimeField(default=timezone.now)

    def save(self, *args, **kwargs):
        if self.id:
            raise ValidationError("Not modify allowed for timeline entries")
        return super().save(*args, **kwargs)

    class Meta:
        index_together = [('content_type', 'object_id', 'namespace'), ]
Example #22
0
class Timeline(models.Model):
    content_type = models.ForeignKey(ContentType, related_name="content_type_timelines")
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')
    namespace = models.CharField(max_length=250, default="default", db_index=True)
    event_type = models.CharField(max_length=250, db_index=True)
    project = models.ForeignKey(Project)
    data = JsonField()
    data_content_type = models.ForeignKey(ContentType, related_name="data_timelines")
    created = models.DateTimeField(default=timezone.now)

    def save(self, *args, **kwargs):
        if self.id:
            raise ValidationError("Not modify allowed for timeline entries")
        return super().save(*args, **kwargs)

    class Meta:
        index_together = [('content_type', 'object_id', 'namespace'), ]
Example #23
0
class Store(models.Model):
    logo = models.ImageField(upload_to='logo', null=False, blank=True)
    name = models.CharField(max_length=255, blank=True)
    #slug = AutoSlugField(populate_from='name', always_update=True, unique=True)
    slogan = models.TextField()
    style = JsonField(default=STYLE_STORE)
    profile = models.OneToOneField(UserProfile, unique=True, related_name='store')
    status = models.IntegerField(choices=STATUS_STORE, default=0)
    slug = models.SlugField(auto_created=True)

    def __str__(self):
        return self.name

    def clean_slug(self):
        if self.name != '':
            if Store.objects.get(slug= self.slug).count() > 0:
                raise ValidationError('Error, fields name is unique')

    def get_url(self):
        if self.slug:
            return reverse('store', args=[self.slug])
        else:
            return ''

    def save(self, *args, **kwargs):
        # Validate if has all config
        for key, value in STYLE_STORE.items():
            if not key in self.style or not self.style[key]:
                self.style[key] = value

        if self.name and not self.slug:
            from django.template.defaultfilters import slugify
            slug = slugify(self.name)
           
            while Store.objects.filter(slug=slug).count() != 0:
                slug += '1'
            
            self.slug = slug

        if self.name != '':
            self.status = 1
        else:
            self.status = 0
        return super(Store, self).save(*args, **kwargs)
Example #24
0
class UserLocation(models.Model):
    title = models.CharField(max_length=100)
    userProfile = models.ForeignKey(UserProfile, related_name='locations')
    lat = models.FloatField(null=False)
    lng = models.FloatField(null=False)
    radius = models.IntegerField(default=5000)
    is_address = models.BooleanField(default=False)

    address = JsonField(default=INFO_ADDRESS)

    def get_address(self):
        address = self.address
        address['lat'] = self.lat
        address['lng'] = self.lng
        return address

    def save(self, *args, **kwargs):
        try:
            url = 'http://maps.googleapis.com/maps/api/geocode/json?latlng='+ \
                  str(self.lat) + ',' + str(self.lng) +'&sensor=true'

            response = urllib.request.urlopen(url)
            json_response = response.read()
            obj = json.loads(json_response.decode("utf-8"))
            if len(obj['results']):
                for district in obj["results"][0]["address_components"]:
                    if "locality" in district["types"]:
                        self.address['locality'] = district["long_name"]
                    elif "administrative_area_level_2" in district["types"]:
                        self.address['administrative_area_level_2'] = district["long_name"]
                    elif "administrative_area_level_1" in district["types"]:
                        self.address['administrative_area_level_1'] = district["long_name"]
                    elif "country" in district["types"]:
                        self.address['country'] = district["long_name"]
        except:
            pass

        return super(UserLocation, self).save(*args, **kwargs)

    def __str__(self):
        return self.title
Example #25
0
class CreditTransaction(UUIDModel):
    user = models.ForeignKey(settings.AUTH_USER_MODEL,
                             related_name='credit_transactions')
    amount = models.IntegerField()
    rule = models.ForeignKey(CreditRule, related_name='transactions')
    properties = JsonField(default=dict, blank=True)

    def __unicode__(self):
        return "%s %d:%s by %s" % (self.id, self.amount, self.rule,
                                   self.user.username)

    @property
    def type(self):
        return CREDIT_IN if self.amount > 0 else CREDIT_OUT

    def get_type_display(self):
        return str(self.type)

    def display(self):
        return self.rule.display(self)

    @property
    def app_url(self):
        self.display()
        return getattr(self.target, 'app_url', None) if hasattr(
            self, 'target') else None

    @property
    def web_url(self):
        self.display()
        return getattr(self.target, 'web_url', None) if hasattr(
            self, 'target') else None

    def notify_user(self):
        from shoutit.controllers.notifications_controller import notify_user_of_credit_transaction
        notify_user_of_credit_transaction(self)

    def serializer(self, version=None):
        from shoutit_credit.serializers import CreditTransactionSerializer
        return CreditTransactionSerializer
Example #26
0
class Notification(models.Model):
    # TODO: Validar que el usuario o el email sea requerido (uno si o si)
    type = models.CharField(max_length=20, choices=TYPE_NOTIFICATION)
    message = models.TextField()
    created = models.DateTimeField(auto_now_add=True)
    receiver = models.ForeignKey(User, null=True)
    extras = JsonField()
    read = models.DateTimeField(blank=True, null=True)

    objects = NotificationManager.as_manager()

    def marked_read(self):
        if not self.read:
            self.read = datetime_now()
            self.save()

    def __str__(self):
        return self.message

    class Meta:
        verbose_name = "Notification"
        verbose_name_plural = "Notifications"
        ordering = ["-created"]

    def get_user(self):
        return self.extras.get('user', "")

    def get_object_id(self):
        return self.extras.get('id', 0)

    def get_url(self):
        return self.extras.get('url', "")

    def get_email(self):
        if self.receiver and self.receiver.email:
            return self.receiver.email
        elif self.extras.get('email'):
            return self.extras.get('email')
        else:
            return None
Example #27
0
class ConfigNotification(models.Model):
    user = models.OneToOneField(AUTH_USER_MODEL, related_name='config_notifications', unique=True)
    config = JsonField(default=CONFIG_NOTIFICATION)

    def save(self, *args, **kwargs):

        # Validate if has all config
        for check_key in CONFIG_NOTIFICATION.keys():
            if not self.config.get(check_key):
                self.config[check_key] = CONFIG_NOTIFICATION[check_key]
            for config in CONFIG_NOTIFICATION.keys():
                for key, value in CONFIG_NOTIFICATION[config].items():
                    if not self.config.get(config) or not key in self.config.get(config) or key == 'label':
                        self.config[config][key] = value

        super(ConfigNotification, self).save(*args, **kwargs)

    def has_perm(self, type, canal):
        if self.config.get(type) and self.config[type].get(canal):
            return True
        else:
            return False
Example #28
0
class Location(models.Model):
    """Geo and meta representation of a location"""

    name = models.CharField(_('Location Name'), max_length=255)

    address = models.CharField(_('Address'),
                               max_length=255,
                               blank=True,
                               null=True,
                               unique=True)

    location = models.PointField(_('Location'), blank=True, null=True)

    location_hash = models.CharField(max_length=32, null=True)

    meta = JsonField(_('metadata'), blank=True, null=True)

    objects = models.GeoManager()

    def __str__(self):
        return "{name}".format(name=self.name)

    def save(self, **kwargs):
        location_hash = md5('{}@{}'.format(
            self.name, self.address).encode('utf-8')).hexdigest()
        if self.location_hash != location_hash and self.address:
            location_geocode = geocode(self.address)
            if location_geocode:
                self.meta = location_geocode.raw
                pos = [float(self.meta.get(coord)) for coord in ['lon', 'lat']]
                self.location = Point(*pos)
                self.location_hash = location_hash
            else:
                self.meta = {"geocoded": False}
                self.location = None
        super().save(**kwargs)
Example #29
0
class SEEDUser(AbstractBaseUser, PermissionsMixin):
    """
    An abstract base class implementing a fully featured User model with
    admin-compliant permissions.

    Username, password and email are required. Other fields are optional.
    """
    username = models.EmailField(
        _('username (email)'), unique=True,
        help_text=_('User\'s email address.  Used for auth as well.'))
    first_name = models.CharField(_('first name'), max_length=30, blank=True)
    last_name = models.CharField(_('last name'), max_length=30, blank=True)
    email = models.EmailField(_('email address'), blank=True)
    is_staff = models.BooleanField(
        _('staff status'), default=False,
        help_text=_('Designates whether the user can log into this admin '
                    'site.'))
    is_active = models.BooleanField(
        _('active'), default=True,
        help_text=_('Designates whether this user should be treated as '
                    'active. Unselect this instead of deleting accounts.'))
    date_joined = models.DateTimeField(_('date joined'), default=timezone.now)

    default_custom_columns = JsonField(default={})
    default_building_detail_custom_columns = JsonField(default={})
    show_shared_buildings = models.BooleanField(
        _('active'), default=False,
        help_text=_('shows shared buildings within search results'))
    default_organization = models.ForeignKey(
        Organization,
        blank=True,
        null=True,
        related_name='default_users'
    )
    api_key = models.CharField(
        _('api key'),
        max_length=128,
        blank=True,
        default='',
        db_index=True
    )

    objects = UserManager()

    USERNAME_FIELD = 'username'
    REQUIRED_FIELDS = ['email']

    class Meta:
        verbose_name = _('user')
        verbose_name_plural = _('users')

    def get_absolute_url(self):
        return "/users/%s/" % urlquote(self.username)

    def get_full_name(self):
        """
        Returns the first_name plus the last_name, with a space in between.
        """
        full_name = '%s %s' % (self.first_name, self.last_name)
        return full_name.strip()

    def get_short_name(self):
        "Returns the short name for the user."
        return self.first_name

    def email_user(self, subject, message, from_email=None):
        """
        Sends an email to this User.
        """
        send_mail(subject, message, from_email, [self.email])

    def generate_key(self):
        """
        Creates and sets an API key for this user.
        Adapted from tastypie:

        https://github.com/toastdriven/django-tastypie/blob/master/tastypie/models.py#L47  # noqa
        """
        new_uuid = uuid.uuid4()
        api_key = hmac.new(new_uuid.bytes, digestmod=sha1).hexdigest()
        self.api_key = api_key
        self.save()

    def save(self, *args, **kwargs):
        """
        Ensure that email and username are synced.
        """

        # NL: Why are we setting the email to the user name, don't we need the
        # email? It seems that the username is then suppose to be the email,
        # correct? Regardless, this code seems problematic
        if self.email.lower() != self.username:
            self.email = self.username
        return super(SEEDUser, self).save(*args, **kwargs)
Example #30
0
class HistoryEntry(models.Model):
    """
    Domain model that represents a history
    entry storage table.

    It is used for store object changes and
    comments.
    """
    id = models.CharField(primary_key=True, max_length=255, unique=True,
                          editable=False, default=_generate_uuid)

    user = JsonField(blank=True, default=None, null=True)
    created_at = models.DateTimeField(default=timezone.now)
    type = models.SmallIntegerField(choices=HISTORY_TYPE_CHOICES)
    key = models.CharField(max_length=255, null=True, default=None, blank=True, db_index=True)

    # Stores the last diff
    diff = JsonField(null=True, default=None)

    # Stores the last complete frozen object snapshot
    snapshot = JsonField(null=True, default=None)

    # Stores a values of all identifiers used in
    values = JsonField(null=True, default=None)

    # Stores a comment
    comment = models.TextField(blank=True)
    comment_html = models.TextField(blank=True)

    delete_comment_date = models.DateTimeField(null=True, blank=True, default=None)
    delete_comment_user = JsonField(blank=True, default=None, null=True)

    # Flag for mark some history entries as
    # hidden. Hidden history entries are important
    # for save but not important to preview.
    # Order fields are the good example of this fields.
    is_hidden = models.BooleanField(default=False)

    # Flag for mark some history entries as complete
    # snapshot. The rest are partial snapshot.
    is_snapshot = models.BooleanField(default=False)

    _importing = None
    
    @cached_property
    def is_change(self):
      return self.type == HistoryType.change

    @cached_property
    def is_create(self):
      return self.type == HistoryType.create

    @cached_property
    def is_delete(self):
      return self.type == HistoryType.delete

    @cached_property
    def owner(self):
        pk = self.user["pk"]
        model = apps.get_model("users", "User")
        return model.objects.get(pk=pk)

    @cached_property
    def values_diff(self):
        result = {}
        users_keys = ["assigned_to", "owner"]

        def resolve_diff_value(key):
            value = None
            diff = get_diff_of_htmls(
                self.diff[key][0] or "",
                self.diff[key][1] or ""
            )

            if diff:
                key = "{}_diff".format(key)
                value = (None, diff)

            return (key, value)

        def resolve_value(field, key):
            data = self.values[field]
            key = str(key)

            if key not in data:
                return None
            return data[key]

        for key in self.diff:
            value = None

            # Note: Hack to prevent description_diff propagation
            #       on old HistoryEntry objects.
            if key == "description_diff":
                continue
            elif key == "content_diff":
                continue
            elif key == "blocked_note_diff":
                continue
            elif key in["description", "content", "blocked_note"]:
                (key, value) = resolve_diff_value(key)
            elif key in users_keys:
                value = [resolve_value("users", x) for x in self.diff[key]]
            elif key == "watchers":
                value = [[resolve_value("users", x) for x in self.diff[key][0]],
                         [resolve_value("users", x) for x in self.diff[key][1]]]
            elif key == "points":
                points = {}

                pointsold = self.diff["points"][0]
                pointsnew = self.diff["points"][1]
                # pointsold = pointsnew

                if pointsold is None:
                    for role_id, point_id in pointsnew.items():
                        role_name = resolve_value("roles", role_id)
                        points[role_name] = [None, resolve_value("points", point_id)]

                else:
                    for role_id, point_id in pointsnew.items():
                        role_name = resolve_value("roles", role_id)
                        oldpoint_id = pointsold.get(role_id, None)
                        points[role_name] = [resolve_value("points", oldpoint_id),
                                           resolve_value("points", point_id)]

                # Process that removes points entries with
                # duplicate value.
                for role in dict(points):
                    values = points[role]
                    if values[1] == values[0]:
                        del points[role]

                if points:
                    value = points

            elif key == "attachments":
                attachments = {
                    "new": [],
                    "changed": [],
                    "deleted": [],
                }

                oldattachs = {x["id"]:x for x in self.diff["attachments"][0]}
                newattachs = {x["id"]:x for x in self.diff["attachments"][1]}

                for aid in set(tuple(oldattachs.keys()) + tuple(newattachs.keys())):
                    if aid in oldattachs and aid in newattachs:
                        changes = make_diff_from_dicts(oldattachs[aid], newattachs[aid],
                                                       excluded_keys=("filename", "url"))

                        if changes:
                            change = {
                                "filename": newattachs.get(aid, {}).get("filename", ""),
                                "url": newattachs.get(aid, {}).get("url", ""),
                                "changes": changes
                            }
                            attachments["changed"].append(change)
                    elif aid in oldattachs and aid not in newattachs:
                        attachments["deleted"].append(oldattachs[aid])
                    elif aid not in oldattachs and aid in newattachs:
                        attachments["new"].append(newattachs[aid])

                if attachments["new"] or attachments["changed"] or attachments["deleted"]:
                    value = attachments

            elif key == "custom_attributes":
                custom_attributes = {
                    "new": [],
                    "changed": [],
                    "deleted": [],
                }

                oldcustattrs = {x["id"]:x for x in self.diff["custom_attributes"][0] or []}
                newcustattrs = {x["id"]:x for x in self.diff["custom_attributes"][1] or []}

                for aid in set(tuple(oldcustattrs.keys()) + tuple(newcustattrs.keys())):
                    if aid in oldcustattrs and aid in newcustattrs:
                        changes = make_diff_from_dicts(oldcustattrs[aid], newcustattrs[aid],
                                                       excluded_keys=("name"))

                        if changes:
                            change = {
                                "name": newcustattrs.get(aid, {}).get("name", ""),
                                "changes": changes
                            }
                            custom_attributes["changed"].append(change)
                    elif aid in oldcustattrs and aid not in newcustattrs:
                        custom_attributes["deleted"].append(oldcustattrs[aid])
                    elif aid not in oldcustattrs and aid in newcustattrs:
                        custom_attributes["new"].append(newcustattrs[aid])

                if custom_attributes["new"] or custom_attributes["changed"] or custom_attributes["deleted"]:
                    value = custom_attributes

            elif key in self.values:
                value = [resolve_value(key, x) for x in self.diff[key]]
            else:
                value = self.diff[key]

            if not value:
                continue

            result[key] = value

        return result

    class Meta:
        ordering = ["created_at"]
Example #31
0
class TaxLotState(models.Model):
    # The state field names should match pretty close to the pdf, just
    # because these are the most 'public' fields in terms of
    # communicating with the cities.

    confidence = models.FloatField(default=0, null=True, blank=True)

    # Support finding the property by the import_file
    import_file = models.ForeignKey(ImportFile, null=True, blank=True)

    # Add organization to the tax lot states
    organization = models.ForeignKey(Organization)
    data_state = models.IntegerField(choices=DATA_STATE,
                                     default=DATA_STATE_UNKNOWN)
    merge_state = models.IntegerField(choices=MERGE_STATE,
                                      default=MERGE_STATE_UNKNOWN,
                                      null=True)

    custom_id_1 = models.CharField(max_length=255, null=True, blank=True)

    jurisdiction_tax_lot_id = models.CharField(max_length=2047,
                                               null=True,
                                               blank=True)
    block_number = models.CharField(max_length=255, null=True, blank=True)
    district = models.CharField(max_length=255, null=True, blank=True)
    address_line_1 = models.CharField(max_length=255, null=True, blank=True)
    address_line_2 = models.CharField(max_length=255, null=True, blank=True)
    normalized_address = models.CharField(max_length=255,
                                          null=True,
                                          blank=True,
                                          editable=False)

    city = models.CharField(max_length=255, null=True, blank=True)
    state = models.CharField(max_length=255, null=True, blank=True)
    postal_code = models.CharField(max_length=255, null=True, blank=True)
    number_properties = models.IntegerField(null=True, blank=True)

    extra_data = JsonField(default={}, blank=True)

    def __unicode__(self):
        return u'TaxLot State - %s' % self.pk

    def promote(self, cycle):
        """
            Promote the TaxLotState to the view table for the given cycle

            Args:
                cycle: Cycle to assign the view

            Returns:
                The resulting TaxLotView (note that it is not returning the
                TaxLotState)

        """
        # First check if the cycle and the PropertyState already have a view
        tlvs = TaxLotView.objects.filter(cycle=cycle, state=self)

        if len(tlvs) == 0:
            _log.debug("Found 0 TaxLotViews, adding TaxLot, promoting")
            # There are no PropertyViews for this property state and cycle.
            # Most likely there is nothing to match right now, so just
            # promote it to the view

            # Need to create a property for this state
            if self.organization is None:
                _log.error("organization is None")

            taxlot = TaxLot.objects.create(organization=self.organization)

            tlv = TaxLotView.objects.create(taxlot=taxlot,
                                            cycle=cycle,
                                            state=self)

            # This is legacy but still needed here to have the tests pass.
            self.data_state = DATA_STATE_MATCHING

            self.save()

            return tlv
        elif len(tlvs) == 1:
            _log.debug("Found 1 PropertyView... Nothing to do")
            # PropertyView already exists for cycle and state. Nothing to do.

            return tlvs[0]
        else:
            _log.debug("Found %s PropertyView" % len(tlvs))
            _log.debug("This should never occur, famous last words?")

            return None

    def to_dict(self, fields=None, include_related_data=True):
        """
        Returns a dict version of the TaxLotState, either with all fields
        or masked to just those requested.
        """

        # TODO: make this a serializer and/or merge with PropertyState.to_dict
        if fields:
            model_fields, ed_fields = split_model_fields(self, fields)
            extra_data = self.extra_data
            ed_fields = filter(lambda f: f in extra_data, ed_fields)

            result = {field: getattr(self, field) for field in model_fields}
            result['extra_data'] = {
                field: extra_data[field]
                for field in ed_fields
            }

            # always return id's and canonical_building id's
            result['id'] = result['pk'] = self.pk

            # should probably also return children, parents, and coparent
            # result['children'] = map(lambda c: c.id, self.children.all())
            # result['parents'] = map(lambda p: p.id, self.parents.all())
            # result['co_parent'] = (self.co_parent and self.co_parent.pk)
            # result['coparent'] = (self.co_parent and {
            #     field: self.co_parent.pk for field in ['pk', 'id']
            #     })

            return result

        d = obj_to_dict(self, include_m2m=include_related_data)

        # if include_related_data:
        # d['parents'] = list(self.parents.values_list('id', flat=True))
        # d['co_parent'] = self.co_parent.pk if self.co_parent else None

        return d

    def save(self, *args, **kwargs):
        # Calculate and save the normalized address
        if self.address_line_1 is not None:
            self.normalized_address = normalize_address_str(
                self.address_line_1)
        else:
            self.normalized_address = None

        return super(TaxLotState, self).save(*args, **kwargs)