Exemple #1
0
class Job(TimeStampedModelMixin):
    """
    Model for a Job.
    """
    def __init__(self, *args, **kwargs):
        kwargs['the_geom'] = convert_polygon(kwargs.get('the_geom')) or ''
        kwargs['the_geom_webmercator'] = convert_polygon(
            kwargs.get('the_geom_webmercator')) or ''
        kwargs['the_geog'] = convert_polygon(kwargs.get('the_geog')) or ''
        super(Job, self).__init__(*args, **kwargs)

    id = models.AutoField(primary_key=True, editable=False)
    uid = models.UUIDField(unique=True,
                           default=uuid.uuid4,
                           editable=False,
                           db_index=True)
    user = models.ForeignKey(User, related_name='owner')
    name = models.CharField(max_length=100, db_index=True)
    description = models.CharField(max_length=1000, db_index=True)
    event = models.CharField(max_length=100,
                             db_index=True,
                             default='',
                             blank=True)
    region = models.ForeignKey(Region, null=True, on_delete=models.SET_NULL)
    provider_tasks = models.ManyToManyField(ProviderTask,
                                            related_name='provider_tasks')
    configs = models.ManyToManyField(ExportConfig,
                                     related_name='configs',
                                     blank=True)
    published = models.BooleanField(default=False,
                                    db_index=True)  # publish export
    feature_save = models.BooleanField(
        default=False, db_index=True)  # save feature selections
    feature_pub = models.BooleanField(
        default=False, db_index=True)  # publish feature selections
    the_geom = models.MultiPolygonField(verbose_name='Extent for export',
                                        srid=4326,
                                        default='')
    the_geom_webmercator = models.MultiPolygonField(
        verbose_name='Mercator extent for export', srid=3857, default='')
    the_geog = models.MultiPolygonField(
        verbose_name='Geographic extent for export',
        geography=True,
        default='')
    objects = models.GeoManager()
    include_zipfile = models.BooleanField(default=False)
    json_tags = JSONField(default=dict)

    class Meta:  # pragma: no cover
        managed = True
        db_table = 'jobs'

    def save(self, *args, **kwargs):
        self.the_geom = convert_polygon(self.the_geom)
        self.the_geog = GEOSGeometry(self.the_geom)
        self.the_geom_webmercator = self.the_geom.transform(ct=3857,
                                                            clone=True)
        super(Job, self).save(*args, **kwargs)

    def __str__(self):
        return '{0}'.format(self.name)

    @property
    def overpass_extents(self, ):
        """
        Return the export extents in order required by Overpass API.
        """
        extents = GEOSGeometry(self.the_geom).extent  # (w,s,e,n)
        # overpass needs extents in order (s,w,n,e)
        overpass_extents = '{0},{1},{2},{3}'.format(str(extents[1]),
                                                    str(extents[0]),
                                                    str(extents[3]),
                                                    str(extents[2]))
        return overpass_extents

    @property
    def extents(self, ):
        return GEOSGeometry(self.the_geom).extent  # (w,s,e,n)

    @property
    def filters(self, ):
        """
        Return key=value pairs for each tag in this export.

        Used in utils.overpass.filter to filter the export.
        """
        # Command-line key=value filters for osmfilter
        filters = []
        for tag in self.json_tags:
            kv = '{0}={1}'.format(tag['key'], tag['value'])
            filters.append(kv)
        return filters

    @property
    def categorised_tags(self, ):
        """
        Return tags mapped according to their geometry types.
        """
        points = set()
        lines = set()
        polygons = set()
        for tag in self.json_tags:
            if 'point' in tag['geom']:
                points.add(tag['key'])
            if 'line' in tag['geom']:
                lines.add(tag['key'])
            if 'polygon' in tag['geom']:
                polygons.add(tag['key'])
        return {
            'points': sorted(list(points)),
            'lines': sorted(list(lines)),
            'polygons': sorted(list(polygons))
        }

    @property
    def bounds_geojson(self, ):
        return serialize('geojson', [self],
                         geometry_field='the_geom',
                         fields=('name', 'the_geom'))
Exemple #2
0
class JustificativaAusencia(models.Model):

    FIELDFILE_NAME = ('upload_anexo', )

    metadata = JSONField(verbose_name=_('Metadados'),
                         blank=True,
                         null=True,
                         default=None,
                         encoder=DjangoJSONEncoder)

    TIPO_AUSENCIA_CHOICES = Choices(
        (1, 'materia', 'Matéria'),
        (2, 'sessao', 'Sessão'),
    )
    parlamentar = models.ForeignKey(Parlamentar,
                                    on_delete=models.PROTECT,
                                    verbose_name=_('Parlamentar'))
    sessao_plenaria = models.ForeignKey(SessaoPlenaria,
                                        on_delete=models.CASCADE,
                                        verbose_name=_('Sessão Plenária'))
    tipo_ausencia = models.ForeignKey(TipoJustificativa,
                                      on_delete=models.PROTECT,
                                      verbose_name=_('Tipo de Justificativa'))
    data = models.DateField(verbose_name=_('Data'))
    hora = models.CharField(max_length=5, verbose_name=_('Horário (hh:mm)'))
    observacao = models.TextField(max_length=150,
                                  blank=True,
                                  verbose_name=_('Observação'))
    ausencia = models.PositiveIntegerField(verbose_name=_('Ausente em'),
                                           choices=TIPO_AUSENCIA_CHOICES,
                                           default=1)

    materias_do_expediente = models.ManyToManyField(
        ExpedienteMateria,
        blank=True,
        verbose_name=_('Matérias do Expediente'))

    materias_da_ordem_do_dia = models.ManyToManyField(
        OrdemDia, blank=True, verbose_name=_('Matérias do Ordem do Dia'))

    upload_anexo = models.FileField(blank=True,
                                    null=True,
                                    storage=OverwriteStorage(),
                                    upload_to=anexo_upload_path,
                                    verbose_name=_('Anexo de Justificativa'))

    class Meta:
        verbose_name = _('Justificativa de Ausência')
        verbose_name_plural = _('Justificativas de Ausências')

    def __str__(self):
        return 'Justificativa de Ausência'

    def delete(self, using=None, keep_parents=False):
        if self.upload_anexo:
            self.upload_anexo.delete()

        return models.Model.delete(self,
                                   using=using,
                                   keep_parents=keep_parents)

    def save(self,
             force_insert=False,
             force_update=False,
             using=None,
             update_fields=None):

        if not self.pk and self.upload_anexo:
            upload_anexo = self.upload_anexo
            self.upload_anexo = None
            models.Model.save(self,
                              force_insert=force_insert,
                              force_update=force_update,
                              using=using,
                              update_fields=update_fields)

            self.upload_anexo = upload_anexo

        return models.Model.save(self,
                                 force_insert=force_insert,
                                 force_update=force_update,
                                 using=using,
                                 update_fields=update_fields)
Exemple #3
0
class Migration(migrations.Migration):

    dependencies = [
        ('occasions', '0004_character'),
    ]

    operations = [
        migrations.CreateModel(
            name='Meet',
            fields=[
                ('id',
                 models.BigAutoField(auto_created=True,
                                     primary_key=True,
                                     serialize=False,
                                     verbose_name='ID')),
                ('created',
                 model_utils.fields.AutoCreatedField(
                     default=django.utils.timezone.now,
                     editable=False,
                     verbose_name='created')),
                ('modified',
                 model_utils.fields.AutoLastModifiedField(
                     default=django.utils.timezone.now,
                     editable=False,
                     verbose_name='modified')),
                ('audience_editable',
                 models.BooleanField(
                     default=True,
                     help_text=
                     '[some meets are editable only by coworkers] participant can edit AttendingMeet?',
                     verbose_name='participant can edit AttendingMeet?')),
                ('shown_audience',
                 models.BooleanField(
                     default=True,
                     db_index=True,
                     help_text=
                     '[some meets are only for internal records] show the AttendingMeet to attendee?',
                     verbose_name='show AttendingMeet to participant?')),
                ('start',
                 models.DateTimeField(null=False,
                                      blank=False,
                                      default=Utility.now_with_timezone)),
                ('finish',
                 models.DateTimeField(
                     null=False,
                     blank=False,
                     help_text="Required for user to filter by time")),
                ('is_removed', models.BooleanField(default=False)),
                ('site_type',
                 models.ForeignKey(
                     help_text='site: django_content_type id for table name',
                     on_delete=models.SET(0),
                     to='contenttypes.ContentType')),
                ('site_id', models.BigIntegerField()),
                ('assembly',
                 models.ForeignKey(
                     blank=True,
                     null=True,
                     on_delete=django.db.models.deletion.SET_NULL,
                     to='occasions.Assembly')),
                ('slug', models.SlugField(max_length=50, unique=True)),
                ('display_name',
                 models.CharField(
                     blank=True,
                     null=True,
                     db_index=True,
                     help_text=
                     'The Rock, Little Foot, singspiration, A/V control, etc.',
                     max_length=50)),
                ('infos',
                 JSONField(
                     blank=True,
                     default=dict,
                     help_text=
                     'Example: {"info": "...", "url": "https://..."}. Please keep {} here even no data',
                     null=True)),
            ],
            options={
                'db_table': 'occasions_meets',
            },
            bases=(models.Model, Utility),
        ),
    ]
Exemple #4
0
class Attendee(UUIDModel, Utility, TimeStampedModel, SoftDeletableModel):
    # RELATIVES_KEYWORDS = ['parent', 'mother', 'guardian', 'father', 'caregiver']
    # AS_PARENT_KEYWORDS = ['notifier', 'caregiver']  # to find attendee's parents/caregiver in cowokers view of all activities
    # BE_LISTED_KEYWORDS = ['care receiver']  # let the attendee's attendance showed in their parent/caregiver account
    pasts = GenericRelation('persons.Past')
    places = GenericRelation('whereabouts.Place')
    notes = GenericRelation(Note)
    related_ones = models.ManyToManyField('self',
                                          through='Relationship',
                                          symmetrical=False,
                                          related_name='related_to')
    division = models.ForeignKey('whereabouts.Division',
                                 default=0,
                                 null=False,
                                 blank=False,
                                 on_delete=models.SET(0))
    user = models.OneToOneField(settings.AUTH_USER_MODEL,
                                default=None,
                                null=True,
                                blank=True,
                                on_delete=models.SET_NULL)
    families = models.ManyToManyField('persons.Family',
                                      through='FamilyAttendee',
                                      related_name='families')
    first_name = models.CharField(max_length=25,
                                  db_index=True,
                                  null=True,
                                  blank=True)
    last_name = models.CharField(max_length=25,
                                 db_index=True,
                                 null=True,
                                 blank=True)
    first_name2 = models.CharField(max_length=12,
                                   db_index=True,
                                   null=True,
                                   blank=True)
    last_name2 = models.CharField(max_length=8,
                                  db_index=True,
                                  null=True,
                                  blank=True)
    gender = models.CharField(max_length=11,
                              blank=False,
                              null=False,
                              default=GenderEnum.UNSPECIFIED,
                              choices=GenderEnum.choices())
    actual_birthday = models.DateField(blank=True, null=True)
    estimated_birthday = models.DateField(blank=True, null=True)
    deathday = models.DateField(blank=True, null=True)
    photo = PrivateFileField(
        "Photo", blank=True, null=True, upload_to="attendee_portrait"
    )  #https://github.com/edoburu/django-private-storage
    progressions = JSONField(
        null=True,
        blank=True,
        default=dict,
        help_text=
        'Example: {"Christian": true, "baptized": {"time": "12/31/2020", "place":"SF"}}. Please keep {} here even no data'
    )
    infos = JSONField(
        null=True,
        blank=True,
        default=Utility.attendee_infos,
        help_text=
        'Example: {"fixed": {"food_pref": "peanut allergy", "nick_name": "John"}}. Please keep {} here even no data'
    )

    @property
    def display_label(self):
        return (self.first_name or '') + ' ' + (self.last_name or '') + ' ' + (
            self.last_name2 or '') + (self.first_name2 or '')

    @property
    def division_label(self):
        return self.division.display_name if self.division else None

    @cached_property
    def family_members(self):
        return self.__class__.objects.filter(
            families__in=self.families.all()).distinct()

    @cached_property
    def self_phone_numbers(self):
        return self.self_addresses_for_fields_of(['phone1', 'phone2'])

    @cached_property
    def self_email_addresses(self):
        return self.self_addresses_for_fields_of(['email1', 'email2'])

    def self_addresses_for_fields_of(self, fields):
        contacts = self.infos.get('contacts', {})
        return ', '.join(
            [contacts.get(field) for field in fields if contacts.get(field)])

    @cached_property
    def caregiver_email_addresses(self):
        return self.caregiver_addresses_for_fields_of(['email1', 'email2'])

    @cached_property
    def caregiver_phone_numbers(self):
        return self.caregiver_addresses_for_fields_of(['phone1', 'phone2'])

    def caregiver_addresses_for_fields_of(self, fields):
        return ', '.join(
            set(
                a.self_addresses_for_fields_of(fields)
                for a in self.get_relative_emergency_contacts()))

    def get_relative_emergency_contacts(self):
        return self.related_ones.filter(
            to_attendee__relation__relative=True,
            to_attendee__relation__emergency_contact=True,
            to_attendee__finish__gte=datetime.now(timezone.utc),
        )

    def under_same_org_with(self, other_attendee_id):
        if other_attendee_id:
            return Attendee.objects.filter(
                pk=other_attendee_id,
                division__organization=self.division.organization).exists()
        return False

    @cached_property
    def parents_notifiers_names(self):
        """
        :return: attendees' names of their parents/caregiviers
        """
        return ', '.join(
            list(self.get_relative_emergency_contacts().order_by(
                '-to_attendee__relation__display_order', ).values_list(
                    'infos__names__original', flat=True)))

    def age(self):
        birthday = self.actual_birthday or self.estimated_birthday
        try:
            if birthday:
                return (date.today() - birthday) // timedelta(days=365.2425)
            else:
                return None
        except Exception as e:
            print(self.__str__() + "'s birthday incorrect: ", birthday,
                  '. Type: ', type(birthday), ' exception: ', e)
            return None

    def __str__(self):
        return self.display_label

    # def all_relations(self): #cannot import Relationship, probably needs native query
    #     return dict(((r.from_attendee, r.relation) if r.to_attendee == self else (r.to_attendee, r.relation) for r in Relationship.objects.filter(Q(from_attendee=self.id) | Q(to_attendee=self.id))))
    # switching to symmetrical False with Facebook model (but add relationship both ways and need add/remove_relationship methods) http://charlesleifer.com/blog/self-referencing-many-many-through/
    # also attendee.related_ones will return deleted relationship, so extra filter is required (.filter(relationship__is_removed = False))

    def clean(self):
        if not (self.last_name or self.last_name2):
            raise ValidationError("You must specify a last_name")

    def get_absolute_url(self):
        return reverse('/persons/attendee_detail_view/',
                       kwargs={'pk': self.pk})

    class Meta:
        db_table = 'persons_attendees'
        ordering = ['last_name', 'first_name']
        indexes = [
            GinIndex(
                fields=['infos'],
                name='attendee_infos_gin',
            ),
            GinIndex(
                fields=['progressions'],
                name='attendee_progressions_gin',
            ),
        ]

    def save(self, *args, **kwargs):
        name = f"{self.first_name or ''} {self.last_name or ''}".strip()
        name2 = f"{self.last_name2 or ''}{self.first_name2 or ''}".strip()
        self.infos['names']['original'] = f"{name} {name2}".strip()
        if self.division.organization.infos.get(
                'flags',
            {}).get('opencc_convert'):  # Let search work in either language
            self.infos['names']['traditional'] = OpenCC('s2t').convert(name2)
            self.infos['names']['simplified'] = OpenCC('t2s').convert(name2)
        # if self.division.organization.infos.flags.accent_convert:  # For Spanish, Not in V1
        #     self.infos['names']['unaccented'] = converter(name)
        super(Attendee, self).save(*args, **kwargs)
Exemple #5
0
class Item(models.Model):
    item_type = models.ForeignKey(ItemType,
                                  on_delete=models.CASCADE,
                                  blank=False)
    data = JSONField("data", default=defaultJSON, blank=False)
class PhysicalArtifactFormDataRecord(models.Model):

    INSTANCE_ID_SEPARATOR = "__instance-"

    ACTION_TYPE_ASSIGN_VALUE = 'value'
    ACTION_TYPE_ASSIGN_COMMENT = 'comment'

    COMPONENT_TYPE_TEXT = 'text'
    COMPONENT_TYPE_TAB = 'tab'
    COMPONENT_TYPE_SECTION = 'section'
    COMPONENT_TYPE_GROUP = 'group'
    COMPONENT_TYPE_NUMBER = 'number'
    COMPONENT_TYPE_EMAIL = 'email'
    COMPONENT_TYPE_SELECT = 'select'
    COMPONENT_TYPE_MULTI_SELECT = 'multi-select'
    COMPONENT_TYPE_TEXT_AREA = 'text_area'
    COMPONENT_TYPE_TABLE = 'table'
    COMPONENT_TYPE_EXPANDER_TABLE = 'expander_table'
    COMPONENT_TYPE_LABEL = 'label'
    COMPONENT_TYPE_RADIO = 'radiobuttons'
    COMPONENT_TYPE_CHECKBOX = 'checkbox'
    COMPONENT_TYPE_DECLARATION = 'declaration'
    COMPONENT_TYPE_FILE = 'file'
    COMPONENT_TYPE_DATE = 'date'
    COMPONENT_TYPE_CHOICES = (
        (COMPONENT_TYPE_TEXT, 'Text'),
        (COMPONENT_TYPE_TAB, 'Tab'),
        (COMPONENT_TYPE_SECTION, 'Section'),
        (COMPONENT_TYPE_GROUP, 'Group'),
        (COMPONENT_TYPE_NUMBER, 'Number'),
        (COMPONENT_TYPE_EMAIL, 'Email'),
        (COMPONENT_TYPE_SELECT, 'Select'),
        (COMPONENT_TYPE_MULTI_SELECT, 'Multi-Select'),
        (COMPONENT_TYPE_TEXT_AREA, 'Text Area'),
        (COMPONENT_TYPE_TABLE, 'Table'),
        (COMPONENT_TYPE_EXPANDER_TABLE, 'Expander Table'),
        (COMPONENT_TYPE_LABEL, 'Label'),
        (COMPONENT_TYPE_RADIO, 'Radio'),
        (COMPONENT_TYPE_CHECKBOX, 'Checkbox'),
        (COMPONENT_TYPE_DECLARATION, 'Declaration'),
        (COMPONENT_TYPE_FILE, 'File'),
        (COMPONENT_TYPE_DATE, 'Date'),
    )

    physical_artifact = models.ForeignKey(PhysicalArtifact,
                                          related_name='form_data_records')
    field_name = models.CharField(max_length=512, blank=True, null=True)
    schema_name = models.CharField(max_length=256, blank=True, null=True)
    instance_name = models.CharField(max_length=256, blank=True, null=True)
    component_type = models.CharField(max_length=64,
                                      choices=COMPONENT_TYPE_CHOICES,
                                      default=COMPONENT_TYPE_TEXT)
    value = JSONField(blank=True, null=True)
    comment = models.TextField(blank=True, null=True)
    deficiency = models.TextField(blank=True, null=True)

    def __str__(self):
        return "Physical Artifact {id} record {field}: {value}".format(
            id=self.physical_artifact_id,
            field=self.field_name,
            value=self.value[:8])

    class Meta:
        app_label = 'wildlifecompliance'
        unique_together = (
            'physical_artifact',
            'field_name',
        )

    @staticmethod
    #def process_form(request, PhysicalArtifact, form_data, action=ACTION_TYPE_ASSIGN_VALUE):
    def process_form(PhysicalArtifact,
                     form_data,
                     action=ACTION_TYPE_ASSIGN_VALUE):
        #can_edit_comments = request.user.has_perm(
        #    'wildlifecompliance.licensing_officer'
        #) or request.user.has_perm(
        #    'wildlifecompliance.assessor'
        #)
        #can_edit_deficiencies = request.user.has_perm(
        #    'wildlifecompliance.licensing_officer'
        #)

        if action == PhysicalArtifactFormDataRecord.ACTION_TYPE_ASSIGN_COMMENT and\
                not can_edit_comments and not can_edit_deficiencies:
            raise Exception('You are not authorised to perform this action!')

        for field_name, field_data in form_data.items():
            schema_name = field_data.get('schema_name', '')
            component_type = field_data.get('component_type', '')
            value = field_data.get('value', '')
            comment = field_data.get('comment_value', '')
            deficiency = field_data.get('deficiency_value', '')
            instance_name = ''

            if PhysicalArtifactFormDataRecord.INSTANCE_ID_SEPARATOR in field_name:
                [parsed_schema_name, instance_name] = field_name.split(
                    PhysicalArtifactFormDataRecord.INSTANCE_ID_SEPARATOR)
                schema_name = schema_name if schema_name else parsed_schema_name

            form_data_record, created = PhysicalArtifactFormDataRecord.objects.get_or_create(
                physical_artifact_id=PhysicalArtifact.id,
                field_name=field_name)
            if created:
                form_data_record.schema_name = schema_name
                form_data_record.instance_name = instance_name
                form_data_record.component_type = component_type
            if action == PhysicalArtifactFormDataRecord.ACTION_TYPE_ASSIGN_VALUE:
                form_data_record.value = value
            elif action == PhysicalArtifactFormDataRecord.ACTION_TYPE_ASSIGN_COMMENT:
                if can_edit_comments:
                    form_data_record.comment = comment
                if can_edit_deficiencies:
                    form_data_record.deficiency = deficiency
            form_data_record.save()
Exemple #7
0
class WildlifeLicence(models.Model):
    STATUS_CHOICES = (
        ('current','Current'),
        ('expired','Expired'),
        ('cancelled','Cancelled'),
        ('surrendered','Surrendered'),
        ('suspended','Suspended')
    )
    status = models.CharField(max_length=40, choices=STATUS_CHOICES,
                                       default=STATUS_CHOICES[0][0])
    parent_licence=models.ForeignKey('self',blank=True,null=True,related_name='children_licence')
    licence_document = models.ForeignKey(LicenceDocument, blank=True, null=True, related_name='licence_document')
    cover_letter_document = models.ForeignKey(LicenceDocument, blank=True, null=True, related_name='cover_letter_document')
    replaced_by = models.ForeignKey('self', blank=True, null=True)
    current_application = models.ForeignKey(Application,related_name = '+')
    activity = models.CharField(max_length=255, blank=True, null=True)
    region = models.CharField(max_length=255, blank=True, null=True)
    tenure = models.CharField(max_length=255, blank=True, null=True)
    title = models.CharField(max_length=255, blank=True, null=True)
    renewal_sent = models.BooleanField(default=False)
    issue_date = models.DateField(blank=True,null=True)
    original_issue_date = models.DateField(auto_now_add=True)
    start_date = models.DateField(blank=True,null=True)
    expiry_date = models.DateField(blank=True,null=True)
    surrender_details = JSONField(blank=True,null=True)
    suspension_details = JSONField(blank=True,null=True)
    # applicant = models.ForeignKey(Organisation,on_delete=models.PROTECT, related_name='wildlifecompliance_licences')

    org_applicant = models.ForeignKey(Organisation, blank=True, null=True, related_name='wildlifecompliance_org_applicant')
    proxy_applicant = models.ForeignKey(EmailUser, blank=True, null=True, related_name='wildlifecompliance_proxy_applicant')
    submitter = models.ForeignKey(EmailUser, blank=True, null=True, related_name='wildlifecompliance_submitter')

    extracted_fields = JSONField(blank=True, null=True)
    licence_activity_type=models.ForeignKey('WildlifeLicenceActivityType',null=True)
    licence_type=models.ForeignKey('WildlifeLicenceActivity',null=True)

    licence_number = models.CharField(max_length=64, blank=True, null=True)
    licence_sequence = models.IntegerField(blank=True, default=1)
    licence_class = models.ForeignKey(WildlifeLicenceClass)
    #licence_sequence = models.IntegerField(blank=True, unique=True, default=seq_idx)

    # licence_activity_type = models.ForeignKey(WildlifeLicenceActivityType)
    # licence_descriptor = models.ForeignKey(WildlifeLicenceDescriptor)

    class Meta:
        unique_together = (('licence_number','licence_sequence','licence_class'))
        app_label = 'wildlifecompliance'

    def __str__(self):
        return '{} {}-{}'.format(self.licence_type, self.licence_number, self.licence_sequence)

    def save(self, *args, **kwargs):
        super(WildlifeLicence, self).save(*args,**kwargs)
        if not self.licence_number:
            self.licence_number = 'L{0:06d}'.format(self.next_licence_number_id)
            #self.licence_number = 'L{0:06d}'.format(self.pk)
            self.save()

    @property
    def next_licence_number_id(self):
        licence_number_max = WildlifeLicence.objects.all().aggregate(Max('licence_number'))['licence_number__max']
        if licence_number_max == None:
            return self.pk
        else:
            return int(licence_number_max.split('L')[1]) + 1


#    def seq_idx():
#        no = WildlifeLicence.objects.get().aggregate(Max(order))
#        if no == None:
#            return 1
#        else:
#            return no + 1

    @property
    def reference(self):
        return '{}-{}'.format(self.licence_number, self.licence_sequence)

    @property
    def is_issued(self):
        return self.licence_number is not None and len(self.licence_number) > 0

    def generate_doc(self):
        from wildlifecompliance.components.licences.pdf import create_licence_doc
        self.licence_document = create_licence_doc(self,self.current_application)
        self.save()

    def log_user_action(self, action, request):
        return LicenceUserAction.log_action(self, action, request.user)
Exemple #8
0
class MensagemSolicitacao(models.Model):

    FIELDFILE_NAME = ('anexo', )

    metadata = JSONField(
        verbose_name=_('Metadados'),
        blank=True, null=True, default=None, encoder=DjangoJSONEncoder)

    created = models.DateTimeField(
        verbose_name=_('created'), editable=False, auto_now_add=True)

    owner = models.ForeignKey(
        get_settings_auth_user_model(),
        blank=True, null=True, default=None,
        verbose_name=_('owner'), related_name='+',
        on_delete=PROTECT)

    descricao = models.TextField(
        default='',  verbose_name=_('Descrição'))

    solicitacao = models.ForeignKey(
        Solicitacao,
        verbose_name=_('Solicitação'),
        related_name='mensagemsolicitacao_set',
        on_delete=PROTECT)

    notificacoes = GenericRelation(
        Notificacao, related_query_name='notificacoes')

    anexo = models.FileField(
        blank=True,
        null=True,
        storage=media_protected_storage,
        upload_to=anexo_ouvidoria_path,
        verbose_name=_('Anexo'),
        validators=[restringe_tipos_de_arquivo_midias],
        help_text=_('Envie um arquivo em anexo a sua mensagem.'))

    content_type = models.CharField(
        max_length=250,
        default='')

    class Meta:
        ordering = ('created', )
        verbose_name = _('Mensagem de Solicitação')
        verbose_name_plural = _('Mensagens de Solicitação')

    def delete(self, using=None, keep_parents=False):
        if self.anexo:
            self.anexo.delete()

        return models.Model.delete(
            self, using=using, keep_parents=keep_parents)

    def save(self, force_insert=False, force_update=False, using=None,
             update_fields=None):

        if not self.pk and self.anexo:
            anexo = self.anexo
            self.anexo = None
            models.Model.save(self, force_insert=force_insert,
                              force_update=force_update,
                              using=using,
                              update_fields=update_fields)
            self.anexo = anexo
            self.content_type = self.anexo.file.content_type

            if self.anexo.file.content_type in TIPOS_MIDIAS_PERMITIDOS:
                name_file = 'anexo.%s' % TIPOS_MIDIAS_PERMITIDOS[self.content_type]
                self.anexo.save(name_file, self.anexo.file)

        models.Model.save(self, force_insert=force_insert,
                          force_update=force_update, using=using, update_fields=update_fields)

    @property
    def email_notify(self):
        return {
            'subject': self.solicitacao.email_notify['subject'],
        }

        """'body': ('Solicitação: ' + self.solicitacao.titulo, self.descricao),
        'owner': self.owner,
        'created': self.created"""

    def __str__(self):
        return self.descricao
Exemple #9
0
class Approval(RevisionedMixin):
    STATUS_CHOICES = (('current', 'Current'), ('expired', 'Expired'),
                      ('cancelled', 'Cancelled'),
                      ('surrendered', 'Surrendered'), ('suspended',
                                                       'Suspended'))
    lodgement_number = models.CharField(max_length=9, blank=True, default='')
    status = models.CharField(max_length=40,
                              choices=STATUS_CHOICES,
                              default=STATUS_CHOICES[0][0])
    licence_document = models.ForeignKey(ApprovalDocument,
                                         blank=True,
                                         null=True,
                                         related_name='licence_document')
    cover_letter_document = models.ForeignKey(
        ApprovalDocument,
        blank=True,
        null=True,
        related_name='cover_letter_document')
    replaced_by = models.ForeignKey('self', blank=True, null=True)
    #current_proposal = models.ForeignKey(Proposal,related_name = '+')
    current_proposal = models.ForeignKey(Proposal, related_name='approvals')
    #    activity = models.CharField(max_length=255)
    #    region = models.CharField(max_length=255)
    #    tenure = models.CharField(max_length=255,null=True)
    #    title = models.CharField(max_length=255)
    renewal_document = models.ForeignKey(ApprovalDocument,
                                         blank=True,
                                         null=True,
                                         related_name='renewal_document')
    renewal_sent = models.BooleanField(default=False)
    issue_date = models.DateTimeField()
    original_issue_date = models.DateField(auto_now_add=True)
    start_date = models.DateField()
    expiry_date = models.DateField()
    surrender_details = JSONField(blank=True, null=True)
    suspension_details = JSONField(blank=True, null=True)
    applicant = models.ForeignKey(Organisation,
                                  on_delete=models.PROTECT,
                                  related_name='disturbance_approvals')
    extracted_fields = JSONField(blank=True, null=True)
    cancellation_details = models.TextField(blank=True)
    cancellation_date = models.DateField(blank=True, null=True)
    set_to_cancel = models.BooleanField(default=False)
    set_to_suspend = models.BooleanField(default=False)
    set_to_surrender = models.BooleanField(default=False)
    reissued = models.BooleanField(default=False)

    class Meta:
        app_label = 'disturbance'
        unique_together = ('lodgement_number', 'issue_date')

    @property
    def region(self):
        return self.current_proposal.region.name

    @property
    def district(self):
        return self.current_proposal.district.name

    @property
    def tenure(self):
        return self.current_proposal.tenure.name

    @property
    def activity(self):
        return self.current_proposal.activity

    @property
    def title(self):
        return self.current_proposal.title

    @property
    def next_id(self):
        #ids = map(int,[(i.lodgement_number.split('A')[1]) for i in Approval.objects.all()])
        ids = map(int, [
            i.split('A')[1]
            for i in Approval.objects.all().values_list('lodgement_number',
                                                        flat=True) if i
        ])
        return max(ids) + 1 if ids else 1

    def save(self, *args, **kwargs):
        super(Approval, self).save(*args, **kwargs)
        if self.lodgement_number == '':
            self.lodgement_number = 'A{0:06d}'.format(self.next_id)
            self.save()

    def __str__(self):
        return self.lodgement_number

    @property
    def reference(self):
        return 'A{}'.format(self.id)

    @property
    def can_reissue(self):
        return self.status == 'current' or self.status == 'suspended'

    @property
    def can_reinstate(self):
        return (self.status == 'cancelled' or self.status == 'suspended'
                or self.status == 'surrendered') and self.can_action

    @property
    def allowed_assessors(self):
        return self.current_proposal.compliance_assessors

    @property
    def allowed_approvers(self):
        return self.current_proposal.allowed_approvers

    @property
    def is_issued(self):
        return self.licence_number is not None and len(self.licence_number) > 0

    @property
    def can_action(self):
        if not (self.set_to_cancel or self.set_to_suspend
                or self.set_to_surrender):
            return True
        else:
            return False

    @property
    def can_renew(self):
        try:
            renew_conditions = {
                'previous_application': self.current_proposal,
                'proposal_type': 'renewal'
            }
            proposal = Proposal.objects.get(**renew_conditions)
            if proposal:
                return False
        except Proposal.DoesNotExist:
            return True

    @property
    def can_amend(self):
        try:
            amend_conditions = {
                'previous_application': self.current_proposal,
                'proposal_type': 'amendment'
            }
            proposal = Proposal.objects.get(**amend_conditions)
            if proposal:
                return False
        except Proposal.DoesNotExist:
            if self.can_renew:
                return True
            else:
                return False

    def generate_doc(self, user, preview=False):
        from disturbance.components.approvals.pdf import create_approval_doc, create_approval_pdf_bytes
        copied_to_permit = self.copiedToPermit_fields(
            self.current_proposal)  #Get data related to isCopiedToPermit tag
        if preview:
            return create_approval_pdf_bytes(self, self.current_proposal,
                                             copied_to_permit, user)
        self.licence_document = create_approval_doc(self,
                                                    self.current_proposal,
                                                    copied_to_permit, user)
        self.save(version_comment='Created Approval PDF: {}'.format(
            self.licence_document.name))
        self.current_proposal.save(version_comment='Created Approval PDF: {}'.
                                   format(self.licence_document.name))

    def generate_renewal_doc(self):
        from disturbance.components.approvals.pdf import create_renewal_doc
        self.renewal_document = create_renewal_doc(self, self.current_proposal)
        self.save(version_comment='Created Approval PDF: {}'.format(
            self.renewal_document.name))
        self.current_proposal.save(version_comment='Created Approval PDF: {}'.
                                   format(self.renewal_document.name))

    def copiedToPermit_fields(self, proposal):
        p = proposal
        copied_data = []
        search_assessor_data = []
        search_schema = search_multiple_keys(p.schema,
                                             primary_search='isCopiedToPermit',
                                             search_list=['label', 'name'])
        if p.assessor_data:
            search_assessor_data = search_keys(
                p.assessor_data, search_list=['assessor', 'name'])
        if search_schema:
            for c in search_schema:
                try:
                    if search_assessor_data:
                        for d in search_assessor_data:
                            if c['name'] == d['name']:
                                if d['assessor']:
                                    #copied_data.append({c['label'], d['assessor']})
                                    copied_data.append(
                                        {c['label']: d['assessor']})
                except:
                    raise
        return copied_data

    def log_user_action(self, action, request):
        return ApprovalUserAction.log_action(self, action, request.user)

    def expire_approval(self, user):
        with transaction.atomic():
            try:
                today = timezone.localtime(timezone.now()).date()
                if self.status == 'current' and self.expiry_date < today:
                    self.status = 'expired'
                    self.save()
                    send_approval_expire_email_notification(self)
                    proposal = self.current_proposal
                    ApprovalUserAction.log_action(
                        self,
                        ApprovalUserAction.ACTION_EXPIRE_APPROVAL.format(
                            self.id), user)
                    ProposalUserAction.log_action(
                        proposal,
                        ProposalUserAction.ACTION_EXPIRED_APPROVAL_.format(
                            proposal.id), user)
            except:
                raise

    def approval_cancellation(self, request, details):
        with transaction.atomic():
            try:
                if not request.user in self.allowed_assessors:
                    raise ValidationError(
                        'You do not have access to cancel this approval')
                if not self.can_reissue and self.can_action:
                    raise ValidationError(
                        'You cannot cancel approval if it is not current or suspended'
                    )
                self.cancellation_date = details.get(
                    'cancellation_date').strftime('%Y-%m-%d')
                self.cancellation_details = details.get('cancellation_details')
                cancellation_date = datetime.datetime.strptime(
                    self.cancellation_date, '%Y-%m-%d')
                cancellation_date = cancellation_date.date()
                self.cancellation_date = cancellation_date
                today = timezone.now().date()
                if cancellation_date <= today:
                    if not self.status == 'cancelled':
                        self.status = 'cancelled'
                        self.set_to_cancel = False
                        send_approval_cancel_email_notification(self)
                else:
                    self.set_to_cancel = True
                    send_approval_cancel_email_notification(self,
                                                            future_cancel=True)
                #import ipdb; ipdb.set_trace()
                self.save()
                # Log proposal action
                self.log_user_action(
                    ApprovalUserAction.ACTION_CANCEL_APPROVAL.format(self.id),
                    request)
                # Log entry for organisation
                #self.current_proposal.log_user_action(ProposalUserAction.ACTION_CANCEL_APPROVAL.format(self.current_proposal.id),request)
            except:
                raise

    def approval_suspension(self, request, details):
        with transaction.atomic():
            try:
                if not request.user in self.allowed_assessors:
                    raise ValidationError(
                        'You do not have access to suspend this approval')
                if not self.can_reissue and self.can_action:
                    raise ValidationError(
                        'You cannot suspend approval if it is not current or suspended'
                    )
                if details.get('to_date'):
                    to_date = details.get('to_date').strftime('%d/%m/%Y')
                else:
                    to_date = ''
                self.suspension_details = {
                    'from_date': details.get('from_date').strftime('%d/%m/%Y'),
                    'to_date': to_date,
                    'details': details.get('suspension_details'),
                }
                today = timezone.now().date()
                from_date = datetime.datetime.strptime(
                    self.suspension_details['from_date'], '%d/%m/%Y')
                from_date = from_date.date()
                if from_date <= today:
                    if not self.status == 'suspended':
                        self.status = 'suspended'
                        self.set_to_suspend = False
                        self.save()
                        send_approval_suspend_email_notification(self)
                else:
                    self.set_to_suspend = True
                    send_approval_suspend_email_notification(
                        self, future_suspend=True)
                self.save()
                # Log approval action
                self.log_user_action(
                    ApprovalUserAction.ACTION_SUSPEND_APPROVAL.format(self.id),
                    request)
                # Log entry for proposal
                #self.current_proposal.log_user_action(ProposalUserAction.ACTION_SUSPEND_APPROVAL.format(self.current_proposal.id),request)
            except:
                raise

    def reinstate_approval(self, request):
        with transaction.atomic():
            try:
                if not request.user in self.allowed_assessors:
                    raise ValidationError(
                        'You do not have access to reinstate this approval')
                if not self.can_reinstate:
                    #if not self.status == 'suspended':
                    raise ValidationError(
                        'You cannot reinstate approval at this stage')
                today = timezone.now().date()
                if not self.can_reinstate and self.expiry_date >= today:
                    #if not self.status == 'suspended' and self.expiry_date >= today:
                    raise ValidationError(
                        'You cannot reinstate approval at this stage')
                if self.status == 'cancelled':
                    self.cancellation_details = ''
                    self.cancellation_date = None
                if self.status == 'surrendered':
                    self.surrender_details = {}
                if self.status == 'suspended':
                    self.suspension_details = {}

                self.status = 'current'
                #self.suspension_details = {}
                self.save()
                send_approval_reinstate_email_notification(self, request)
                # Log approval action
                self.log_user_action(
                    ApprovalUserAction.ACTION_REINSTATE_APPROVAL.format(
                        self.id), request)
                # Log entry for proposal
                #self.current_proposal.log_user_action(ProposalUserAction.ACTION_REINSTATE_APPROVAL.format(self.current_proposal.id),request)
            except:
                raise

    def approval_surrender(self, request, details):
        with transaction.atomic():
            try:
                if not request.user.disturbance_organisations.filter(
                        organisation_id=self.applicant.id):
                    if not request.user in self.allowed_assessors:
                        raise ValidationError(
                            'You do not have access to surrender this approval'
                        )
                if not self.can_reissue and self.can_action:
                    raise ValidationError(
                        'You cannot surrender approval if it is not current or suspended'
                    )
                self.surrender_details = {
                    'surrender_date':
                    details.get('surrender_date').strftime('%d/%m/%Y'),
                    'details':
                    details.get('surrender_details'),
                }
                today = timezone.now().date()
                surrender_date = datetime.datetime.strptime(
                    self.surrender_details['surrender_date'], '%d/%m/%Y')
                surrender_date = surrender_date.date()
                if surrender_date <= today:
                    if not self.status == 'surrendered':
                        self.status = 'surrendered'
                        self.set_to_surrender = False
                        self.save()
                        send_approval_surrender_email_notification(self)
                else:
                    self.set_to_surrender = True
                    send_approval_surrender_email_notification(
                        self, future_surrender=True)
                self.save()
                # Log approval action
                self.log_user_action(
                    ApprovalUserAction.ACTION_SURRENDER_APPROVAL.format(
                        self.id), request)
                # Log entry for proposal
                #self.current_proposal.log_user_action(ProposalUserAction.ACTION_SURRENDER_APPROVAL.format(self.current_proposal.id),request)
            except:
                raise

    def pdf_view_log(self, request):
        self.log_user_action(
            ApprovalUserAction.ACTION_APPROVAL_PDF_VIEW.format(self.id),
            request)
        return self
Exemple #10
0
class Institute(models.Model):
    institute_id = models.AutoField(primary_key=True, editable=False)
    code = models.CharField(max_length=20, db_index=True, unique=True)
    name = models.CharField(max_length=255, db_index=True)
    data = JSONField()

    class Meta:
        db_table = 'vavilov_institute'

    def __str__(self):
        return '{}({})'.format(self.name, self.code)

    @property
    def num_accessions(self):
        return Accession.objects.filter(institute=self).count()

    @property
    def num_accessionsets(self):
        return AccessionSet.objects.filter(institute=self).count()

    def stats_by_country(self, user=None):
        stats = {}
        accession_stats = Country.objects.raw(
            get_institute_stats_raw_sql(institute_id=self.institute_id,
                                        stats_type='country',
                                        entity_type='accession',
                                        user=user))

        accessionset_stats = Country.objects.raw(
            get_institute_stats_raw_sql(institute_id=self.institute_id,
                                        stats_type='country',
                                        entity_type='accessionset',
                                        user=user))

        for row in accession_stats:
            _integrate_country_stats(stats, row, 'accession')
        for row in accessionset_stats:
            _integrate_country_stats(stats, row, 'accessionset')
        return sorted(stats.values(), key=itemgetter('num_accessions'),
                      reverse=True)

    def stats_by_taxa(self, user=None):
        stats = {}
        accession_stats = Taxon.objects.raw(
            get_institute_stats_raw_sql(institute_id=self.institute_id,
                                        stats_type='taxa',
                                        entity_type='accession',
                                        user=user))
        accessionset_stats = Taxon.objects.raw(
            get_institute_stats_raw_sql(institute_id=self.institute_id,
                                        stats_type='taxa',
                                        entity_type='accessionset',
                                        user=user))
        for row in accession_stats:
            _integrate_taxa_stats(stats, row, 'accession')
        for row in accessionset_stats:
            _integrate_taxa_stats(stats, row, 'accessionset')
        return stats

    @property
    def pdcis(self):
        pdcis = [float(p['pdci']) for p in Passport.objects.filter(institute=self).values('pdci')]
        return pdci_distrib(pdcis)
Exemple #11
0
class Payment(models.Model):
    """ 
        Payment model.
        
        State `STATUS_PREAUTHORIZED_UNPAID` is a special state, where a future payment
            has been authorized and saved at the payment proveider, and only needs a special
            API call to confirm and "cash in" the payment. 
            This is used for postponed subscriptions, that are created while a current 
            subscription is still running.
    """
    
    TYPE_CHOICES = (
        (PAYMENT_TYPE_DIRECT_DEBIT, _('Direct Debit (SEPA)')),
        (PAYMENT_TYPE_CREDIT_CARD, _('Credit Card')),
        (PAYMENT_TYPE_PAYPAL, _('PayPal')),
    )
    
    STATUS_NOT_STARTED = 0
    STATUS_STARTED = 1
    STATUS_COMPLETED_BUT_UNCONFIRMED = 2
    STATUS_PAID = 3
    STATUS_PREAUTHORIZED_UNPAID = 301
    STATUS_FAILED = 101
    STATUS_RETRACTED = 102
    STATUS_CANCELED = 103
    
    STATUS_CHOICES = (
        (STATUS_NOT_STARTED, _('Not started')),
        (STATUS_STARTED, _('Payment initiated but not completed by user yet')),
        (STATUS_COMPLETED_BUT_UNCONFIRMED, _('Payment processing')),
        (STATUS_PAID, _('Successfully paid')),
        (STATUS_PREAUTHORIZED_UNPAID, _('Pre-authorized for payment, but not yet paid')),
        (STATUS_FAILED, _('Failed')),
        (STATUS_RETRACTED, _('Retracted')),
        (STATUS_CANCELED, _('Canceled')),
    )
    
    # payments with this status are "done", signalling that
    # further payments may be made on a subscription
    FINALIZED_STATUSES = (
        STATUS_FAILED,
        STATUS_PAID,
    )
    
    user = models.ForeignKey(settings.AUTH_USER_MODEL, editable=False,
        related_name='payments', on_delete=models.CASCADE, null=True)
    subscription = models.ForeignKey('wechange_payments.Subscription', editable=False,
        related_name='payments', on_delete=models.SET_NULL, blank=True, null=True)
    
    vendor_transaction_id = models.CharField(_('Vendor Transaction Id'), max_length=50,
        help_text='An Id for the payment generated by the Payment Service')
    internal_transaction_id = models.CharField(_('Internal Transaction Id'), max_length=50,
        help_text='An Id for the payment generated by us')
    amount = models.FloatField(default='0.0')
    type = models.CharField(_('Payment Type'), blank=False,
        default=PAYMENT_TYPE_DIRECT_DEBIT, choices=TYPE_CHOICES, editable=False, max_length=50)
    last_action_at = models.DateTimeField(verbose_name=_('Last Action At'), editable=False, auto_now=True)
    completed_at = models.DateTimeField(verbose_name=_('Completed At'), editable=False, blank=True, null=True)
    status = models.PositiveSmallIntegerField(_('Payment Status'), blank=False,
        default=STATUS_NOT_STARTED, choices=STATUS_CHOICES, editable=False)
    
    is_reference_payment = models.BooleanField(verbose_name=_('Is reference payment'), default=True, editable=False, 
        help_text='Is this the reference (first) payment in a series or a subscription payment derived from a reference payment?') 
    is_postponed_payment = models.BooleanField(verbose_name=_('Is postponed payment'), default=False, editable=False, 
        help_text='Is this a postponed payment, that gets pre-authorized first, and then cashed-in at some later point?') 
    revoked = models.BooleanField(verbose_name=_('Has been revoked'), default=False)
    
    # billing address details, can be saved for some payment methods, but not necessary
    first_name = models.CharField(blank=True, null=True, max_length=255)
    last_name = models.CharField(blank=True, null=True, max_length=255)
    organisation = models.CharField(blank=True, null=True, max_length=255)
    email = models.EmailField(blank=True, null=True)
    address = models.CharField(blank=True, null=True, max_length=255)
    city = models.CharField(blank=True, null=True, max_length=255)
    postal_code = models.CharField(blank=True, null=True, max_length=255)
    country = CountryField(blank=True, null=True)
    
    backend = models.CharField(_('Backend class used'), max_length=255)
    extra_data = JSONField(null=True, blank=True)
    
    class Meta(object):
        app_label = 'wechange_payments'
        verbose_name = _('Payment')
        verbose_name_plural = _('Payments')
    
    def get_type_string(self):
        return dict(self.TYPE_CHOICES).get(self.type)
Exemple #12
0
class Measurement(models.Model):
    timestamp = models.DateTimeField(auto_now=False, auto_now_add=True)
    patient = models.ForeignKey(Patient, on_delete=models.CASCADE)
    data = JSONField()
Exemple #13
0
class Analysis(models.Model):
    event = models.ForeignKey(Event, on_delete=models.CASCADE)
    updated = models.DateTimeField(auto_now=True, auto_now_add=False)
    start_date = models.DateField(auto_now=True)
    end_date = models.DateField(auto_now=True)
    data = JSONField()
Exemple #14
0
class Event(models.Model):
    timestamp = models.DateTimeField(auto_now=False, auto_now_add=True)
    case = models.ForeignKey(Case, on_delete=models.CASCADE)
    doctor = models.ForeignKey(Doctor, on_delete=models.CASCADE)
    data = JSONField()
Exemple #15
0
class DiarioOficial(CommonMixin):
    FIELDFILE_NAME = ('arquivo', )

    metadata = JSONField(verbose_name=_('Metadados'),
                         blank=True,
                         null=True,
                         default=None,
                         encoder=DjangoJSONEncoder)

    edicao = models.PositiveIntegerField(default=0, verbose_name='Edição')

    descricao = models.CharField(max_length=250, verbose_name=_('Descrição'))

    tipo = models.ForeignKey(TipoDeDiario,
                             on_delete=models.PROTECT,
                             verbose_name=_('Tipo do Diário'))

    data = models.DateField(blank=True, null=True, verbose_name=_('Data'))

    arquivo = models.FileField(blank=True,
                               null=True,
                               upload_to=diario_upload_path,
                               storage=OverwriteStorage(),
                               verbose_name=_('Arquivo Digital do Diário'),
                               validators=[restringe_tipos_de_arquivo_txt])

    normas = models.ManyToManyField(
        'norma.NormaJuridica', verbose_name=_('Normas Publicadas no Diário'))

    data_ultima_atualizacao = models.DateTimeField(blank=True,
                                                   null=True,
                                                   auto_now=True,
                                                   verbose_name=_('Data'))

    class Meta:
        verbose_name = _('Diário Oficial')
        verbose_name_plural = _('Diários Oficiais')
        ordering = ('-data', 'edicao')

    def __str__(self):
        return self.descricao

    @property
    def ano(self):
        return self.data.year

    def save(self,
             force_insert=False,
             force_update=False,
             using=None,
             update_fields=None):

        if not self.pk and self.arquivo:
            arquivo = self.arquivo
            self.arquivo = None
            models.Model.save(self,
                              force_insert=force_insert,
                              force_update=force_update,
                              using=using,
                              update_fields=update_fields)
            self.arquivo = arquivo

        return models.Model.save(self,
                                 force_insert=force_insert,
                                 force_update=force_update,
                                 using=using,
                                 update_fields=update_fields)
Exemple #16
0
class BookingInvoice(RevisionedMixin):
    PAYMENT_METHOD_CC = 0
    PAYMENT_METHOD_BPAY = 1
    PAYMENT_METHOD_MONTHLY_INVOICING = 2
    PAYMENT_METHOD_OTHER = 3
    PAYMENT_METHOD_CHOICES = (
        (PAYMENT_METHOD_CC, 'Credit Card'),
        (PAYMENT_METHOD_BPAY, 'BPAY'),
        (PAYMENT_METHOD_MONTHLY_INVOICING, 'Monthly Invoicing'),
        (PAYMENT_METHOD_OTHER, 'Other (Cash/Cheque)'),
    )

    booking = models.ForeignKey(Booking, related_name='invoices')
    invoice_reference = models.CharField(max_length=50,
                                         null=True,
                                         blank=True,
                                         default='')
    payment_method = models.SmallIntegerField(
        choices=PAYMENT_METHOD_CHOICES, default=0
    )  # duplicating from ledger Invoice model to allow easier filtering on payment dashboard
    deferred_payment_date = models.DateField(blank=True, null=True)
    payment_due_notification_sent = models.BooleanField(default=False)
    property_cache = JSONField(null=True, blank=True, default={})

    def __str__(self):
        return 'Booking {} : Invoice #{}'.format(self.id,
                                                 self.invoice_reference)

    class Meta:
        app_label = 'commercialoperator'

    def save(self, *args, **kwargs):
        self.update_property_cache(False)
        super(BookingInvoice, self).save(*args, **kwargs)

    @property
    def active(self):
        try:
            invoice = Invoice.objects.get(reference=self.invoice_reference)
            return False if invoice.voided else True
        except Invoice.DoesNotExist:
            pass
        return False

    @property
    def invoice(self):
        try:
            invoice = Invoice.objects.get(reference=self.invoice_reference)
            return invoice
        except Invoice.DoesNotExist:
            pass
        return False

    @property
    def overdue(self):
        if self.invoice and self.deferred_payment_date and (
                self.invoice.payment_status == 'unpaid'
                or self.invoice.payment_status == 'partially_paid'
        ) and self.deferred_payment_date < timezone.now().date():
            return True
        return False

    def get_property_cache(self):
        '''
        Get properties which were previously resolved.
        '''
        if len(self.property_cache) == 0:
            self.update_property_cache()

        #if self.processing_status == self.PROCESSING_STATUS_AWAITING_PAYMENT:
        #    self.update_property_cache()

        return self.property_cache

    def get_property_cache_key(self, key):
        '''
        Get properties which were previously resolved with key.
        '''
        try:

            self.property_cache[key]

        except KeyError:
            self.update_property_cache()

        return self.property_cache[key]

    def update_property_cache(self, save=True):
        '''
        Refresh cached properties with updated properties.
        '''
        logger.debug('BookingInvoice.update_property_cache()')

        self.property_cache['payment_status'] = self._payment_status

        if save is True:
            self.save()

        return self.property_cache

    @property
    def payment_status(self):
        """ get cached value, if it exists """
        if 'payment_status' not in self.property_cache:
            self.update_property_cache()

        return self.get_property_cache_key('payment_status')

    @property
    def _payment_status(self):
        if self.invoice:
            payment_status = self.invoice.payment_status
            return ' '.join([
                i.capitalize()
                for i in payment_status.replace('_', ' ').split()
            ])
        return 'Unpaid'
Exemple #17
0
class Migration(migrations.Migration):

    dependencies = [
        ('persons', '0005_family'),
        ('whereabouts', '0004_division'),
        migrations.swappable_dependency(settings.AUTH_USER_MODEL),
    ]

    operations = [
        migrations.CreateModel(
            name='Attendee',
            fields=[
                ('id',
                 model_utils.fields.UUIDField(default=uuid4,
                                              editable=False,
                                              primary_key=True,
                                              serialize=False)),
                ('user',
                 models.OneToOneField(blank=True,
                                      default=None,
                                      null=True,
                                      on_delete=models.deletion.SET_NULL,
                                      to=settings.AUTH_USER_MODEL)),
                ('created',
                 model_utils.fields.AutoCreatedField(
                     default=django.utils.timezone.now,
                     editable=False,
                     verbose_name='created')),
                ('modified',
                 model_utils.fields.AutoLastModifiedField(
                     default=django.utils.timezone.now,
                     editable=False,
                     verbose_name='modified')),
                ('is_removed', models.BooleanField(default=False)),
                ('division',
                 models.ForeignKey(default=0,
                                   blank=False,
                                   null=False,
                                   on_delete=models.SET(0),
                                   to='whereabouts.Division')),
                ('actual_birthday', models.DateField(blank=True, null=True)),
                ('estimated_birthday', models.DateField(blank=True,
                                                        null=True)),
                ('deathday', models.DateField(blank=True, null=True)),
                ('first_name',
                 models.CharField(blank=True,
                                  db_index=True,
                                  max_length=25,
                                  null=True)),
                ('last_name',
                 models.CharField(blank=True,
                                  db_index=True,
                                  max_length=25,
                                  null=True)),
                ('first_name2',
                 models.CharField(blank=True,
                                  db_index=True,
                                  max_length=12,
                                  null=True)),
                ('last_name2',
                 models.CharField(blank=True,
                                  db_index=True,
                                  max_length=8,
                                  null=True)),
                ('gender',
                 models.CharField(choices=GenderEnum.choices(),
                                  default=GenderEnum.UNSPECIFIED,
                                  max_length=11)),
                ('photo',
                 PrivateFileField(blank=True,
                                  null=True,
                                  storage=PrivateFileSystemStorage(),
                                  upload_to='attendee_portrait',
                                  verbose_name='Photo')),
                ('progressions',
                 JSONField(
                     blank=True,
                     default=dict,
                     help_text=
                     'Example: {"Christian": true, "baptized": {"time": "12/31/2020", "place":"SF"}}. Please keep {} here even no data',
                     null=True)),
                ('infos',
                 JSONField(
                     blank=True,
                     null=True,
                     default=Utility.attendee_infos,
                     help_text=
                     'Example: {"fixed": {"food_pref": "peanut allergy", "nick_name": "John"}}. Please keep {} here even no data'
                 )),
            ],
            options={
                'db_table': 'persons_attendees',
                'ordering': ['last_name', 'first_name'],
            },
            bases=(Utility, models.Model),
        ),
        # migrations.RunSQL(
        #     sql="""
        #         ALTER TABLE persons_attendees DROP COLUMN full_name;
        #         ALTER TABLE persons_attendees ADD COLUMN full_name VARCHAR(70)
        #               GENERATED ALWAYS AS (TRIM(
        #                 COALESCE(first_name, '') || ' ' ||
        #                 COALESCE(last_name, '')  || ' ' ||
        #                 COALESCE(last_name2, '')   ||
        #                 COALESCE(first_name2, '')
        #               )) STORED;
        #         CREATE INDEX attendee_full_name_raw
        #           ON persons_attendees (full_name);
        #         """,
        #     # reverse_sql="",
        # ),  # switching to use opencc for language conversion in Attendee.save()
        migrations.AddIndex(
            model_name='attendee',
            index=django.contrib.postgres.indexes.GinIndex(
                fields=['infos'], name='attendee_infos_gin'),
        ),
        migrations.AddIndex(
            model_name='attendee',
            index=django.contrib.postgres.indexes.GinIndex(
                fields=['progressions'], name='attendee_progressions_gin'),
        ),
    ]
Exemple #18
0
class DocumentoAcessorioAdministrativo(CountPageMixin):
    FIELDFILE_NAME = ('arquivo', )

    metadata = JSONField(verbose_name=_('Metadados'),
                         blank=True,
                         null=True,
                         default=None,
                         encoder=DjangoJSONEncoder)

    documento = models.ForeignKey(DocumentoAdministrativo,
                                  on_delete=models.PROTECT)
    tipo = models.ForeignKey(TipoDocumentoAdministrativo,
                             on_delete=models.PROTECT,
                             verbose_name=_('Tipo'))
    nome = models.CharField(max_length=30, verbose_name=_('Nome'))
    arquivo = PortalFileField(blank=True,
                              null=True,
                              upload_to=texto_upload_path,
                              storage=OverwriteStorage(),
                              verbose_name=_('Arquivo'),
                              max_length=512)
    data = models.DateField(blank=True, null=True, verbose_name=_('Data'))
    autor = models.CharField(max_length=50,
                             blank=True,
                             verbose_name=_('Autor'))
    assunto = models.TextField(blank=True, verbose_name=_('Assunto'))
    indexacao = models.TextField(blank=True)

    class Meta:
        verbose_name = _('Documento Acessório')
        verbose_name_plural = _('Documentos Acessórios')

    def __str__(self):
        return self.nome

    def delete(self, using=None, keep_parents=False):
        if self.arquivo:
            self.arquivo.delete()

        return models.Model.delete(self,
                                   using=using,
                                   keep_parents=keep_parents)

    def save(self,
             force_insert=False,
             force_update=False,
             using=None,
             update_fields=None):

        if not self.pk and self.arquivo:
            arquivo = self.arquivo
            self.arquivo = None
            models.Model.save(self,
                              force_insert=force_insert,
                              force_update=force_update,
                              using=using,
                              update_fields=update_fields)
            self.arquivo = arquivo

        return models.Model.save(self,
                                 force_insert=force_insert,
                                 force_update=force_update,
                                 using=using,
                                 update_fields=update_fields)
Exemple #19
0
class StatementLine(models.Model):
    """ Records an single imported bank statement line

    A StatementLine is purely a utility to aid in the creation of transactions
    (in the process known as reconciliation). StatementLines have no impact on
    account balances.

    However, the :meth:`StatementLine.create_transaction()` method can be used to create
    a transaction based on the information in the StatementLine.

    Attributes:

        uuid (SmallUUID): UUID for statement line. Use to prevent leaking of IDs (if desired).
        timestamp (datetime): The datetime when the object was created.
        date (date): The date given by the statement line
        statement_import (StatementImport): The import to which the line belongs
        amount (Decimal): The amount for the statement line, positive or nagative.
        description (str): Any description/memo information provided
        transaction (Transaction): Optionally, the transaction created for this statement line. This normally
            occurs during reconciliation. See also :meth:`StatementLine.create_transaction()`.
    """
    uuid = SmallUUIDField(default=uuid_default(), editable=False)
    timestamp = models.DateTimeField(default=timezone.now)
    date = models.DateField()
    statement_import = models.ForeignKey(StatementImport,
                                         related_name='lines',
                                         on_delete=models.CASCADE)
    amount = models.DecimalField(max_digits=13, decimal_places=2)
    description = models.TextField(default='', blank=True)
    type = models.CharField(max_length=50, default='')
    # TODO: Add constraint to ensure transaction amount = statement line amount
    # TODO: Add constraint to ensure one statement line per transaction
    transaction = models.ForeignKey(
        Transaction,
        default=None,
        blank=True,
        null=True,
        help_text='Reconcile this statement line to this transaction',
        on_delete=models.SET_NULL)
    source_data = JSONField(
        default={}, help_text='Original data received from the data source.')

    objects = StatementLineManager()

    def natural_key(self):
        return (self.uuid, )

    @property
    def is_reconciled(self):
        """Has this statement line been reconciled?

        Determined as ``True`` if :attr:`transaction` has been set.

        Returns:
            bool: ``True`` if reconciled, ``False`` if not.
        """
        return bool(self.transaction)

    @db_transaction.atomic()
    def create_transaction(self, to_account):
        """Create a transaction for this statement amount and account, into to_account

        This will also set this StatementLine's ``transaction`` attribute to the newly
        created transaction.

        Args:
            to_account (Account): The account the transaction is into / out of.

        Returns:
            Transaction: The newly created (and committed) transaction.

        """
        from_account = self.statement_import.bank_account

        transaction = Transaction.objects.create()
        Leg.objects.create(transaction=transaction,
                           account=from_account,
                           amount=+(self.amount * -1))
        Leg.objects.create(transaction=transaction,
                           account=to_account,
                           amount=-(self.amount * -1))

        transaction.date = self.date
        transaction.save()

        self.transaction = transaction
        self.save()
        return transaction
Exemple #20
0
            return None
        return super(DateTimeAwareJSONField,
                     self).to_python(decode_datetime_objects(value))

    def get_prep_lookup(self, lookup_type, value):
        if lookup_type in ('has_key', 'has_keys', 'has_any_keys'):
            return value
        if isinstance(value, (dict, list)):
            return Json(value,
                        dumps=partial(json.dumps,
                                      cls=DateTimeAwareJSONEncoder))
        return super(JSONField, self).get_prep_lookup(lookup_type, value)

    def validate(self, value, model_instance):
        super(JSONField, self).validate(value, model_instance)
        try:
            json.dumps(value, cls=DateTimeAwareJSONEncoder)
        except TypeError:
            raise ValidationError(
                self.error_messages['invalid'],
                code='invalid',
                params={'value': value},
            )


JSONField.register_lookup(lookups.DataContains)
JSONField.register_lookup(lookups.ContainedBy)
JSONField.register_lookup(lookups.HasKey)
JSONField.register_lookup(lookups.HasKeys)
JSONField.register_lookup(lookups.HasAnyKeys)
Exemple #21
0
class View(NameAndDescription, CreateModifyFields):
    """
    Class to represent different views of the table attached to a workflow.
    It only contains (aside from the basic fields) a formula to filter
    rows, and a subset of the columns in the workflow.

    @DynamicAttrs
    """

    workflow = models.ForeignKey(
        Workflow,
        db_index=True,
        null=False,
        blank=False,
        on_delete=models.CASCADE,
        related_name='views')

    # Set of columns for the personalised action IN (subset of the matrix
    # columns
    columns = models.ManyToManyField(
        Column,
        verbose_name=_("Subset of columns to show"),
        related_name='views'
    )

    # Formula to select a subset of rows for action IN
    formula = JSONField(
        verbose_name=_("Subset of rows to show"),
        default=dict,
        blank=True,
        null=True,
        help_text=_('Preselect rows satisfying this condition'))

    # Number of rows allowed by the formula.
    nrows = None

    def __str__(self):
        return self.name

    @property
    def num_columns(self):
        """Number of columns considered by this view.

        :return: Number of elements in the columns relation
        """
        return self.columns.count()

    @property
    def num_rows(self):
        """Number of rows considered by this view.

        :return: Number of rows resulting from using the formula
        """
        if not self.nrows:
            self.nrows = sql.get_num_rows(
                self.workflow.get_data_frame_table_name(),
                self.formula
            )

        return self.nrows

    @property
    def column_names(self):
        """List of column names.

        :return: List of column names
        """
        return [column.name for column in self.columns.all()]

    def log(self, user, operation_type: str, **kwargs):
        """Log the operation with the object."""
        payload = {
            'id': self.id,
            'name': self.name,
            'columns': [col.name for col in self.columns.all()],
            'formula': formula.evaluate(
                self.formula,
                formula.EVAL_TXT),
            'nrows': self.nrows}

        payload.update(kwargs)
        return Log.objects.register(
            user,
            operation_type,
            self.workflow,
            payload)

    class Meta:
        """Define uniqueness with name in workflow and order by name."""

        unique_together = ('name', 'workflow')
        ordering = ['name', ]
Exemple #22
0
class NormaJuridica(CountPageMixin):
    FIELDFILE_NAME = ('texto_integral', )

    ESFERA_FEDERACAO_CHOICES = Choices(
        ('M', 'municipal', _('Municipal')),
        ('E', 'estadual', _('Estadual')),
        ('F', 'federal', _('Federal')),
    )

    metadata = JSONField(verbose_name=_('Metadados'),
                         blank=True,
                         null=True,
                         default=None,
                         encoder=DjangoJSONEncoder)

    texto_integral = PortalFileField(
        blank=True,
        null=True,
        upload_to=norma_upload_path,
        verbose_name=_('Texto Integral'),
        storage=OverwriteStorage(),
        validators=[restringe_tipos_de_arquivo_txt],
        max_length=512)

    tipo = models.ForeignKey(TipoNormaJuridica,
                             on_delete=models.PROTECT,
                             verbose_name=_('Tipo da Norma Jurídica'))
    materia = models.ForeignKey(MateriaLegislativa,
                                blank=True,
                                null=True,
                                on_delete=models.PROTECT,
                                verbose_name=_('Matéria'))
    numero = models.CharField(max_length=8, verbose_name=_('Número'))
    ano = models.PositiveSmallIntegerField(verbose_name=_('Ano'),
                                           choices=RANGE_ANOS)
    esfera_federacao = models.CharField(max_length=1,
                                        verbose_name=_('Esfera Federação'),
                                        choices=ESFERA_FEDERACAO_CHOICES)
    data = models.DateField(blank=False, null=True, verbose_name=_('Data'))
    data_publicacao = models.DateField(blank=True,
                                       null=True,
                                       verbose_name=_('Data de Publicação'))
    veiculo_publicacao = models.CharField(
        max_length=30, blank=True, verbose_name=_('Veículo de Publicação'))
    pagina_inicio_publicacao = models.PositiveIntegerField(
        blank=True, null=True, verbose_name=_('Pg. Início'))
    pagina_fim_publicacao = models.PositiveIntegerField(
        blank=True, null=True, verbose_name=_('Pg. Fim'))
    ementa = models.TextField(verbose_name=_('Ementa'))
    indexacao = models.TextField(blank=True, verbose_name=_('Indexação'))
    observacao = models.TextField(blank=True, verbose_name=_('Observação'))
    complemento = models.NullBooleanField(blank=True,
                                          verbose_name=_('Complementar ?'),
                                          choices=YES_NO_CHOICES)
    # XXX was a CharField (attention on migrate)
    assuntos = models.ManyToManyField(AssuntoNorma,
                                      blank=True,
                                      verbose_name=_('Assuntos'))
    data_vigencia = models.DateField(blank=True,
                                     null=True,
                                     verbose_name=_('Data Fim Vigência'))
    timestamp = models.DateTimeField(null=True)

    texto_articulado = GenericRelation(TextoArticulado,
                                       related_query_name='texto_articulado')

    data_ultima_atualizacao = models.DateTimeField(blank=True,
                                                   null=True,
                                                   auto_now=True,
                                                   verbose_name=_('Data'))

    autores = models.ManyToManyField(Autor,
                                     through='AutoriaNorma',
                                     through_fields=('norma', 'autor'),
                                     symmetrical=False)

    norma_de_destaque = models.BooleanField(
        verbose_name=_('Norma de Destaque ?'),
        choices=YES_NO_CHOICES,
        default=False)

    apelido = models.TextField(blank=True, verbose_name=_('Apelido'))

    user = models.ForeignKey(get_settings_auth_user_model(),
                             verbose_name=_('Usuário'),
                             on_delete=models.PROTECT,
                             null=True,
                             blank=True)
    ip = models.CharField(verbose_name=_('IP'),
                          max_length=30,
                          blank=True,
                          default='')

    _certidao = GenericRelation(CertidaoPublicacao,
                                related_query_name='normajuridica_cert')

    def diariooficial(self):
        if self.veiculo_publicacao:
            if not self.diariooficial_set.filter(
                    edicao=self.veiculo_publicacao).exists():
                from cmj.diarios.models import DiarioOficial

                d = DiarioOficial.objects.filter(
                    edicao=self.veiculo_publicacao).last()

                if d:
                    d.normas.add(self)

        return self.diariooficial_set.all()

    class Meta:
        verbose_name = _('Norma Jurídica')
        verbose_name_plural = _('Normas Jurídicas')
        ordering = ['-data', '-numero']

    def get_normas_relacionadas(self):
        principais = NormaRelacionada.objects.filter(
            norma_principal=self.id).order_by('norma_principal__data',
                                              'norma_relacionada__data')
        relacionadas = NormaRelacionada.objects.filter(
            norma_relacionada=self.id).order_by('norma_principal__data',
                                                'norma_relacionada__data')
        return (principais, relacionadas)

    def get_anexos_norma_juridica(self):
        anexos = AnexoNormaJuridica.objects.filter(norma=self.id)
        return anexos

    @property
    def is_signed(self):
        try:
            return self.metadata and self.metadata['signs'] and \
                self.metadata['signs']['texto_integral'] and \
                self.metadata['signs']['texto_integral']['signs']
        except:
            return False

    @property
    def certidao(self):
        return self._certidao.all().first()

    @property
    def __descr__(self):
        return self.ementa

    def __str__(self):
        return _('%(tipo)s nº %(numero)s de %(data)s') % {
            'tipo': self.tipo,
            'numero': self.numero,
            'data': defaultfilters.date(self.data, "d \d\e F \d\e Y")
        }

    @property
    def epigrafe(self):
        return _('%(tipo)s nº %(numero)s de %(data)s') % {
            'tipo': self.tipo,
            'numero': self.numero,
            'data': defaultfilters.date(self.data, "d \d\e F \d\e Y")
        }

    def delete(self, using=None, keep_parents=False):
        if self.texto_integral:
            self.texto_integral.delete()

        return models.Model.delete(self,
                                   using=using,
                                   keep_parents=keep_parents)

    def save(self,
             force_insert=False,
             force_update=False,
             using=None,
             update_fields=None):

        if not self.pk and self.texto_integral:
            texto_integral = self.texto_integral
            self.texto_integral = None
            models.Model.save(self,
                              force_insert=force_insert,
                              force_update=force_update,
                              using=using,
                              update_fields=update_fields)
            self.texto_integral = texto_integral

        return models.Model.save(self,
                                 force_insert=force_insert,
                                 force_update=force_update,
                                 using=using,
                                 update_fields=update_fields)
Exemple #23
0
class BaseTaggableObjectModel(LastVisitedMixin, IndexingUtilsMixin, AttachableObjectModel):
    """
    Represents the base for all cosinnus main models. Each inheriting model
    has a set of simple ``tags`` which are just strings. Additionally each
    model has a ``media_tag`` that refers to all general tags like a location,
    people and so on.
    """

    media_tag = models.OneToOneField(settings.COSINNUS_TAG_OBJECT_MODEL,
        blank=True, null=True, on_delete=models.SET_NULL)

    group = models.ForeignKey(settings.COSINNUS_GROUP_OBJECT_MODEL, verbose_name=_('Team'),
        related_name='%(app_label)s_%(class)s_set', on_delete=models.CASCADE)

    title = models.CharField(_('Title'), max_length=255)
    slug = models.SlugField(max_length=55, blank=True)  # human readable part is 50 chars

    creator = models.ForeignKey(settings.AUTH_USER_MODEL,
        verbose_name=_('Creator'),
        on_delete=models.CASCADE,
        null=True,
        related_name='%(app_label)s_%(class)s_set')
    created = models.DateTimeField(
        verbose_name=_('Created'),
        editable=False,
        auto_now_add=True)
    last_modified = models.DateTimeField(
        verbose_name=_('Last modified'),
        editable=False,
        auto_now=True)
    
    last_action = models.DateTimeField(
        verbose_name='Last action date',
        auto_now_add=True,
        help_text='A datetime for when a significant action last happened for this object, '\
            'which users might be interested in. I.e. new comments, special edits, etc.')
    last_action_user = models.ForeignKey(settings.AUTH_USER_MODEL,
        verbose_name='Last action user',
        on_delete=models.SET_NULL,
        null=True,
        related_name='+',
        help_text='The user which caused the last significant action to update the `last_action` datetime.')

    settings = JSONField(default=dict, blank=True, null=True)

    class Meta(object):
        abstract = True
        unique_together = (('group', 'slug'),)

    def __str__(self):
        return self.title

    def __repr__(self):
        return "<tagged object {0} {1} (ID: {2})>".format(
            self.__class__.__name__, self.title, self.pk)

    def save(self, *args, **kwargs):
        created = bool(self.pk) == False
        unique_aware_slugify(self, 'title', 'slug', group=self.group)
        self.title = clean_single_line_text(self.title)
        if hasattr(self, '_media_tag_cache'):
            del self._media_tag_cache
        # set last action timestamp
        if not self.last_action:
            self.last_action = self.created
        if not self.last_action_user:
            self.last_action_user = self.creator
            
        super(BaseTaggableObjectModel, self).save(*args, **kwargs)
        
        if not getattr(self, 'media_tag', None):
            self.media_tag = get_tag_object_model().objects.create()
            self.save()
        if created:
            pass
    
    def on_save_added_tagged_persons(self, set_users):
        """ Called by the taggable form whenever this object is saved and -new- persons
            have been added as tagged! 
            This can be overridden in specific TaggableObjects for a more specific notification email message.
            Just add extra={'mail_template':'<your_template>', 'subject_template':'<your_template>'} """
        # exclude creator from audience always
        set_users -= set([self.creator])
        cosinnus_notifications.user_tagged_in_object.send(sender=self, user=self.creator, obj=self, audience=list(set_users))
            

    def media_tag_object(self):
        key = '_media_tag_cache'
        if not hasattr(self, key):
            if self.media_tag_id is None:
                setattr(self, key, self.group.media_tag)
            else:
                setattr(self, key, self.media_tag)
        return getattr(self, key)
    
    @property
    def sort_key(self):
        """ The main property on which this object model is sorted """
        if not self.created:
            return now()
        return self.created
    
    def grant_extra_read_permissions(self, user):
        """ An overridable check for whether this object grants certain users read permissions
            even though by general rules that user couldn't read the object.
            
            @param user: The user to check for extra permissions for """
        return False
    
    def grant_extra_write_permissions(self, user, **kwargs):
        """ An overridable check for whether this object grants certain users write permissions
            even though by general rules that user couldn't write the object.
            
            @param user: The user to check for extra permissions for """
        return False
    
    def get_tagged_persons_hash(self):
        """ Returns a hashable tuple of sorted list of ids of all tagged persons.
            Usuable to compare equality of attached files to objects. """
        return tuple(sorted(list(self.media_tag.persons.all().values_list('id', flat=True))))
    
    def get_delete_url(self):
        """ Similar to get_absolute_url, this returns the URL for this object's implemented delete view.
            Needs to be set by a specific implementation of BaseTaggableObjectModel """
        raise ImproperlyConfigured("The get_delete_url function must be implemented for model '%s'" % self.__class__)
    
    def get_cosinnus_app_name(self):
        return app_registry.get_name(self.__class__.__module__.split('.')[0])
    
    def render_additional_notification_content_rows(self):
        """ Used when rendering email notifications for an object. Any list of html strings returned here
            will be added, on row each, after the object_text of the notification object.
            Note: use mark_safe on the html strings if you do not wish them to be escaped!
            @return: An array of HTML strings or [] """
        content_snippets = []
        tag_object = self.media_tag
        if tag_object.location and tag_object.location_url:
            from cosinnus.models.group import CosinnusPortal
            location_html = render_to_string('cosinnus/html_mail/content_snippets/tagged_location.html', 
                                             {'tag_object': tag_object, 'domain': CosinnusPortal.get_current().get_domain()})
            content_snippets.append(mark_safe(location_html))
        return content_snippets
    
    def update_last_action(self, last_action_dt, last_action_user=None, save=True):
        """ Sets the `last_action` timestamp which is used for sorting items for
            timely relevance to show the users in their timelines or similar. """
        self.last_action = last_action_dt
        if last_action_user:
            self.last_action_user = last_action_user
        if save:
            self.save()
Exemple #24
0
class Payment(models.Model):
    """
    Model which store information about user payments

    Can link to tx_hash or Charge object, depending on what type of payment user choose
    """

    TRANSFER_STATES_DEFAULT = ('WAITING_FOR_TRANSFER', 'DONE', 'ERROR',
                               'RETURNED', 'QUEUED', 'PENDING')
    ADAPTED_TRANSFER_STATES_DEFAULT = ('WAITING_FOR_VALIDATION', 'SUCCESS',
                                       'FAIL', 'RETURN', 'WAITING_FOR_RELAY',
                                       'PENDING')
    COLLECTION_STATES_DEFAULT = ('NOT_COLLECTED', 'COLLECTED', 'ERROR')
    TRANSFER_STATES = list(
        zip(TRANSFER_STATES_DEFAULT, TRANSFER_STATES_DEFAULT))
    COLLECTION_STATES = list(
        zip(COLLECTION_STATES_DEFAULT, COLLECTION_STATES_DEFAULT))

    exchange_request = models.ForeignKey(ExchangeRequest,
                                         on_delete=models.CASCADE,
                                         null=True)
    charge = models.ForeignKey('quantum.Charge',
                               on_delete=models.CASCADE,
                               null=True)
    tx_hash = models.CharField(max_length=100, null=True, default='')
    currency = models.CharField(max_length=50, null=True, default='')
    from_address = models.CharField(max_length=100, null=True, default='')
    original_amount = models.DecimalField(max_digits=MAX_DIGITS,
                                          decimal_places=0)
    rate = models.DecimalField(max_digits=512, decimal_places=0)
    sent_amount = models.DecimalField(max_digits=MAX_DIGITS, decimal_places=0)
    created_date = models.DateTimeField(auto_now_add=True)
    transfer_state = FSMField(default=TRANSFER_STATES_DEFAULT[0],
                              choices=TRANSFER_STATES)
    collection_state = FSMField(default=COLLECTION_STATES_DEFAULT[0],
                                choices=COLLECTION_STATES)
    collection_tx_hash = models.CharField(max_length=100,
                                          null=True,
                                          default='')
    returned_tx_hash = models.CharField(max_length=100, null=True, default='')
    transfer_state_history = JSONField(
        default=generate_transfer_state_history_default)

    @property
    def adapted_state(self):
        """ Temporary addon to adjust statuses for /payments/get-status/ endpoint """
        return dict(
            zip(self.TRANSFER_STATES_DEFAULT,
                self.ADAPTED_TRANSFER_STATES_DEFAULT))[self.transfer_state]

    # States change
    @transition(field=transfer_state,
                source=['WAITING_FOR_TRANSFER', 'ERROR', 'PENDING'],
                target='DONE')
    def state_transfer_done(self):
        pass

    @transition(field=transfer_state, source='*', target='ERROR')
    def state_transfer_error(self):
        print('Transfer not completed, reverting payment', flush=True)

    @transition(field=transfer_state, source='*', target='RETURNED')
    def state_transfer_returned(self):
        pass

    @transition(field=transfer_state, source='*', target='QUEUED')
    def state_transfer_queued(self):
        pass

    @transition(field=transfer_state,
                source=['QUEUED', 'WAITING_FOR_TRANSFER'],
                target='PENDING')
    def state_transfer_pending(self):
        pass

    @transition(field=collection_state,
                source=['NOT_COLLECTED', 'ERROR'],
                target='COLLECTED')
    def state_collect_duc(self):
        pass

    @transition(field=collection_state, source='*', target='ERROR')
    def state_error_collect_duc(self):
        pass
Exemple #25
0
class Return(models.Model):
    """
    A number of requirements relating to a Licence condition recorded during
    the Licence period.
    """
    RETURN_PROCESSING_STATUS_DUE = 'due'
    RETURN_PROCESSING_STATUS_OVERDUE = 'overdue'
    RETURN_PROCESSING_STATUS_DRAFT = 'draft'
    RETURN_PROCESSING_STATUS_FUTURE = 'future'
    RETURN_PROCESSING_STATUS_WITH_CURATOR = 'with_curator'
    RETURN_PROCESSING_STATUS_ACCEPTED = 'accepted'
    RETURN_PROCESSING_STATUS_PAYMENT = 'payment'
    PROCESSING_STATUS_CHOICES = ((RETURN_PROCESSING_STATUS_DUE, 'Due'),
                                 (RETURN_PROCESSING_STATUS_OVERDUE, 'Overdue'),
                                 (RETURN_PROCESSING_STATUS_DRAFT,
                                  'Draft'), (RETURN_PROCESSING_STATUS_FUTURE,
                                             'Future'),
                                 (RETURN_PROCESSING_STATUS_WITH_CURATOR,
                                  'With Curator'),
                                 (RETURN_PROCESSING_STATUS_ACCEPTED,
                                  'Accepted'),
                                 (RETURN_PROCESSING_STATUS_PAYMENT,
                                  'Awaiting Payment'))

    RETURN_CUSTOMER_STATUS_DUE = 'due'
    RETURN_CUSTOMER_STATUS_OVERDUE = 'overdue'
    RETURN_CUSTOMER_STATUS_DRAFT = 'draft'
    RETURN_CUSTOMER_STATUS_FUTURE = 'future'
    RETURN_CUSTOMER_STATUS_UNDER_REVIEW = 'under_review'
    RETURN_CUSTOMER_STATUS_ACCEPTED = 'accepted'
    RETURN_CUSTOMER_STATUS_DISCARD = 'discard'
    RETURN_CUSTOMER_STATUS_PAYMENT = 'payment'

    # Displayable choices for customer status.
    CUSTOMER_DISPLAYABLE_STATE = {
        RETURN_CUSTOMER_STATUS_DUE: 'Due',
        RETURN_CUSTOMER_STATUS_OVERDUE: 'Overdue',
        RETURN_CUSTOMER_STATUS_DRAFT: 'Draft',
        RETURN_CUSTOMER_STATUS_FUTURE: 'Future Submission',
        RETURN_CUSTOMER_STATUS_UNDER_REVIEW: 'Under Review',
        RETURN_CUSTOMER_STATUS_ACCEPTED: 'Accepted',
        RETURN_CUSTOMER_STATUS_DISCARD: 'Discarded',
        RETURN_CUSTOMER_STATUS_PAYMENT: 'Awaiting Payment',
    }

    # status that allow a customer to edit a Return.
    CUSTOMER_EDITABLE_STATE = [
        RETURN_PROCESSING_STATUS_DRAFT,
        RETURN_PROCESSING_STATUS_PAYMENT,
    ]

    # List of statuses from above that allow a customer to view a Return.
    # (read-only)
    CUSTOMER_VIEWABLE_STATE = [
        RETURN_PROCESSING_STATUS_WITH_CURATOR,
        RETURN_PROCESSING_STATUS_ACCEPTED
    ]

    lodgement_number = models.CharField(max_length=9, blank=True, default='')
    application = models.ForeignKey(Application,
                                    related_name='returns_application')
    licence = models.ForeignKey('wildlifecompliance.WildlifeLicence',
                                related_name='returns_licence')
    due_date = models.DateField()
    processing_status = models.CharField(
        choices=PROCESSING_STATUS_CHOICES,
        max_length=20,
        default=RETURN_PROCESSING_STATUS_FUTURE)
    assigned_to = models.ForeignKey(EmailUser,
                                    related_name='returns_curator',
                                    null=True,
                                    blank=True)
    condition = models.ForeignKey(ApplicationCondition,
                                  blank=True,
                                  null=True,
                                  related_name='returns_condition',
                                  on_delete=models.SET_NULL)
    lodgement_date = models.DateTimeField(blank=True, null=True)
    submitter = models.ForeignKey(EmailUser,
                                  blank=True,
                                  null=True,
                                  related_name='returns_submitter')
    reminder_sent = models.BooleanField(default=False)
    post_reminder_sent = models.BooleanField(default=False)
    return_type = models.ForeignKey(ReturnType, null=True)
    nil_return = models.BooleanField(default=False)
    comments = models.TextField(blank=True, null=True)
    return_fee = models.DecimalField(max_digits=8,
                                     decimal_places=2,
                                     default='0')
    property_cache = JSONField(null=True, blank=True, default={})

    class Meta:
        app_label = 'wildlifecompliance'

    def __str__(self):
        return self.lodgement_number

    def save(self, *args, **kwargs):
        self.update_property_cache(False)
        super(Return, self).save(*args, **kwargs)
        '''
        Append 'R' to Return id to generate Return lodgement number.
        '''
        if self.lodgement_number == '':
            new_lodgement_id = 'R{0:06d}'.format(self.pk)
            self.lodgement_number = new_lodgement_id
            self.save()

    def get_property_cache(self):
        '''
        Get properties which were previously resolved.
        '''
        if len(self.property_cache) == 0:
            self.update_property_cache()

        return self.property_cache

    def update_property_cache(self, save=True):
        '''
        Refresh cached properties with updated properties.
        '''
        # self.property_cache['payment_status'] = self.payment_status

        if save is True:
            self.save()

        return self.property_cache

    def get_property_cache_key(self, key):
        '''
        Get properties which were previously resolved with key.
        '''
        try:

            self.property_cache[key]

        except KeyError:
            self.update_property_cache()

        return self.property_cache

    @property
    def activity(self):
        return self.application.activity

    @property
    def title(self):
        return self.application.title

    @property
    def holder(self):
        return self.application.applicant

    @property
    def resources(self):
        return self.return_type.data_descriptor.get('resources', [])

    @property
    def format(self):
        return self.return_type.data_format

    @property
    def template(self):
        """
        Return data spreadsheet template for uploading information.
        :return: spreadsheet template format.
        """
        template = self.return_type.data_template
        return self.return_type.data_template.url if template else None

    @property
    def has_question(self):
        """
        Property defining if the Return is Question based.
        :return: Boolean
        """
        return True if self.format == ReturnType.FORMAT_QUESTION else False

    @property
    def has_data(self):
        """
        Property defining if the Return is Data based.
        :return: Boolean
        """
        return True if self.format == ReturnType.FORMAT_DATA else False

    @property
    def has_sheet(self):
        """
        Property defining if the Return is Running Sheet based.
        :return: Boolean
        """
        return True if self.format == ReturnType.FORMAT_SHEET else False

    @property
    def has_species_list(self):
        """
        Boolean Property defining if the Return has an associated species list.
        """
        return False if self.return_type.with_no_species else True

    @property
    def customer_status(self):
        """
        Property defining external status in relation to processing status.
        :return: External Status.
        """
        DUE = self.RETURN_CUSTOMER_STATUS_DUE
        OVERDUE = self.RETURN_CUSTOMER_STATUS_OVERDUE
        DRAFT = self.RETURN_CUSTOMER_STATUS_DRAFT
        FUTURE = self.RETURN_CUSTOMER_STATUS_FUTURE
        UNDER_REVIEW = self.RETURN_CUSTOMER_STATUS_UNDER_REVIEW
        ACCEPTED = self.RETURN_CUSTOMER_STATUS_ACCEPTED

        workflow_mapper = {
            self.RETURN_PROCESSING_STATUS_DUE: DUE,
            self.RETURN_PROCESSING_STATUS_OVERDUE: OVERDUE,
            self.RETURN_PROCESSING_STATUS_DRAFT: DRAFT,
            self.RETURN_PROCESSING_STATUS_FUTURE: DRAFT,
            self.RETURN_PROCESSING_STATUS_WITH_CURATOR: UNDER_REVIEW,
            self.RETURN_PROCESSING_STATUS_ACCEPTED: ACCEPTED,
            self.RETURN_PROCESSING_STATUS_PAYMENT: UNDER_REVIEW
        }

        return workflow_mapper.get(self.processing_status, FUTURE)

    @property
    def payment_status(self):
        """
        Property defining fee status for this Return.
        :return:
        """
        if not self.return_type.fee_required:
            return ReturnInvoice.PAYMENT_STATUS_NOT_REQUIRED
        else:
            if self.invoices.count() == 0:
                return ReturnInvoice.PAYMENT_STATUS_UNPAID
            else:
                try:
                    latest_invoice = Invoice.objects.get(
                        reference=self.invoices.latest('id').invoice_reference)
                except ReturnInvoice.DoesNotExist:
                    return ReturnInvoice.PAYMENT_STATUS_UNPAID
                return latest_invoice.payment_status

    @property
    def return_fee_paid(self):
        return self.payment_status in [
            ReturnInvoice.PAYMENT_STATUS_NOT_REQUIRED,
            ReturnInvoice.PAYMENT_STATUS_PAID,
            ReturnInvoice.PAYMENT_STATUS_OVERPAID,
        ]

    @property
    def has_payment(self):
        """
        Property defining if payment is required for this Return.
        :return:
        """
        has_payment = False
        if self.get_latest_invoice():
            has_payment = True

        return has_payment

    @property
    def total_paid_amount(self):
        '''
        Property defining the total fees already paid for this licence Return.
        '''
        amount = 0
        if self.invoices.count() > 0:
            for invoice in self.invoices.all():
                detail = Invoice.objects.get(
                    reference=invoice.invoice_reference)
                amount += detail.payment_amount
                amount -= detail.refund_amount

        return amount

    @property
    def activity_curators(self):
        '''
        QuerySet of authorised licence activity curators for this return.
        '''
        groups = self.get_permission_groups('return_curator').values_list(
            'id', flat=True)

        return EmailUser.objects.filter(groups__id__in=groups).distinct()

    @transaction.atomic
    def set_submitted(self, request):
        '''
        TODO:AYN This is redundant. ReturnService.
        '''
        try:
            submit_status = [
                Return.RETURN_PROCESSING_STATUS_FUTURE,
                Return.RETURN_PROCESSING_STATUS_DUE,
                Return.RETURN_PROCESSING_STATUS_OVERDUE,
                Return.RETURN_PROCESSING_STATUS_DRAFT,
            ]
            CURATOR = Return.RETURN_PROCESSING_STATUS_WITH_CURATOR
            if self.processing_status in submit_status:
                self.processing_status = CURATOR
                self.submitter = request.user
                self.save()

            # code for amendment returns is still to be added, so
            # lodgement_date is set outside if statement
            self.lodgement_date = timezone.now()
            self.save()
            # this below code needs to be reviewed
            # self.save(version_comment='Return submitted:{}'.format(self.id))
            # self.application.save(
            #   version_comment='Return submitted:{}'.format(self.id))
            self.log_user_action(
                ReturnUserAction.ACTION_SUBMIT_REQUEST.format(self), request)
            send_external_submit_email_notification(request, self)
            # send_submit_email_notification(request,self)
        except BaseException:
            raise

    def set_return_species(self):
        '''
        Set the species available for this return.
        '''
        try:
            return_table = []
            specie_names = []

            if self.return_type.with_application_species:
                specie_names = self.get_application_return_species()

            elif self.return_type.with_regulated_species:
                specie_names = self.get_regulated_return_species()

            for name in specie_names:
                return_table.append(ReturnTable(name=name,
                                                ret_id=str(self.id)))

            if return_table:
                ReturnTable.objects.bulk_create(return_table)

        except BaseException as e:
            logger.error('set_return_speces() ID: {0} - {1}'.format(
                self.id, e))

    def get_application_return_species(self):
        '''
        Set the species available for this return.
        '''
        species = []

        return species

    def get_regulated_return_species(self):
        '''
        Set the species available for this return.
        '''
        species = []

        return species

    def set_future_return_species(self):
        '''
        Set the species available for this return.
        '''
        SPECIES = ApplicationFormDataRecord.COMPONENT_TYPE_SELECT_SPECIES
        STATUS = [
            # Return.RETURN_PROCESSING_STATUS_FUTURE
            'NO SPECIES ALLOWED'
        ]

        if self.processing_status not in STATUS:
            '''
            Species are only set for FUTURE processing status.
            '''
            return

        species_qs = ApplicationFormDataRecord.objects.values(
            'value', ).filter(
                licence_activity_id=self.condition.licence_activity_id,
                licence_purpose_id=self.condition.licence_purpose_id,
                application_id=self.condition.application_id,
                component_type=SPECIES,
            )
        return_table = []
        specie_ids = []
        for specie_id in species_qs:
            specie_ids += specie_id['value']

        for id_name in specie_ids:
            return_table.append(ReturnTable(name=id_name, ret_id=str(self.id)))

        if return_table:
            ReturnTable.objects.bulk_create(return_table)

    def set_processing_status(self, status):
        '''
        Set the processing status for this Return.
        '''
        self.processing_status = status
        self.save()

    def set_return_fee(self, fee):
        '''
        Set the submission fee for this return.
        '''
        self.return_fee = fee
        self.save()

    def get_customer_status(self):
        '''
        Get displayable customer status.
        '''
        return self.CUSTOMER_DISPLAYABLE_STATE.get(self.customer_status)

    def get_latest_invoice(self):
        '''
        Get latest invoice for this Return.
        '''
        latest_invoice = None
        if self.invoices.count() > 0:
            try:
                latest_invoice = Invoice.objects.get(
                    reference=self.invoices.latest('id').invoice_reference)
            except Invoice.DoesNotExist:
                return None

        return latest_invoice

    def get_permission_groups(self, codename):
        '''
        Get the queryset of ActivityPermissionGroups matching this return based
        on licence activity.
        '''
        from wildlifecompliance.components.applications.models import (
            ActivityPermissionGroup)
        selected_activity_ids = ApplicationCondition.objects.filter(
            id=self.condition_id,
            licence_activity__isnull=False).values_list('licence_activity__id',
                                                        flat=True)

        if not selected_activity_ids:
            return ActivityPermissionGroup.objects.none()

        return ActivityPermissionGroup.get_groups_for_activities(
            selected_activity_ids, codename)

    @transaction.atomic
    def accept(self, request):
        '''
        TODO:AYN This is redundant. ReturnService.
        '''
        try:
            self.processing_status = Return.RETURN_PROCESSING_STATUS_ACCEPTED
            self.save()
            self.log_user_action(
                ReturnUserAction.ACTION_ACCEPT_REQUEST.format(self), request)
            send_return_accept_email_notification(self, request)
        except BaseException:
            raise

    def save_return_table(self, table_name, table_rows, request):
        """
        Persist Return Table of data to database.
        :param table_name:
        :param table_rows:
        :param request:
        :return:
        """
        try:
            # wrap atomic context here to allow natural handling of a Record
            # Modified concurrent error. (optimistic lock)
            # get the Return Table record and save immediately to check if
            # it has been concurrently modified.
            return_table, created = ReturnTable.objects.get_or_create(
                name=table_name, ret=self)
            return_table.save()
            # delete any existing rows as they will all be recreated
            return_table.returnrow_set.all().delete()
            return_rows = [
                ReturnRow(return_table=return_table, data=row)
                for row in table_rows
            ]

            ReturnRow.objects.bulk_create(return_rows)

            # log transaction
            self.log_user_action(
                ReturnUserAction.ACTION_SAVE_REQUEST.format(self), request)

        except RecordModifiedError:
            raise IntegrityError(
                'A concurrent save occurred please refresh page details.')
        except BaseException:
            raise

    def log_user_action(self, action, request):
        return ReturnUserAction.log_action(self, action, request.user)
Exemple #26
0
class DocumentoAdministrativo(models.Model):
    tipo = models.ForeignKey(TipoDocumentoAdministrativo,
                             on_delete=models.PROTECT,
                             verbose_name=_('Tipo Documento'))
    numero = models.PositiveIntegerField(verbose_name=_('Número'))
    ano = models.PositiveSmallIntegerField(verbose_name=_('Ano'),
                                           choices=RANGE_ANOS)
    protocolo = models.ForeignKey(Protocolo,
                                  blank=True,
                                  null=True,
                                  on_delete=models.PROTECT,
                                  verbose_name=_('Protocolo'))

    materia = models.ForeignKey(MateriaLegislativa,
                                blank=True,
                                null=True,
                                on_delete=models.PROTECT,
                                related_name='documentoadministrativo_set',
                                verbose_name=_('Matéria Vinculada'))

    data = models.DateField(verbose_name=_('Data'))

    temp_migracao_doc_acessorio = models.PositiveIntegerField(
        blank=True,
        null=True,
        verbose_name=_(
            'Field temporário para migração dos docs acessórios da procuradoria'
        ))

    temp_migracao_sislegis = models.PositiveIntegerField(
        blank=True,
        null=True,
        verbose_name=_(
            'Field temporário para migração dos docs adms do sislegis'))

    old_path = models.TextField(
        verbose_name=_('Path antigo para Sislegis - Publicações'),
        blank=True,
        null=True,
        default=None)

    old_json = JSONField(verbose_name=_('Json from origin import'),
                         blank=True,
                         null=True,
                         default=None,
                         encoder=DjangoJSONEncoder)

    epigrafe = models.CharField(max_length=1000,
                                blank=True,
                                verbose_name=_('Epigrafe / Título'))
    interessado = models.CharField(max_length=1000,
                                   blank=True,
                                   verbose_name=_('Interessado'))
    autor = models.ForeignKey(Autor,
                              blank=True,
                              null=True,
                              on_delete=models.PROTECT)

    dias_prazo = models.PositiveIntegerField(blank=True,
                                             null=True,
                                             verbose_name=_('Dias Prazo'))
    data_fim_prazo = models.DateField(blank=True,
                                      null=True,
                                      verbose_name=_('Data Fim Prazo'))

    data_vencimento = models.DateField(blank=True,
                                       null=True,
                                       verbose_name=_('Data de Vencimento'))

    tramitacao = models.BooleanField(verbose_name=_('Em Tramitação?'),
                                     choices=YES_NO_CHOICES,
                                     default=False)
    assunto = models.TextField(blank=True,
                               null=True,
                               verbose_name=_('Assunto'))
    numero_externo = models.PositiveIntegerField(
        blank=True, null=True, verbose_name=_('Número Externo'))
    observacao = models.TextField(blank=True, verbose_name=_('Resumo'))
    texto_integral = PortalFileField(blank=True,
                                     null=True,
                                     storage=OverwriteStorage(),
                                     upload_to=texto_upload_path,
                                     verbose_name=_('Texto Integral'))

    anexados = models.ManyToManyField('self',
                                      blank=True,
                                      through='Anexado',
                                      symmetrical=False,
                                      related_name='anexo_de',
                                      through_fields=('documento_principal',
                                                      'documento_anexado'))

    workspace = models.ForeignKey(AreaTrabalho,
                                  verbose_name=_('Área de Trabalho'),
                                  related_name='documentoadministrativo_set',
                                  blank=True,
                                  null=True,
                                  on_delete=PROTECT)

    data_ultima_atualizacao = models.DateTimeField(blank=True,
                                                   null=True,
                                                   auto_now=True,
                                                   verbose_name=_('Data'))

    _certidao = GenericRelation(
        CertidaoPublicacao, related_query_name='documentoadministrativo_cert')

    class Meta:
        verbose_name = _('Documento Administrativo')
        verbose_name_plural = _('Documentos Administrativos')

    @property
    def certidao(self):
        return self._certidao.all().first()

    @property
    def __descr__(self):
        return self.assunto

    def __str__(self):
        if self.epigrafe:
            return '%s' % self.epigrafe

        return _(
            '%(sigla)s - %(tipo)s nº %(numero)s/%(ano)s %(interessado)s') % {
                'sigla': self.tipo.sigla,
                'tipo': self.tipo,
                'numero': self.numero,
                'ano': self.ano,
                'interessado':
                ('(%s)' % self.interessado) if self.interessado else ''
            }

    def delete(self, using=None, keep_parents=False):
        texto_integral = self.texto_integral

        result = models.Model.delete(self,
                                     using=using,
                                     keep_parents=keep_parents)

        if texto_integral:
            texto_integral.delete(save=False)

        return result

    def save(self,
             force_insert=False,
             force_update=False,
             using=None,
             update_fields=None):

        if not self.pk and self.texto_integral:
            texto_integral = self.texto_integral
            self.texto_integral = None
            models.Model.save(self,
                              force_insert=force_insert,
                              force_update=force_update,
                              using=using,
                              update_fields=update_fields)
            self.texto_integral = texto_integral

        return models.Model.save(self,
                                 force_insert=force_insert,
                                 force_update=force_update,
                                 using=using,
                                 update_fields=update_fields)
Exemple #27
0
class ReturnType(models.Model):
    """
    A definition to identify the format used to facilitate Return.
    """
    FORMAT_SHEET = 'sheet'
    FORMAT_QUESTION = 'question'
    FORMAT_DATA = 'data'
    FORMAT_CHOICES = ((FORMAT_SHEET, 'Sheet'), (FORMAT_QUESTION, 'Question'),
                      (FORMAT_DATA, 'Data'))
    # Species list for this Return Type can be regulated, application-based or
    # none.
    SPECIES_LIST_REGULATED = 'regulated'
    SPECIES_LIST_APPLICATION = 'application'
    SPECIES_LIST_NONE = 'none'
    SPECIES_LIST_CHOICES = ((SPECIES_LIST_REGULATED, 'Regulated Species List'),
                            (SPECIES_LIST_APPLICATION,
                             'Application Species List'), (SPECIES_LIST_NONE,
                                                           'No Species List'))
    name = models.CharField(null=True, blank=True, max_length=100)
    description = models.TextField(null=True, blank=True, max_length=256)
    data_descriptor = JSONField()
    data_format = models.CharField('Data format',
                                   max_length=30,
                                   choices=FORMAT_CHOICES,
                                   default=FORMAT_DATA)
    # data_template is only used by ReturnData Format for upload.
    data_template = models.FileField(upload_to=template_directory_path,
                                     null=True,
                                     blank=True)
    fee_required = models.BooleanField(default=False)
    # fee_amount is a base amount required for the Return Type.
    fee_amount = models.DecimalField(max_digits=8,
                                     decimal_places=2,
                                     default='0')
    # fee_name is an optional field for fee and can be used to correspond to
    # JSON property.
    fee_name = models.CharField(null=True, blank=True, max_length=50)
    oracle_account_code = models.CharField(max_length=100, default='')
    replaced_by = models.ForeignKey('self',
                                    on_delete=models.PROTECT,
                                    blank=True,
                                    null=True)
    version = models.SmallIntegerField(default=1, blank=False, null=False)
    # species_list is the type of species associated with this Return Type.
    species_list = models.CharField('Species List',
                                    max_length=30,
                                    choices=SPECIES_LIST_CHOICES,
                                    default=SPECIES_LIST_NONE)

    def __str__(self):
        return '{0} - v{1}'.format(self.name, self.version)

    class Meta:
        app_label = 'wildlifecompliance'
        unique_together = ('name', 'version')

    @property
    def resources(self):
        return self.data_descriptor.get('resources', [])

    @property
    def with_application_species(self):
        '''
        Boolean property to indicate that this return type is for a list of
        species selected when applying for licence.
        '''
        with_application_species = False

        if self.species_list == self.SPECIES_LIST_APPLICATION:
            with_application_species = True

        return with_application_species

    @property
    def with_regulated_species(self):
        '''
        Boolean property to indicate that this return type is for a list of
        regulated species applied from the questions answered when applying.
        '''
        with_regulated_species = False

        if self.species_list == self.SPECIES_LIST_REGULATED:
            with_regulated_species = True

        return with_regulated_species

    @property
    def with_no_species(self):
        '''
        Boolean property to indicate that this return type has no species
        associated.
        '''
        no_species = False

        if self.species_list == self.SPECIES_LIST_NONE:
            no_species = True

        return no_species

    def get_resource_by_name(self, name):
        for resource in self.resources:
            if resource.get('name') == name:
                return resource
        return None

    def get_schema_by_name(self, name):
        resource = self.get_resource_by_name(name)
        return resource.get('schema', {}) if resource else None
Exemple #28
0
class SessaoPlenaria(models.Model):
    FIELDFILE_NAME = ('upload_pauta', 'upload_ata', 'upload_anexo')

    metadata = JSONField(verbose_name=_('Metadados'),
                         blank=True,
                         null=True,
                         default=None,
                         encoder=DjangoJSONEncoder)

    cod_andamento_sessao = models.PositiveIntegerField(blank=True, null=True)

    painel_aberto = models.BooleanField(blank=True,
                                        default=False,
                                        verbose_name=_('Painel está aberto?'))
    tipo = models.ForeignKey(TipoSessaoPlenaria,
                             on_delete=models.PROTECT,
                             verbose_name=_('Tipo'))
    sessao_legislativa = models.ForeignKey(
        SessaoLegislativa,
        on_delete=models.CASCADE,
        verbose_name=_('Sessão Legislativa'))
    legislatura = models.ForeignKey(Legislatura,
                                    on_delete=models.PROTECT,
                                    verbose_name=_('Legislatura'))
    # XXX seems to be empty
    data_inicio = models.DateField(verbose_name=_('Abertura'))
    hora_inicio = models.CharField(max_length=5,
                                   verbose_name=_('Horário (hh:mm)'))
    hora_fim = models.CharField(max_length=5,
                                blank=True,
                                verbose_name=_('Horário (hh:mm)'))
    numero = models.PositiveIntegerField(verbose_name=_('Número'))
    data_fim = models.DateField(blank=True,
                                null=True,
                                verbose_name=_('Encerramento'))
    url_audio = models.URLField(
        max_length=150,
        blank=True,
        verbose_name=_('URL Arquivo Áudio (Formatos MP3 / AAC)'))
    url_video = models.URLField(
        max_length=150,
        blank=True,
        verbose_name=_('URL Arquivo Vídeo (Formatos MP4 / FLV / WebM)'))
    upload_pauta = PortalFileField(blank=True,
                                   null=True,
                                   upload_to=pauta_upload_path,
                                   verbose_name=_('Pauta da Sessão'),
                                   storage=OverwriteStorage(),
                                   validators=[restringe_tipos_de_arquivo_txt])
    upload_ata = PortalFileField(blank=True,
                                 null=True,
                                 upload_to=ata_upload_path,
                                 storage=OverwriteStorage(),
                                 verbose_name=_('Ata da Sessão'),
                                 validators=[restringe_tipos_de_arquivo_txt])
    upload_anexo = PortalFileField(blank=True,
                                   null=True,
                                   storage=OverwriteStorage(),
                                   upload_to=anexo_upload_path,
                                   verbose_name=_('Anexo da Sessão'))
    iniciada = models.NullBooleanField(blank=True,
                                       choices=YES_NO_CHOICES,
                                       verbose_name=_('Sessão iniciada?'),
                                       default=True)
    finalizada = models.NullBooleanField(blank=True,
                                         choices=YES_NO_CHOICES,
                                         verbose_name=_('Sessão finalizada?'),
                                         default=False)
    selo_votacao_adicionado = models.NullBooleanField(
        blank=True,
        choices=YES_NO_CHOICES,
        verbose_name=_('Selo de Votação Adicionado?'),
        default=False)
    interativa = models.NullBooleanField(blank=True,
                                         choices=YES_NO_CHOICES,
                                         verbose_name=_('Sessão interativa'))
    tema_solene = models.TextField(blank=True,
                                   max_length=500,
                                   verbose_name=_('Tema da Sessão Solene'))

    data_ultima_atualizacao = models.DateTimeField(blank=True,
                                                   null=True,
                                                   auto_now=True,
                                                   verbose_name=_('Data'))

    _certidao = GenericRelation(CertidaoPublicacao,
                                related_query_name='sessaoplenaria_cert')

    _diario = GenericRelation(VinculoDocDiarioOficial,
                              related_query_name='sessaoplenaria_diario')

    class Meta:
        verbose_name = _('Sessão Plenária')
        verbose_name_plural = _('Sessões Plenárias')

    @property
    def __descr__(self):
        return str(self)

    @property
    def certidao(self):
        return self._certidao.all().first()

    @property
    def diariooficial(self):
        try:
            return self._diario.all().first().diario
        except:
            return None

    def __str__(self):

        tnc = self.tipo.TIPO_NUMERACAO_CHOICES

        base = '{}ª {}'.format(self.numero, self.tipo.nome)

        if self.tipo.tipo_numeracao == tnc.quizenal:
            base += ' da {}ª Quinzena'.format(
                1 if self.data_inicio.day <= 15 else 2)

        if self.tipo.tipo_numeracao <= tnc.mensal:
            base += ' do mês de {}'.format(
                formats.date_format(self.data_inicio, 'F'))

        if self.tipo.tipo_numeracao <= tnc.anual:
            base += ' de {}'.format(self.data_inicio.year)

        if self.tipo.tipo_numeracao <= tnc.sessao_legislativa:
            base += ' da {}ª Sessão Legislativa'.format(
                self.sessao_legislativa.numero)

        if self.tipo.tipo_numeracao <= tnc.legislatura:
            base += ' da {}ª Legislatura'.format(self.legislatura.numero)

        return base
        """return _('%(numero)sª Sessão %(tipo_nome)s'
                 ' da %(sessao_legislativa_numero)sª Sessão Legislativa'
                 ' da %(legislatura_id)sª Legislatura') % {

            'numero': self.numero,
            'tipo_nome': self.tipo.nome,
            'sessao_legislativa_numero': self.sessao_legislativa.numero,
            # XXX check if it shouldn't be legislatura.numero
            'legislatura_id': self.legislatura.numero}
        """

    def str_short(self):

        tnc = self.tipo.TIPO_NUMERACAO_CHOICES

        base = '{}ª {}'.format(self.numero, self.tipo.nome)

        if self.tipo.tipo_numeracao == tnc.quizenal:
            base += ' da {}ª Quinzena'.format(
                1 if self.data_inicio.day <= 15 else 2)

        if self.tipo.tipo_numeracao <= tnc.mensal:
            base += ' do mês de {}'.format(
                formats.date_format(self.data_inicio, 'F'))

        if self.tipo.tipo_numeracao <= tnc.anual:
            base += ' de {}'.format(self.data_inicio.year)

        return base

    def str_title(self):
        return self.str_short()

    def str_subtitle(self):

        tnc = self.tipo.TIPO_NUMERACAO_CHOICES

        base = ''
        if self.tipo.tipo_numeracao <= tnc.sessao_legislativa:
            base = '{}ª Sessão Legislativa'.format(
                self.sessao_legislativa.numero)

        if self.tipo.tipo_numeracao <= tnc.legislatura:
            base += ' da {}ª Legislatura'.format(self.legislatura.numero)

        return base

    def delete(self, using=None, keep_parents=False):
        if self.upload_pauta:
            self.upload_pauta.delete()

        if self.upload_ata:
            self.upload_ata.delete()

        if self.upload_anexo:
            self.upload_anexo.delete()

        return models.Model.delete(self,
                                   using=using,
                                   keep_parents=keep_parents)

    def save(self,
             force_insert=False,
             force_update=False,
             using=None,
             update_fields=None):

        if not self.pk and (self.upload_pauta or self.upload_ata
                            or self.upload_anexo):
            upload_pauta = self.upload_pauta
            upload_ata = self.upload_ata
            upload_anexo = self.upload_anexo
            self.upload_pauta = None
            self.upload_ata = None
            self.upload_anexo = None
            models.Model.save(self,
                              force_insert=force_insert,
                              force_update=force_update,
                              using=using,
                              update_fields=update_fields)

            self.upload_pauta = upload_pauta
            self.upload_ata = upload_ata
            self.upload_anexo = upload_anexo

        return models.Model.save(self,
                                 force_insert=force_insert,
                                 force_update=force_update,
                                 using=using,
                                 update_fields=update_fields)

    @property
    def ano(self):
        return self.data_inicio.year
Exemple #29
0
class Gathering(TimeStampedModel, SoftDeletableModel, Utility):
    notes = GenericRelation(Note)
    id = models.BigAutoField(auto_created=True,
                             primary_key=True,
                             serialize=False,
                             verbose_name='ID')
    meet = models.ForeignKey('Meet',
                             on_delete=models.SET(0),
                             null=False,
                             blank=False)
    start = models.DateTimeField(null=False, blank=False)
    finish = models.DateTimeField(
        null=False,
        blank=False,
        help_text="Required for user to filter by time")
    attendings = models.ManyToManyField('persons.Attending',
                                        through='Attendance')
    display_name = models.CharField(max_length=50,
                                    blank=True,
                                    null=True,
                                    help_text="02/09/2020, etc")
    infos = JSONField(
        null=True,
        blank=True,
        default=dict,
        help_text=
        'Example: {"LG_location": "F207", "link": "https://..."}. Please keep {} here even no data'
    )
    occurrence = models.ForeignKey(Occurrence,
                                   blank=True,
                                   null=True,
                                   on_delete=models.SET_NULL)
    site_type = models.ForeignKey(
        ContentType,
        on_delete=models.SET(0),
        help_text='site: django_content_type id for table name')
    site_id = models.BigIntegerField()
    site = GenericForeignKey('site_type', 'site_id')

    # from itertools import groupby
    # from operator import attrgetter
    #
    # ordered_program_sessions = ProgramSession.objects.order_by('program_group', 'start_at')
    # program_sessions_grouped_by_program_groups = {
    #     k: list(v)
    #     for k, v in groupby(ordered_program_sessions, attrgetter('program_group'))
    # } #=> {<ProgramGroup: The Rock  >: [<ProgramSession: The Rock #1...>, <ProgramSession: The Rock #2...>]}

    def get_absolute_url(self):
        return reverse('gathering_detail', args=[str(self.id)])

    @property
    def gathering_label(self):
        return (self.meet.display_name or '') + ' ' + (self.display_name or '')

    class Meta:
        db_table = 'occasions_gatherings'
        ordering = ['meet', 'start']
        constraints = [
            models.UniqueConstraint(
                fields=['meet_id', 'site_type_id', 'site_id', 'start'],
                condition=models.Q(is_removed=False),
                name='uniq_meet_location_time')
        ]

    def __str__(self):
        return '%s %s %s %s' % (self.meet, self.start, self.display_name
                                or '', self.site or '')
    def get_prep_value(self, value):
        if value is not None:
            return Json(value, dumps=partial(json.dumps, cls=DjangoJSONEncoder))
        return value


    def get_prep_lookup(self, lookup_type, value):
        if lookup_type in ('has_key', 'has_keys', 'has_any_keys'):
            return value
        if isinstance(value, (dict, list)):
            return Json(value, dumps=partial(json.dumps, cls=DjangoJSONEncoder))
        return super(JSONField, self).get_prep_lookup(lookup_type, value)

    def validate(self, value, model_instance):
        super(JSONField, self).validate(value, model_instance)
        try:
            json.dumps(value, cls=DjangoJSONEncoder)
        except TypeError:
            raise exceptions.ValidationError(
                self.error_messages['invalid'],
                code='invalid',
                params={'value': value},
            )


JSONField.register_lookup(lookups.DataContains)
JSONField.register_lookup(lookups.ContainedBy)
JSONField.register_lookup(lookups.HasKey)
JSONField.register_lookup(lookups.HasKeys)
JSONField.register_lookup(lookups.HasAnyKeys)
Exemple #31
0
class Approval(RevisionedMixin):
    STATUS_CURRENT = 'current'
    STATUS_EXPIRED = 'expired'
    STATUS_CANCELLED = 'cancelled'
    STATUS_SURRENDERED = 'surrendered'
    STATUS_SUSPENDED = 'suspended'
    STATUS_CHOICES = ((STATUS_CURRENT, 'Current'), (STATUS_EXPIRED, 'Expired'),
                      (STATUS_CANCELLED, 'Cancelled'),
                      (STATUS_SURRENDERED, 'Surrendered'), (STATUS_SUSPENDED,
                                                            'Suspended'))
    lodgement_number = models.CharField(max_length=9, blank=True, default='')
    status = models.CharField(max_length=40,
                              choices=STATUS_CHOICES,
                              default=STATUS_CHOICES[0][0])
    # NB: licence_document not used for Apiary applications
    licence_document = models.ForeignKey(ApprovalDocument,
                                         blank=True,
                                         null=True,
                                         related_name='licence_document')
    cover_letter_document = models.ForeignKey(
        ApprovalDocument,
        blank=True,
        null=True,
        related_name='cover_letter_document')
    replaced_by = models.ForeignKey('self', blank=True, null=True)
    #current_proposal = models.ForeignKey(Proposal,related_name = '+')
    current_proposal = models.ForeignKey(Proposal, related_name='approvals')
    renewal_document = models.ForeignKey(ApprovalDocument,
                                         blank=True,
                                         null=True,
                                         related_name='renewal_document')
    # apiary_renewal_document = models.ForeignKey(RenewalDocument, blank=True, null=True, related_name='apiary_renewal_document')
    renewal_sent = models.BooleanField(default=False)
    issue_date = models.DateTimeField()
    original_issue_date = models.DateField(auto_now_add=True)
    start_date = models.DateField()
    expiry_date = models.DateField()
    surrender_details = JSONField(blank=True, null=True)
    suspension_details = JSONField(blank=True, null=True)
    applicant = models.ForeignKey(Organisation,
                                  on_delete=models.PROTECT,
                                  blank=True,
                                  null=True,
                                  related_name='disturbance_approvals')
    proxy_applicant = models.ForeignKey(
        EmailUser,
        on_delete=models.PROTECT,
        blank=True,
        null=True,
        related_name='disturbance_proxy_approvals')
    extracted_fields = JSONField(blank=True, null=True)
    cancellation_details = models.TextField(blank=True)
    cancellation_date = models.DateField(blank=True, null=True)
    set_to_cancel = models.BooleanField(default=False)
    set_to_suspend = models.BooleanField(default=False)
    set_to_surrender = models.BooleanField(default=False)
    reissued = models.BooleanField(default=False)
    apiary_approval = models.BooleanField(default=False)
    no_annual_rental_fee_until = models.DateField(blank=True, null=True)
    apiary_sites = models.ManyToManyField('ApiarySite',
                                          through=ApiarySiteOnApproval,
                                          related_name='approval_set')
    migrated = models.BooleanField(default=False)

    class Meta:
        app_label = 'disturbance'
        unique_together = ('lodgement_number', 'issue_date')

    def add_apiary_sites_to_proposal_apiary_for_renewal(self, proposal_apiary):
        for apiary_site in self.apiary_sites.all(
        ):  # Exclude just in case there is.
            relation = self.get_relation(apiary_site)
            ApiarySiteOnProposal.objects.create(
                apiary_site=apiary_site,
                proposal_apiary=proposal_apiary,
                wkb_geometry_draft=relation.wkb_geometry,
                site_category_draft=relation.site_category,
                for_renewal=True,
            )

    def get_relation(self, apiary_site):
        if isinstance(apiary_site, dict):
            apiary_site = ApiarySite.objects.get(id=apiary_site.get('id'))
        relation_obj = ApiarySiteOnApproval.objects.get(
            apiary_site=apiary_site, approval=self)
        return relation_obj

    def get_relations(self):
        # relation_objs = ApiarySiteOnApproval.objects.filter(apiary_site__in=self.apiary_sites.all(), approval=self)
        relation_objs = ApiarySiteOnApproval.objects.filter(
            apiary_site__in=self.apiary_sites.all(),
            approval=self).exclude(site_status=SITE_STATUS_TRANSFERRED)
        return relation_objs

    @property
    def get_current_apiary_sites(self):
        # relation_objs = ApiarySiteOnApproval.objects.filter(apiary_site__in=self.apiary_sites.all(), approval=self)
        relation_objs = ApiarySiteOnApproval.objects.filter(
            apiary_site__in=self.apiary_sites.all(),
            approval=self).filter(site_status=SITE_STATUS_CURRENT)
        return relation_objs

    @property
    def relevant_renewal_document(self):
        if self.apiary_approval:
            # return self.renewal_documents.filter(expiry_date=self.expiry_date).first() if self.renewal_documents.filter(expiry_date=self.expiry_date) else None
            temp = self.renewal_documents.last()
            all = self.renewal_documents.all()
            return self.renewal_documents.last() if self.renewal_documents.all(
            ) else None
        else:
            return self.renewal_document

        # return self.apiary_renewal_document if self.apiary_renewal_document else self.renewal_document

    @property
    def relevant_applicant_id(self):
        if self.applicant:
            #return self.org_applicant.organisation.id
            return self.applicant.id
        elif self.proxy_applicant:
            return self.proxy_applicant.id

    @property
    def relevant_applicant(self):
        if self.applicant:
            return self.applicant
        else:
            return self.proxy_applicant

    @property
    def relevant_applicant_email_user(self):
        if self.applicant:
            return self.applicant.delegates.all()[0]
        else:
            return self.proxy_applicant

    @property
    def relevant_applicant_email(self):
        if self.applicant and hasattr(
                self.applicant.organisation,
                'email') and self.applicant.organisation.email:
            return self.applicant.organisation.email
        elif self.proxy_applicant:
            return self.proxy_applicant.email
        else:
            return self.current_proposal.submitter.email

    @property
    def relevant_applicant_name(self):
        if self.applicant:
            return self.applicant.name
        elif self.proxy_applicant:
            return self.proxy_applicant.get_full_name()
        else:
            return self.current_proposal.submitter.get_full_name()

    @property
    def relevant_applicant_address(self):
        if self.applicant:
            return self.applicant.address
        elif self.proxy_applicant:
            #return self.proxy_applicant.addresses.all().first()
            return self.proxy_applicant.residential_address
        else:
            return self.current_proposal.submitter.residential_address

    @property
    def region(self):
        try:
            return self.current_proposal.region.name
        except:
            return ''

    @property
    def district(self):
        try:
            return self.current_proposal.district.name
        except:
            return ''

    @property
    def tenure(self):
        try:
            return self.current_proposal.tenure.name
        except:
            return ''

    @property
    def activity(self):
        return self.current_proposal.activity

    @property
    def title(self):
        return self.current_proposal.title

    @property
    def next_id(self):
        #ids = map(int,[(i.lodgement_number.split('A')[1]) for i in Approval.objects.all()])
        ids = map(int, [
            i.split('A')[1]
            for i in Approval.objects.all().values_list('lodgement_number',
                                                        flat=True) if i
        ])
        ids = list(
            ids
        )  # In python 3, map returns map object.  Therefore before 'if ids' it should be converted to the list(/tuple,...) otherwise 'if ids' is always True
        return max(ids) + 1 if ids else 1

    def save(self, *args, **kwargs):
        super(Approval, self).save(*args, **kwargs)
        if self.lodgement_number == '':
            self.lodgement_number = 'A{0:06d}'.format(self.next_id)
            self.save()

    def __str__(self):
        return self.lodgement_number

    @property
    def reference(self):
        return 'A{}'.format(self.id)

    @property
    def can_reissue(self):
        return self.status == Approval.STATUS_CURRENT or self.status == Approval.STATUS_SUSPENDED

    @property
    def can_reinstate(self):
        return self.status in (Approval.STATUS_CANCELLED,
                               Approval.STATUS_SUSPENDED,
                               Approval.STATUS_SURRENDERED) and self.can_action

    @property
    def allowed_assessors(self):
        return self.current_proposal.compliance_assessors

    @property
    def allowed_approvers(self):
        return self.current_proposal.allowed_approvers

    @property
    def is_issued(self):
        return self.licence_number is not None and len(self.licence_number) > 0

    @property
    def can_action(self):
        if not (self.set_to_cancel or self.set_to_suspend
                or self.set_to_surrender):
            return True
        else:
            return False

    @property
    def can_renew(self):
        if self.apiary_approval:
            open_renewal_proposal_exists = False
            # check for "open" renewal proposals
            for proposal in self.proposal_set.all():
                if (proposal.proposal_type == 'renewal'
                        and proposal.processing_status not in [
                            proposal.PROCESSING_STATUS_APPROVED,
                            proposal.PROCESSING_STATUS_DECLINED,
                            proposal.PROCESSING_STATUS_DISCARDED
                        ]):
                    open_renewal_proposal_exists = True
            if self.renewal_sent and not open_renewal_proposal_exists:
                # Renewal notification has been sent, but the current proposal is not for the renewal
                return True
            else:
                return False
        else:
            try:
                renew_conditions = {
                    'previous_application': self.current_proposal,
                    'proposal_type': 'renewal'
                }
                proposal = Proposal.objects.get(**renew_conditions)
                if proposal:
                    # Proposal for the renewal already exists.
                    return False
            except Proposal.DoesNotExist:
                # Proposal for the renewal doesn't exit
                return True

    @property
    def can_amend(self):
        if not self.apiary_approval:
            try:
                amend_conditions = {
                    'previous_application': self.current_proposal,
                    'proposal_type': 'amendment'
                }
                proposal = Proposal.objects.get(**amend_conditions)
                if proposal:
                    return False
            except Proposal.DoesNotExist:
                if self.can_renew:
                    return True
                else:
                    return False

    def generate_apiary_site_transfer_doc(self,
                                          request_user,
                                          site_transfer_proposal,
                                          preview=None):
        return self.generate_apiary_licence_doc(site_transfer_proposal,
                                                request_user, preview)
        # copied_to_permit = self.copiedToPermit_fields(site_transfer_proposal)  # Get data related to isCopiedToPermit tag
        # if preview:
        #     pdf_contents = create_apiary_licence_pdf_contents(self, site_transfer_proposal, copied_to_permit, user)
        #     return pdf_contents
        # self.licence_document = create_approval_document(self, site_transfer_proposal, copied_to_permit, user)
        # self.save(version_comment='Created Approval PDF: {}'.format(self.licence_document.name))
        # self.current_proposal.save(version_comment='Created Approval PDF: {}'.format(self.licence_document.name))

    def generate_doc(self,
                     request_user,
                     preview=None,
                     site_transfer_preview=None):
        #if self.current_proposal and self.current_proposal.apiary_group_application_type:
        if self.apiary_approval:
            return self.generate_apiary_licence_doc(self.current_proposal,
                                                    request_user, preview,
                                                    site_transfer_preview)
        else:
            from disturbance.components.approvals.pdf import create_approval_doc, create_approval_pdf_bytes
            copied_to_permit = self.copiedToPermit_fields(
                self.current_proposal
            )  #Get data related to isCopiedToPermit tag
            if preview:
                return create_approval_pdf_bytes(self, self.current_proposal,
                                                 copied_to_permit,
                                                 request_user)
            self.licence_document = create_approval_doc(
                self, self.current_proposal, copied_to_permit, request_user)
            self.save(version_comment='Created Approval PDF: {}'.format(
                self.licence_document.name))
            self.current_proposal.save(
                version_comment='Created Approval PDF: {}'.format(
                    self.licence_document.name))

    def generate_apiary_licence_doc(self,
                                    proposal,
                                    request_user,
                                    preview=None,
                                    site_transfer_preview=None):
        #import ipdb; ipdb.set_trace()
        copied_to_permit = self.copiedToPermit_fields(
            proposal)  #Get data related to isCopiedToPermit tag
        if preview:
            pdf_contents = create_apiary_licence_pdf_contents(
                self, proposal, copied_to_permit, request_user,
                site_transfer_preview)
            return pdf_contents
        #self.licence_document = create_apiary_licence_pdf_contents(self, proposal, copied_to_permit, request_user)
        self.licence_document = create_approval_document(
            self, proposal, copied_to_permit, request_user)
        self.save(version_comment='Created Approval PDF: {}'.format(
            self.licence_document.name))
        self.current_proposal.save(version_comment='Created Approval PDF: {}'.
                                   format(self.licence_document.name))

    def generate_renewal_doc(self):
        from disturbance.components.approvals.pdf import create_renewal_doc, create_apiary_renewal_doc
        if self.apiary_approval:
            # self.apiary_renewal_document = create_apiary_renewal_doc(self,self.current_proposal)
            # self.save(version_comment='Created Approval PDF: {}'.format(self.apiary_renewal_document.name))
            # self.current_proposal.save(version_comment='Created Approval PDF: {}'.format(self.apiary_renewal_document.name))
            doc = create_apiary_renewal_doc(self, self.current_proposal)
            self.save(
                version_comment='Created Approval PDF: {}'.format(doc.name))
            self.current_proposal.save(
                version_comment='Created Approval PDF: {}'.format(doc.name))
        else:
            self.renewal_document = create_renewal_doc(self,
                                                       self.current_proposal)
            self.save(version_comment='Created Approval PDF: {}'.format(
                self.renewal_document.name))
            self.current_proposal.save(
                version_comment='Created Approval PDF: {}'.format(
                    self.renewal_document.name))

    def copiedToPermit_fields(self, proposal):
        p = proposal
        copied_data = []
        search_assessor_data = []
        search_schema = search_multiple_keys(p.schema,
                                             primary_search='isCopiedToPermit',
                                             search_list=['label', 'name'])
        if p.assessor_data:
            search_assessor_data = search_keys(
                p.assessor_data, search_list=['assessor', 'name'])
        if search_schema:
            for c in search_schema:
                try:
                    if search_assessor_data:
                        for d in search_assessor_data:
                            if c['name'] == d['name']:
                                if d['assessor']:
                                    #copied_data.append({c['label'], d['assessor']})
                                    copied_data.append(
                                        {c['label']: d['assessor']})
                except:
                    raise
        return copied_data

    def log_user_action(self, action, request):
        return ApprovalUserAction.log_action(self, action, request.user)

    def expire_approval(self, user):
        with transaction.atomic():
            try:
                today = timezone.localtime(timezone.now()).date()
                if self.status == Approval.STATUS_CURRENT and self.expiry_date < today:
                    self.status = Approval.STATUS_EXPIRED
                    self.save()
                    send_approval_expire_email_notification(self)

                    # Change the statuses of the apiary sites, too
                    self.change_apiary_site_status(self.status)

                    proposal = self.current_proposal
                    ApprovalUserAction.log_action(
                        self,
                        ApprovalUserAction.ACTION_EXPIRE_APPROVAL.format(
                            self.lodgement_number), user)
                    ProposalUserAction.log_action(
                        proposal,
                        ProposalUserAction.ACTION_EXPIRED_APPROVAL_.format(
                            proposal.lodgement_number), user)
            except:
                raise

    def change_apiary_site_status(self, approval_status):
        relations = self.get_relations()
        if approval_status in (
                Approval.STATUS_CANCELLED,
                Approval.STATUS_SUSPENDED,
                Approval.STATUS_SURRENDERED,
        ):
            relations.update(site_status=SITE_STATUS_NOT_TO_BE_REISSUED)
        elif approval_status == Approval.STATUS_EXPIRED:
            for apiary_site in self.apiary_sites.all():
                apiary_site.make_vacant(True, apiary_site.latest_approval_link)
            relations.update(available=False)
        elif approval_status == Approval.STATUS_CURRENT:
            relations.update(site_status=SITE_STATUS_CURRENT)

    def approval_cancellation(self, request, details):
        with transaction.atomic():
            try:
                if not request.user in self.allowed_assessors:
                    raise ValidationError(
                        'You do not have access to cancel this approval')
                if not self.can_reissue and self.can_action:
                    raise ValidationError(
                        'You cannot cancel approval if it is not current or suspended'
                    )
                self.cancellation_date = details.get(
                    'cancellation_date').strftime('%Y-%m-%d')
                self.cancellation_details = details.get('cancellation_details')
                cancellation_date = datetime.datetime.strptime(
                    self.cancellation_date, '%Y-%m-%d')
                cancellation_date = cancellation_date.date()
                self.cancellation_date = cancellation_date
                today = timezone.now().date()
                if cancellation_date <= today:
                    if not self.status == Approval.STATUS_CANCELLED:
                        self.status = Approval.STATUS_CANCELLED
                        self.set_to_cancel = False
                        send_approval_cancel_email_notification(self)

                        # Change the statuses of the apiary sites, too
                        self.change_apiary_site_status(self.status)
                else:
                    self.set_to_cancel = True
                    send_approval_cancel_email_notification(self,
                                                            future_cancel=True)
                #import ipdb; ipdb.set_trace()
                self.save()
                # Log proposal action
                self.log_user_action(
                    ApprovalUserAction.ACTION_CANCEL_APPROVAL.format(
                        self.lodgement_number), request)
                # Log entry for organisation
                #self.current_proposal.log_user_action(ProposalUserAction.ACTION_CANCEL_APPROVAL.format(self.current_proposal.id),request)
            except:
                raise

    def approval_suspension(self, request, details):
        with transaction.atomic():
            try:
                if not request.user in self.allowed_assessors:
                    raise ValidationError(
                        'You do not have access to suspend this approval')
                if not self.can_reissue and self.can_action:
                    raise ValidationError(
                        'You cannot suspend approval if it is not current or suspended'
                    )
                if details.get('to_date'):
                    to_date = details.get('to_date').strftime('%d/%m/%Y')
                else:
                    to_date = ''
                self.suspension_details = {
                    'from_date': details.get('from_date').strftime('%d/%m/%Y'),
                    'to_date': to_date,
                    'details': details.get('suspension_details'),
                }
                today = timezone.now().date()
                from_date = datetime.datetime.strptime(
                    self.suspension_details['from_date'], '%d/%m/%Y')
                from_date = from_date.date()
                if from_date <= today:
                    if not self.status == Approval.STATUS_SUSPENDED:
                        self.status = Approval.STATUS_SUSPENDED
                        self.set_to_suspend = False
                        self.save()
                        send_approval_suspend_email_notification(self)

                        # Change the statuses of the apiary sites, too
                        self.change_apiary_site_status(self.status)
                else:
                    self.set_to_suspend = True
                    send_approval_suspend_email_notification(
                        self, future_suspend=True)
                self.save()
                # Log approval action
                self.log_user_action(
                    ApprovalUserAction.ACTION_SUSPEND_APPROVAL.format(
                        self.lodgement_number), request)
                # Log entry for proposal
                #self.current_proposal.log_user_action(ProposalUserAction.ACTION_SUSPEND_APPROVAL.format(self.current_proposal.id),request)
            except:
                raise

    def reinstate_approval(self, request):
        with transaction.atomic():
            try:
                if not request.user in self.allowed_assessors:
                    raise ValidationError(
                        'You do not have access to reinstate this approval')
                if not self.can_reinstate:
                    #if not self.status == 'suspended':
                    raise ValidationError(
                        'You cannot reinstate approval at this stage')
                today = timezone.now().date()
                if not self.can_reinstate and self.expiry_date >= today:
                    #if not self.status == 'suspended' and self.expiry_date >= today:
                    raise ValidationError(
                        'You cannot reinstate approval at this stage')
                if self.status == Approval.STATUS_CANCELLED:
                    self.cancellation_details = ''
                    self.cancellation_date = None
                if self.status == Approval.STATUS_SURRENDERED:
                    self.surrender_details = {}
                if self.status == Approval.STATUS_SUSPENDED:
                    self.suspension_details = {}

                self.status = Approval.STATUS_CURRENT
                #self.suspension_details = {}
                self.save()
                send_approval_reinstate_email_notification(self, request)

                # Change the statuses of the apiary sites, too
                self.change_apiary_site_status(self.status)

                # Log approval action
                self.log_user_action(
                    ApprovalUserAction.ACTION_REINSTATE_APPROVAL.format(
                        self.lodgement_number), request)
                # Log entry for proposal
                #self.current_proposal.log_user_action(ProposalUserAction.ACTION_REINSTATE_APPROVAL.format(self.current_proposal.id),request)
            except:
                raise

    def approval_surrender(self, request, details):
        with transaction.atomic():
            try:
                if self.applicant and not request.user.disturbance_organisations.filter(
                        organisation_id=self.relevant_applicant_id):
                    #if not request.user in self.allowed_assessors:
                    if request.user not in self.allowed_assessors and not is_customer(
                            request):
                        raise ValidationError(
                            'You do not have access to surrender this approval'
                        )
                if not self.can_reissue and self.can_action:
                    raise ValidationError(
                        'You cannot surrender approval if it is not current or suspended'
                    )
                self.surrender_details = {
                    'surrender_date':
                    details.get('surrender_date').strftime('%d/%m/%Y'),
                    'details':
                    details.get('surrender_details'),
                }
                today = timezone.now().date()
                surrender_date = datetime.datetime.strptime(
                    self.surrender_details['surrender_date'], '%d/%m/%Y')
                surrender_date = surrender_date.date()
                if surrender_date <= today:
                    if not self.status == Approval.STATUS_SURRENDERED:
                        self.status = Approval.STATUS_SURRENDERED
                        self.set_to_surrender = False
                        self.save()
                        send_approval_surrender_email_notification(self)

                        # Change the statuses of the apiary sites, too
                        self.change_apiary_site_status(self.status)
                else:
                    self.set_to_surrender = True
                    send_approval_surrender_email_notification(
                        self, future_surrender=True)
                self.save()
                # Log approval action
                self.log_user_action(
                    ApprovalUserAction.ACTION_SURRENDER_APPROVAL.format(
                        self.lodgement_number), request)
                # Log entry for proposal
                #self.current_proposal.log_user_action(ProposalUserAction.ACTION_SURRENDER_APPROVAL.format(self.current_proposal.id),request)
            except:
                raise

    def pdf_view_log(self, request):
        self.log_user_action(
            ApprovalUserAction.ACTION_APPROVAL_PDF_VIEW.format(
                self.lodgement_number), request)
        return self