class MeetingFile(ExtendedModel):
    file = models.FileField(
        verbose_name=_('file'), storage=storages.get_storage('meetings'), max_length=2000,
        upload_to=meeting_file_path)
    meeting = models.ForeignKey(Meeting, on_delete=models.DO_NOTHING, related_name='files')

    objects = MeetingFileManager()
    trash = MeetingFileTrashManager()
    i18n = TranslationField()
    tracker = FieldTracker()

    @property
    def download_url(self):
        return self._get_absolute_url(self.file.url, use_lang=False) if self.file else None

    @property
    def name(self):
        return self._get_basename(self.file.name)

    def __str__(self):
        return self.name

    class Meta:
        verbose_name = _('meeting file')
        verbose_name_plural = _('meeting files')
        default_manager_name = "objects"
class LabReport(ExtendedModel):
    link = models.URLField(verbose_name=_('Report Link'),
                           max_length=2000,
                           blank=True,
                           null=True)
    file = models.FileField(verbose_name=_("File"),
                            storage=storages.get_storage('lab_reports'),
                            max_length=2000,
                            blank=True,
                            null=True)
    lab_event = models.ForeignKey(to=LabEvent,
                                  on_delete=models.DO_NOTHING,
                                  related_name="reports")

    objects = SoftDeletableManager()
    trash = TrashManager()
    i18n = TranslationField(fields=())
    tracker = FieldTracker()

    @property
    def report_type(self):
        return 'link' if self.link else 'file'

    @property
    def download_url(self):
        return self._get_absolute_url(self.file.url,
                                      use_lang=False) if self.file else None

    def __str__(self):
        if self.link:
            return self.link

        if self.file:
            return self.file.name

        return super().__str__()

    class Meta:
        verbose_name = _("Report")
        verbose_name_plural = _("Reports")
        db_table = 'lab_report'
        default_manager_name = "objects"
        indexes = [
            GinIndex(fields=["i18n"]),
        ]
Beispiel #3
0
    def handle(self, *args, **options):

        archives_storage = get_storage('datasets_archives')
        archive_dirs = [
            x[0] for x in os.walk(archives_storage.location)
            if x[0] != archives_storage.location
        ]
        for directory in archive_dirs:
            non_symlinks = [
                os.path.join(directory, f) for f in os.listdir(directory)
                if not os.path.islink(os.path.join(directory, f))
            ]
            if len(non_symlinks) > 1:
                files_with_dt = sorted(
                    [(file_path, os.path.getmtime(file_path))
                     for file_path in non_symlinks],
                    key=lambda x: x[1])
                for f in files_with_dt[1:]:
                    os.remove(f[0])
Beispiel #4
0
def archive_resources_files(dataset_id):
    free_space = disk_usage(settings.MEDIA_ROOT).free
    if free_space < settings.ALLOWED_MINIMUM_SPACE:
        logger.error(
            'There is not enough free space on disk, archive creation is canceled.'
        )
        raise ResourceWarning
    logger.debug(
        f'Updating dataset resources files archive for dataset {dataset_id}')
    resourcefile_model = apps.get_model('resources', 'ResourceFile')
    resource_model = apps.get_model('resources', 'Resource')
    dataset_model = apps.get_model('datasets', 'Dataset')
    ds = dataset_model.raw.get(pk=dataset_id)

    def create_full_file_path(_fname):
        storage_location = ds.archived_resources_files.storage.location
        full_fname = ds.archived_resources_files.field.generate_filename(
            ds, _fname)
        return os.path.join(storage_location, full_fname)

    creation_start = datetime.now()
    tmp_filename = f'resources_files_{creation_start.strftime("%Y-%m-%d-%H%M%S%f")}.zip'
    symlink_name = 'resources_files.zip'
    res_storage = storages.get_storage('resources')
    full_symlink_name = ds.archived_resources_files.field.generate_filename(
        ds, symlink_name)

    full_file_path = create_full_file_path(tmp_filename)
    full_symlink_path = create_full_file_path(symlink_name)
    full_tmp_symlink_path = create_full_file_path('tmp_resources_files.zip')
    abs_path = os.path.dirname(full_file_path)
    os.makedirs(abs_path, exist_ok=True)
    with zipfile.ZipFile(full_file_path,
                         'w',
                         zipfile.ZIP_DEFLATED,
                         compresslevel=1) as main_zip:
        res_location = res_storage.location
        if is_enabled('S40_new_file_model.be'):
            filtered_files = resourcefile_model.objects.filter(
                resource__dataset_id=dataset_id,
                resource__status='published',
                resource__is_removed=False)
            file_details = filtered_files.values_list('file', 'resource_id')
        else:
            filtered_files = resource_model.objects.filter(
                status='published', dataset_id=dataset_id).with_files()
            all_resource_files = filtered_files.values('file', 'csv_file',
                                                       'jsonld_file', 'pk')
            file_details = []
            for res in all_resource_files:
                res_files = [(res['file'], res['pk'])]
                if res['csv_file']:
                    res_files.append((res['csv_file'], res['pk']))
                if res['jsonld_file']:
                    res_files.append((res['jsonld_file'], res['pk']))
                file_details.extend(res_files)
        for details in file_details:
            split_name = details[0].split('/')
            full_path = os.path.join(res_location, details[0])
            main_zip.write(
                full_path, os.path.join(f'resource_{details[1]}',
                                        split_name[1]))
    if not ds.archived_resources_files:
        os.symlink(full_file_path, full_symlink_path)
        dataset_model.objects.filter(pk=dataset_id).update(
            archived_resources_files=full_symlink_name)
    else:
        old_file_path = os.path.realpath(full_symlink_path)
        os.symlink(full_file_path, full_tmp_symlink_path)
        os.rename(full_tmp_symlink_path, full_symlink_path)
        os.remove(old_file_path)
    logger.debug(f'Updated dataset {dataset_id} archive with {tmp_filename}')
Beispiel #5
0
class Resource(ExtendedModel):
    SIGNALS_MAP = {
        'updated': (
            revalidate_resource,
            search_signals.update_document_with_related,
            core_signals.notify_updated
        ),
        'published': (
            revalidate_resource,
            search_signals.update_document_with_related,
            core_signals.notify_published
        ),
        'restored': (
            revalidate_resource,
            search_signals.update_document_with_related,
            core_signals.notify_restored
        ),
    }
    file = models.FileField(verbose_name=_("File"), storage=storages.get_storage('resources'),
                            upload_to='%Y%m%d',
                            max_length=2000, blank=True, null=True)
    packed_file = models.FileField(verbose_name=_("Packed file"), storage=storages.get_storage('resources'),
                                   upload_to='%Y%m%d',
                                   max_length=2000, blank=True, null=True)
    file_info = models.TextField(blank=True, null=True, editable=False, verbose_name=_("File info"))
    file_encoding = models.CharField(max_length=150, null=True, blank=True, editable=False,
                                     verbose_name=_("File encoding"))
    link = models.URLField(verbose_name=_('Resource Link'), max_length=2000, blank=True, null=True)
    title = models.CharField(max_length=500, verbose_name=_("title"))
    description = models.TextField(blank=True, null=True, verbose_name=_("Description"))
    position = models.IntegerField(default=1, verbose_name=_("Position"))
    dataset = models.ForeignKey('datasets.Dataset', on_delete=models.CASCADE, related_name='resources',
                                verbose_name=_('Dataset'))

    format = models.CharField(max_length=150, blank=True, null=True, verbose_name=_("Format"),
                              choices=supported_formats_choices())
    type = models.CharField(max_length=10, choices=RESOURCE_TYPE, default='file', editable=False,
                            verbose_name=_("Type"))

    openness_score = models.IntegerField(default=0, verbose_name=_("Openness score"),
                                         validators=[MinValueValidator(0), MaxValueValidator(5)])

    created_by = models.ForeignKey(
        User,
        models.DO_NOTHING,
        blank=False,
        editable=False,
        null=True,
        verbose_name=_("Created by"),
        related_name='resources_created'
    )
    modified_by = models.ForeignKey(
        User,
        models.DO_NOTHING,
        blank=False,
        editable=False,
        null=True,
        verbose_name=_("Modified by"),
        related_name='resources_modified'
    )
    link_tasks = models.ManyToManyField('TaskResult', verbose_name=_('Download Tasks'),
                                        blank=True,
                                        related_name='link_task_resources',
                                        )
    file_tasks = models.ManyToManyField('TaskResult', verbose_name=_('Download Tasks'),
                                        blank=True,
                                        related_name='file_task_resources')
    data_tasks = models.ManyToManyField('TaskResult', verbose_name=_('Download Tasks'),
                                        blank=True,
                                        related_name='data_task_resources')

    old_file = models.FileField(verbose_name=_("File"), storage=storages.get_storage('resources'), upload_to='',
                                max_length=2000, blank=True, null=True)
    old_resource_type = models.TextField(verbose_name=_("Data type"), null=True)
    old_format = models.CharField(max_length=150, blank=True, null=True, verbose_name=_("Format"))
    old_customfields = JSONField(blank=True, null=True, verbose_name=_("Customfields"))
    old_link = models.URLField(verbose_name=_('Resource Link'), max_length=2000, blank=True, null=True)
    downloads_count = models.PositiveIntegerField(default=0)

    show_tabular_view = models.BooleanField(verbose_name=_('Tabular view'), default=True)
    tabular_data_schema = JSONField(null=True, blank=True)
    data_date = models.DateField(null=True, verbose_name=_("Data date"))

    verified = models.DateTimeField(blank=True, default=now, verbose_name=_("Update date"))

    def __str__(self):
        return self.title

    @property
    def media_type(self):
        return self.type or ''

    @property
    def category(self):
        return self.dataset.category if self.dataset else ''

    @property
    def link_is_valid(self):
        task = self.link_tasks.last()
        return task.status if task else 'NOT_AVAILABLE'

    @property
    def file_is_valid(self):
        task = self.file_tasks.last()
        return task.status if task else 'NOT_AVAILABLE'

    @property
    def data_is_valid(self):
        task = self.data_tasks.last()
        return task.status if task else 'NOT_AVAILABLE'

    @property
    def file_url(self):
        if self.file:
            _file_url = self.file.url if not self.packed_file else self.packed_file.url
            return '%s%s' % (settings.API_URL, _file_url)
        return ''

    @property
    def file_size(self):
        return self.file.size if self.file else None

    @property
    def download_url(self):
        if self.file:
            return '{}/resources/{}/file'.format(settings.API_URL, self.ident)
        return ''

    @property
    def is_indexable(self):
        if self.type == 'file' and not self.file_is_valid:
            return False

        if self.type in ('api', 'website') and not self.link_is_valid:
            return False

        return True

    @property
    def tabular_data(self):
        if not self._tabular_data:
            if self.format in ('csv', 'tsv', 'xls', 'xlsx', 'ods') and self.file:
                self._tabular_data = TabularData(self)
        return self._tabular_data

    def index_tabular_data(self, force=False):
        self.tabular_data.validate()
        return self.tabular_data.index(force=force)

    def save_file(self, content, filename):
        dt = self.created.date() if self.created else now().date()
        subdir = dt.isoformat().replace("-", "")
        dest_dir = os.path.join(self.file.storage.location, subdir)
        os.makedirs(dest_dir, exist_ok=True)
        file_path = os.path.join(dest_dir, filename)
        with open(file_path, 'wb') as f:
            f.write(content.read())
        return '%s/%s' % (subdir, filename)

    def revalidate(self, **kwargs):
        if not self.link or self.link.startswith(settings.BASE_URL):
            process_resource_file_task.s(self.id, **kwargs).apply_async(countdown=2)
        else:
            process_resource_from_url_task.s(self.id, **kwargs).apply_async(countdown=2)

    @classmethod
    def accusative_case(cls):
        return _("acc: Resource")

    @property
    def _openness_score(self):
        if not self.format:
            return 0
        _, content = content_type_from_file_format(self.format.lower())
        return OPENNESS_SCORE.get(content, 0)

    @property
    def file_size_human_readable(self):
        file_size = self.file_size or 0
        return sizeof_fmt(file_size)

    @property
    def title_truncated(self):
        title = (self.title[:100] + '..') if len(self.title) > 100 else self.title
        return title

    _tabular_data = None

    i18n = TranslationField(fields=("title", "description"))
    tracker = FieldTracker()
    slugify_field = 'title'

    raw = models.Manager()
    objects = SoftDeletableManager()
    deleted = DeletedManager()

    class Meta:
        verbose_name = _("Resource")
        verbose_name_plural = _("Resources")
        db_table = 'resource'
        default_manager_name = "objects"
        indexes = [GinIndex(fields=["i18n"]), ]
class Organization(ExtendedModel):
    INSTITUTION_TYPE_PRIVATE = 'private'
    INSTITUTION_TYPE_CHOICES = (
        ('local', _('Local government')),
        ('state', _('Public government')),
        (INSTITUTION_TYPE_PRIVATE, _('Private entities')),
        ('other', _('Other')),
    )
    SIGNALS_MAP = {
        'updated': (rdf_signals.update_graph_with_conditional_related,
                    search_signals.update_document_with_related,
                    core_signals.notify_updated),
        'published':
        (rdf_signals.create_graph, search_signals.update_document_with_related,
         core_signals.notify_published),
        'restored':
        (rdf_signals.create_graph, search_signals.update_document_with_related,
         core_signals.notify_restored),
        'removed': (rdf_signals.delete_graph, remove_related_datasets,
                    search_signals.remove_document_with_related,
                    core_signals.notify_removed),
    }

    title = models.CharField(max_length=200, verbose_name=_('Name'))
    description = models.TextField(blank=True,
                                   null=True,
                                   verbose_name=_('Description'))
    image = models.ImageField(max_length=254,
                              storage=storages.get_storage('organizations'),
                              upload_to='%Y%m%d',
                              blank=True,
                              null=True,
                              verbose_name=_('Image URL'))
    postal_code = models.CharField(max_length=6,
                                   null=True,
                                   verbose_name=_('Postal code'))
    city = models.CharField(max_length=200, null=True, verbose_name=_("City"))
    street_type = models.CharField(max_length=50,
                                   null=True,
                                   verbose_name=_("Street type"))
    street = models.CharField(max_length=200,
                              null=True,
                              verbose_name=_("Street"))
    street_number = models.CharField(max_length=200,
                                     null=True,
                                     blank=True,
                                     verbose_name=_("Street number"))
    flat_number = models.CharField(max_length=200,
                                   null=True,
                                   blank=True,
                                   verbose_name=_("Flat number"))

    email = models.CharField(max_length=300,
                             null=True,
                             verbose_name=_("Email"))
    epuap = models.CharField(max_length=500,
                             null=True,
                             verbose_name=_("EPUAP"))
    fax = models.CharField(max_length=50, null=True, verbose_name=_("Fax"))
    fax_internal = models.CharField(max_length=20,
                                    null=True,
                                    blank=True,
                                    verbose_name=_('int.'))

    institution_type = models.CharField(max_length=50,
                                        choices=INSTITUTION_TYPE_CHOICES,
                                        default=INSTITUTION_TYPE_CHOICES[1][0],
                                        verbose_name=_("Institution type"))
    regon = models.CharField(max_length=20, null=True, verbose_name=_("REGON"))
    tel = models.CharField(max_length=50, null=True, verbose_name=_("Phone"))
    tel_internal = models.CharField(max_length=20,
                                    null=True,
                                    blank=True,
                                    verbose_name=_('int.'))
    website = models.CharField(max_length=200,
                               null=True,
                               verbose_name=_("Website"))
    abbreviation = models.CharField(max_length=30,
                                    null=True,
                                    verbose_name=_("Abbreviation"))

    i18n = TranslationField(fields=('title', 'description', 'slug'))

    created_by = models.ForeignKey(User,
                                   models.DO_NOTHING,
                                   blank=False,
                                   editable=False,
                                   null=True,
                                   verbose_name=_("Created by"),
                                   related_name='organizations_created')
    modified_by = models.ForeignKey(User,
                                    models.DO_NOTHING,
                                    blank=False,
                                    editable=False,
                                    null=True,
                                    verbose_name=_("Modified by"),
                                    related_name='organizations_modified')

    def __str__(self):
        if self.title:
            return self.title
        return self.slug

    @property
    def frontend_url(self):
        return f'/institution/{self.ident}'

    @property
    def frontend_absolute_url(self):
        return self._get_absolute_url(self.frontend_url)

    @property
    def image_url(self):
        return self.image.url if self.image else ''

    @property
    def image_absolute_url(self):
        return self._get_absolute_url(self.image_url,
                                      use_lang=False) if self.image_url else ''

    @property
    def short_description(self):
        clean_text = ""
        if self.description:
            clean_text = ''.join(
                BeautifulSoup(self.description,
                              "html.parser").stripped_strings)
        return clean_text

    @property
    def ckan_datasources(self):
        return list(
            set([
                x.source for x in self.published_datasets
                if x.source and x.source.is_ckan
            ]))

    @property
    def datasources(self):
        return list(
            set([x.source for x in self.published_datasets if x.source]))

    @property
    def sources(self):
        _sources = [{
            'title': x.title,
            'url': x.url,
            'source_type': x.source_type
        } for x in self.datasources]
        return sorted(_sources, key=lambda x: x['title'])

    @property
    def api_url_base(self):
        return 'institutions'

    @property
    def description_html(self):
        return format_html(self.description)

    @property
    def datasets_count(self):
        return self.datasets.count()

    @classmethod
    def accusative_case(cls):
        return _("acc: Institution")

    @property
    def published_datasets(self):
        return self.datasets.filter(status='published')

    @property
    def published_datasets_count(self):
        return self.published_datasets.count()

    @property
    def published_resources_count(self):
        return sum(
            [x.published_resources_count for x in self.published_datasets])

    @property
    def address_display(self):
        city = ' '.join(i.strip() for i in [self.postal_code, self.city] if i)
        if not city:
            return None
        number = '/'.join(i.strip()
                          for i in [self.street_number, self.flat_number] if i)
        addres_line = city
        if self.street:
            street = ' '.join(i.strip()
                              for i in [self.street_type, self.street, number]
                              if i)
            addres_line = ', '.join(i for i in [addres_line, street] if i)

        return addres_line

    @property
    def phone_display(self):
        if not self.tel:
            return None
        try:
            p = phonenumbers.parse(self.tel, 'PL')
            phone = phonenumbers.format_number(
                p, phonenumbers.PhoneNumberFormat.INTERNATIONAL)
        except phonenumbers.phonenumberutil.NumberParseException:
            return None
        return _(' int. ').join(i.strip() for i in [phone, self.tel_internal]
                                if i)

    @property
    def fax_display(self):
        if not self.fax:
            return None
        try:
            p = phonenumbers.parse(self.fax, 'PL')
            fax = phonenumbers.format_number(
                p, phonenumbers.PhoneNumberFormat.INTERNATIONAL)
        except phonenumbers.phonenumberutil.NumberParseException:
            return None
        return _(' int. ').join(i.strip() for i in [fax, self.fax_internal]
                                if i)

    objects = OrganizationManager()
    trash = OrganizationTrashManager()

    tracker = FieldTracker()
    slugify_field = 'title'

    short_description.fget.short_description = _("Description")

    class Meta:
        db_table = "organization"
        verbose_name = _("Institution")
        verbose_name_plural = _("Institutions")
        default_manager_name = "objects"
        indexes = [
            GinIndex(fields=["i18n"]),
        ]
Beispiel #7
0
class Application(ExtendedModel):
    SIGNALS_MAP = {
        'updated': (generate_thumbnail, core_signals.notify_updated),
        'published': (generate_thumbnail, core_signals.notify_published),
        'restored': (generate_thumbnail, core_signals.notify_restored),
        'removed': (search_signals.remove_document, core_signals.notify_removed),
        'm2m_added': (search_signals.update_document, core_signals.notify_m2m_added,),
        'm2m_removed': (search_signals.update_document, core_signals.notify_m2m_removed,),
        'm2m_cleaned': (search_signals.update_document, core_signals.notify_m2m_cleaned,),
    }
    title = models.CharField(max_length=300, verbose_name=_("Title"))
    notes = models.TextField(verbose_name=_("Notes"), null=True)
    author = models.CharField(max_length=50, blank=True, null=True, verbose_name=_("Author"))
    url = models.URLField(max_length=300, verbose_name=_("App URL"), null=True)
    image = models.ImageField(
        max_length=200, storage=storages.get_storage('applications'),
        upload_to='%Y%m%d', blank=True, null=True, verbose_name=_("Image URL")
    )
    image_thumb = models.ImageField(
        storage=storages.get_storage('applications'),
        upload_to='%Y%m%d', blank=True, null=True
    )

    datasets = models.ManyToManyField('datasets.Dataset',
                                      db_table='application_dataset',
                                      verbose_name=_('Datasets'),
                                      related_name='applications',
                                      related_query_name="application")
    tags = models.ManyToManyField('tags.Tag',
                                  blank=True,
                                  db_table='application_tag',
                                  verbose_name=_('Tag'),
                                  related_name='applications',
                                  related_query_name="application")

    created_by = models.ForeignKey(
        User,
        models.DO_NOTHING,
        blank=False,
        editable=False,
        null=True,
        verbose_name=_("Created by"),
        related_name='applications_created'
    )

    modified_by = models.ForeignKey(
        User,
        models.DO_NOTHING,
        blank=False,
        editable=False,
        null=True,
        verbose_name=_("Modified by"),
        related_name='applications_modified'
    )

    @cached_property
    def image_url(self):
        try:
            return self.image.url
        except ValueError:
            return ''

    @cached_property
    def image_thumb_url(self):
        try:
            return self.image_thumb.url
        except ValueError:
            return ''

    @cached_property
    def tags_list(self):
        return [tag.name_translated for tag in self.tags.all()]

    @cached_property
    def users_following_list(self):
        return [user.id for user in self.users_following.all()]

    def __str__(self):
        return self.title

    @classmethod
    def accusative_case(cls):
        return _("acc: Application")

    def published_datasets(self):
        return self.datasets.filter(status='published')

    i18n = TranslationField(fields=("title", "notes"))
    raw = models.Manager()
    objects = SoftDeletableManager()
    deleted = DeletedManager()
    tracker = FieldTracker()
    slugify_field = 'title'

    class Meta:
        verbose_name = _("Application")
        verbose_name_plural = _("Applications")
        db_table = "application"
        default_manager_name = "objects"
        indexes = [GinIndex(fields=["i18n"]), ]
Beispiel #8
0
class Dataset(ExtendedModel):
    LICENSE_CC0 = 1
    LICENSE_CC_BY = 2
    LICENSE_CC_BY_SA = 3
    LICENSE_CC_BY_NC = 4
    LICENSE_CC_BY_NC_SA = 5
    LICENSE_CC_BY_ND = 6
    LICENSE_CC_BY_NC_ND = 7
    LICENSES = (
        (LICENSE_CC0, "CC0 1.0"),
        (LICENSE_CC_BY, "CC BY 4.0"),
        (LICENSE_CC_BY_SA, "CC BY-SA 4.0"),
        (LICENSE_CC_BY_NC, "CC BY-NC 4.0"),
        (LICENSE_CC_BY_NC_SA, "CC BY-NC-SA 4.0"),
        (LICENSE_CC_BY_ND, "CC BY-ND 4.0"),
        (LICENSE_CC_BY_NC_ND, "CC BY-NC-ND 4.0"),
    )
    LICENSE_CODE_TO_NAME = dict(LICENSES)

    SIGNALS_MAP = {
        'updated': (rdf_signals.update_graph_with_conditional_related,
                    search_signals.update_document_with_related,
                    core_signals.notify_updated),
        'published':
        (rdf_signals.create_graph, search_signals.update_document_with_related,
         core_signals.notify_published),
        'restored':
        (rdf_signals.create_graph, search_signals.update_document_with_related,
         core_signals.notify_restored),
        'removed': (remove_related_resources, rdf_signals.delete_graph,
                    search_signals.remove_document_with_related,
                    core_signals.notify_removed),
        'post_m2m_added': (
            search_signals.update_document_related,
            rdf_signals.update_graph,
        ),
        'post_m2m_removed': (
            search_signals.update_document_related,
            rdf_signals.update_graph,
        ),
        'post_m2m_cleaned': (
            search_signals.update_document_related,
            rdf_signals.update_graph,
        )
    }
    ext_ident = models.CharField(
        max_length=36,
        blank=True,
        editable=False,
        verbose_name=_('external identifier'),
        help_text=
        _('external identifier of dataset taken during import process (optional)'
          ))
    title = models.CharField(max_length=300,
                             null=True,
                             verbose_name=_("Title"))
    version = models.CharField(max_length=100,
                               blank=True,
                               null=True,
                               verbose_name=_("Version"))
    url = models.CharField(max_length=1000,
                           blank=True,
                           null=True,
                           verbose_name=_("Url"))
    notes = models.TextField(verbose_name=_("Notes"), null=True, blank=False)

    license_chosen = models.PositiveSmallIntegerField(blank=True,
                                                      null=True,
                                                      default=None,
                                                      verbose_name='',
                                                      choices=LICENSES)

    license_old_id = models.CharField(max_length=20,
                                      blank=True,
                                      null=True,
                                      verbose_name=_("License ID"))
    license = models.ForeignKey('licenses.License',
                                on_delete=models.DO_NOTHING,
                                blank=True,
                                null=True,
                                verbose_name=_("License ID"))

    license_condition_db_or_copyrighted = models.TextField(
        blank=True,
        null=True,
        verbose_name=
        _("Conditions for using public information that meets the characteristics of the work or constitute "
          "a database (Article 13 paragraph 2 of the Act on the re-use of public sector information)"
          ))
    license_condition_personal_data = models.CharField(
        max_length=300,
        blank=True,
        null=True,
        verbose_name=_(
            "Conditions for using public sector information containing personal data (Article 14 paragraph 1 "
            "point 4 of the Act on the re-use of public Sector Information)"))
    license_condition_modification = models.NullBooleanField(
        null=True,
        blank=True,
        default=None,
        verbose_name=
        _("The recipient should inform about the processing of the information when it modifies it"
          ))
    license_condition_original = models.NullBooleanField(null=True,
                                                         blank=True,
                                                         default=None)
    license_condition_responsibilities = models.TextField(
        blank=True,
        null=True,
        verbose_name=
        _("The scope of the provider's responsibility for the information provided"
          ))
    license_condition_source = models.NullBooleanField(
        null=True,
        blank=True,
        default=None,
        verbose_name=_(
            "The recipient should inform about the date, time of completion and obtaining information from"
            " the obliged entity"))
    license_condition_timestamp = models.NullBooleanField(null=True,
                                                          blank=True)
    organization = models.ForeignKey('organizations.Organization',
                                     on_delete=models.CASCADE,
                                     related_name='datasets',
                                     verbose_name=_('Institution'))
    customfields = JSONField(blank=True,
                             null=True,
                             verbose_name=_("Customfields"))
    update_frequency = models.CharField(max_length=50,
                                        blank=True,
                                        null=True,
                                        verbose_name=_("Update frequency"))
    is_update_notification_enabled = models.BooleanField(
        default=True, verbose_name=_('turn on notification'))
    update_notification_frequency = models.PositiveSmallIntegerField(
        null=True, blank=True, verbose_name=_('set notifications frequency'))
    update_notification_recipient_email = models.EmailField(
        blank=True,
        verbose_name=_('the person who is the notifications recipient'))

    category = models.ForeignKey('categories.Category',
                                 on_delete=models.SET_NULL,
                                 blank=True,
                                 null=True,
                                 verbose_name=_('Category'),
                                 limit_choices_to=Q(code=''))

    categories = models.ManyToManyField('categories.Category',
                                        db_table='dataset_category',
                                        verbose_name=_('Categories'),
                                        related_name='datasets',
                                        related_query_name="dataset",
                                        limit_choices_to=~Q(code=''))

    tags = models.ManyToManyField('tags.Tag',
                                  db_table='dataset_tag',
                                  blank=False,
                                  verbose_name=_("Tag"),
                                  related_name='datasets',
                                  related_query_name="dataset")

    created_by = models.ForeignKey(User,
                                   models.DO_NOTHING,
                                   blank=False,
                                   editable=False,
                                   null=True,
                                   verbose_name=_("Created by"),
                                   related_name='datasets_created')
    modified_by = models.ForeignKey(User,
                                    models.DO_NOTHING,
                                    blank=False,
                                    editable=False,
                                    null=True,
                                    verbose_name=_("Modified by"),
                                    related_name='datasets_modified')
    source = models.ForeignKey(
        'harvester.DataSource',
        models.CASCADE,
        null=True,
        blank=True,
        verbose_name=_('source'),
        related_name='datasource_datasets',
    )
    verified = models.DateTimeField(blank=True,
                                    default=now,
                                    verbose_name=_("Update date"))
    downloads_count = models.PositiveIntegerField(
        verbose_name=_('download counter'), default=0)
    image = models.ImageField(
        max_length=200,
        storage=get_storage('datasets'),
        upload_to='dataset_logo/%Y%m%d',
        blank=True,
        null=True,
        verbose_name=_('Image URL'),
    )
    image_alt = models.CharField(max_length=255,
                                 blank=True,
                                 verbose_name=_('Alternative text'))
    dcat_vocabularies = JSONField(blank=True,
                                  null=True,
                                  verbose_name=_("Controlled Vocabularies"))
    archived_resources_files = models.FileField(
        storage=get_storage('datasets_archives'),
        blank=True,
        null=True,
        upload_to=archives_upload_to,
        max_length=2000,
        verbose_name=_("Archived resources files"))

    def __str__(self):
        return self.title

    @cached_property
    def has_high_value_data(self):
        return self.resources.filter(has_high_value_data=True).exists()

    @cached_property
    def has_table(self):
        return self.resources.filter(has_table=True).exists()

    @cached_property
    def has_map(self):
        return self.resources.filter(has_map=True).exists()

    @cached_property
    def has_chart(self):
        return self.resources.filter(has_chart=True).exists()

    @property
    def comment_editors(self):
        emails = []
        if self.source:
            emails.extend(self.source.emails_list)
        else:
            if self.update_notification_recipient_email:
                emails.append(self.update_notification_recipient_email)
            elif self.modified_by:
                emails.append(self.modified_by.email)
            else:
                emails.extend(user.email
                              for user in self.organization.users.all())
        return emails

    @property
    def comment_mail_recipients(self):
        return [
            config.CONTACT_MAIL,
        ] + self.comment_editors

    @property
    def is_imported(self):
        return bool(self.source)

    @property
    def is_imported_from_ckan(self):
        return self.is_imported and self.source.is_ckan

    @property
    def is_imported_from_xml(self):
        return self.is_imported and self.source.is_xml

    @property
    def institution(self):
        return self.organization

    @property
    def source_title(self):
        return self.source.name if self.source else None

    @property
    def source_type(self):
        return self.source.source_type if self.source else None

    @property
    def source_url(self):
        return self.source.portal_url if self.source else None

    @property
    def formats(self):
        items = [
            x.formats_list for x in self.resources.all() if x.formats_list
        ]
        return sorted(set([item for sublist in items for item in sublist]))

    @property
    def types(self):
        return list(self.resources.values_list('type', flat=True).distinct())

    @property
    def frontend_url(self):
        return f'/dataset/{self.ident}'

    @property
    def frontend_absolute_url(self):
        return self._get_absolute_url(self.frontend_url)

    @property
    def openness_scores(self):
        return list(set(res.openness_score for res in self.resources.all()))

    @property
    def keywords_list(self):
        return [tag.to_dict for tag in self.tags.all()]

    @property
    def keywords(self):
        return self.tags

    @property
    def tags_list_as_str(self):
        return ', '.join(
            sorted([str(tag) for tag in self.tags.all()], key=str.lower))

    def tags_as_str(self, lang):
        return ', '.join(
            sorted([tag.name for tag in self.tags.filter(language=lang)],
                   key=str.lower))

    @property
    def categories_list_as_html(self):
        categories = self.categories.all()
        return self.mark_safe('<br>'.join(
            category.title for category in categories)) if categories else '-'

    @property
    def categories_list_str(self):
        return ', '.join(self.categories.all().values_list('title', flat=True))

    @property
    def license_code(self):
        license_ = self.LICENSE_CC0
        if self.license_condition_source or self.license_condition_modification or self.license_condition_responsibilities:
            license_ = self.LICENSE_CC_BY
        if self.license_chosen and self.license_chosen > license_:
            license_ = self.license_chosen
        return license_

    @property
    def license_name(self):
        return self.LICENSE_CODE_TO_NAME.get(self.license_code)

    @property
    def license_link(self):
        url = settings.LICENSES_LINKS.get(self.license_name)
        return f'{url}legalcode.{get_language()}'

    @property
    def license_description(self):
        return self.license.title if self.license and self.license.title else ''

    @property
    def last_modified_resource(self):
        return self.resources.all().aggregate(Max('modified'))['modified__max']

    last_modified_resource.fget.short_description = _("modified")

    @property
    def is_license_set(self):
        return any([
            self.license, self.license_condition_db_or_copyrighted,
            self.license_condition_modification,
            self.license_condition_original,
            self.license_condition_responsibilities
        ])

    @property
    def followers_count(self):
        return self.users_following.count()

    @property
    def published_resources_count(self):
        return self.resources.count()

    @property
    def visualization_types(self):
        return list(
            set(
                itertools.chain(
                    *[r.visualization_types for r in self.resources.all()])))

    @property
    def model_name(self):
        return self._meta.model_name

    @classmethod
    def accusative_case(cls):
        return _("acc: Dataset")

    @property
    def image_url(self):
        return self.image.url if self.image else ''

    @property
    def image_absolute_url(self):
        return self._get_absolute_url(self.image_url,
                                      use_lang=False) if self.image_url else ''

    @property
    def dataset_logo(self):
        if self.image_absolute_url:
            return self.mark_safe(
                '<a href="%s" target="_blank"><img src="%s" width="%d" alt="%s" /></a>'
                % (self.admin_change_url, self.image_absolute_url, 100,
                   self.image_alt if self.image_alt else ''))
        return ''

    @property
    def computed_downloads_count(self):
        return ResourceDownloadCounter.objects.filter(
            resource__dataset_id=self.pk).aggregate(count_sum=Sum('count'))['count_sum'] or 0\
            if is_enabled('S16_new_date_counters.be') else self.downloads_count

    @property
    def computed_views_count(self):
        return ResourceViewCounter.objects.filter(
            resource__dataset_id=self.pk).aggregate(count_sum=Sum('count'))['count_sum'] or 0\
            if is_enabled('S16_new_date_counters.be') else self.views_count

    def to_rdf_graph(self):
        schema = self.get_rdf_serializer_schema()
        return schema(many=False).dump(self) if schema else None

    def as_sparql_create_query(self):
        g = self.to_rdf_graph()
        data = ''.join([
            f'{s.n3()} {p.n3()} {o.n3()} . '
            for s, p, o in g.triples((None, None, None))
        ])
        namespaces_dict = {prefix: ns for prefix, ns in g.namespaces()}
        return 'INSERT DATA { %(data)s }' % {'data': data}, namespaces_dict

    def clean(self):
        _range = UPDATE_FREQUENCY_NOTIFICATION_RANGES.get(
            self.update_frequency)
        if (_range and self.update_notification_frequency
                and self.update_notification_frequency not in range(
                    _range[0], _range[1] + 1)):
            msg = _('The value must be between %(min)s and %(max)s') % {
                'min': _range[0],
                'max': _range[1]
            }
            raise ValidationError({'update_notification_frequency': msg})

    @property
    def frequency_display(self):
        return dict(UPDATE_FREQUENCY).get(self.update_frequency)

    @property
    def dataset_update_notification_recipient(self):
        return self.update_notification_recipient_email or self.modified_by.email

    @property
    def regions(self):
        resources_ids = list(self.resources.values_list('pk', flat=True))
        return Region.objects.filter(resource__pk__in=resources_ids).distinct()

    @cached_property
    def showcases_published(self):
        return self.showcases.filter(status='published')

    def archive_files(self):
        archive_resources_files.s(dataset_id=self.pk).apply_async(
            countdown=settings.DATASET_ARCHIVE_FILES_TASK_DELAY)

    @classmethod
    def get_license_data(cls, name, lang=None):
        data = None
        if name not in [
                'CC0', 'CCBY', 'CCBY-SA', 'CCBY-NC', 'CCBY-NC-SA', 'CCBY-ND',
                'CCBY-NC-ND'
        ]:
            return data
        with open(os.path.join(settings.DATA_DIR, 'datasets',
                               'licenses.json')) as fp:
            json_data = json.load(fp)
            try:
                lang = lang or get_language()
                data = json_data[lang][name]
                data = SimpleNamespace(id=uuid4(), **data)
            except Exception as exc:
                logger.debug(exc)
        return data

    i18n = TranslationField(fields=("title", "notes", "image_alt"))
    objects = DatasetManager()
    trash = TrashManager()
    tracker = FieldTracker()
    slugify_field = 'title'
    last_modified_resource.fget.short_description = _("modified")

    class Meta:
        verbose_name = _("Dataset")
        verbose_name_plural = _("Datasets")
        db_table = 'dataset'
        default_manager_name = "objects"
        indexes = [
            GinIndex(fields=["i18n"]),
        ]
class ShowcaseProposal(ShowcaseMixin):
    DECISION_CHOICES = (
        ('accepted', _('Proposal accepted')),
        ('rejected', _('Proposal rejected')),
    )
    applicant_email = models.EmailField(verbose_name=_('applicant email'),
                                        blank=True)
    image = models.ImageField(max_length=200,
                              storage=storages.get_storage('showcases'),
                              upload_to='proposals/image/%Y%m%d',
                              blank=True,
                              null=True,
                              verbose_name=_('image URL'))
    illustrative_graphics = models.ImageField(
        max_length=200,
        storage=storages.get_storage('showcases'),
        upload_to='proposals/illustrative_graphics/%Y%m%d',
        blank=True,
        null=True,
        verbose_name=_('illustrative graphics'),
    )
    file = models.FileField(
        verbose_name=_('attachement'),
        storage=storages.get_storage('showcases'),
        upload_to='proposals/file/%Y%m%d',
        max_length=2000,
        blank=True,
        null=True,
    )
    datasets = models.ManyToManyField('datasets.Dataset',
                                      blank=True,
                                      verbose_name=_('datasets'),
                                      related_name='showcase_proposals')
    keywords = ArrayField(models.CharField(max_length=100),
                          verbose_name=_('keywords'),
                          default=list)
    report_date = models.DateField(verbose_name=_('report date'))
    decision = models.CharField(max_length=8,
                                verbose_name=_('decision'),
                                choices=DECISION_CHOICES,
                                blank=True)
    decision_date = models.DateField(verbose_name=_('decision date'),
                                     null=True,
                                     blank=True)
    comment = models.TextField(verbose_name=_('comment'), blank=True)

    showcase = models.OneToOneField('showcases.Showcase',
                                    on_delete=models.SET_NULL,
                                    null=True,
                                    blank=True,
                                    verbose_name=_('showcase'))
    created_by = models.ForeignKey(User,
                                   models.DO_NOTHING,
                                   blank=True,
                                   null=True,
                                   verbose_name=_('created by'),
                                   related_name='showcase_proposals_created')

    modified_by = models.ForeignKey(User,
                                    models.DO_NOTHING,
                                    blank=True,
                                    null=True,
                                    verbose_name=_('modified by'),
                                    related_name='showcase_proposals_modified')

    objects = ShowcaseProposalManager()
    trash = ShowcaseProposalTrashManager()
    i18n = TranslationField()
    tracker = FieldTracker()

    class Meta(ShowcaseMixin.Meta):
        verbose_name = _('Showcase Proposal')
        verbose_name_plural = _('Showcase Proposals')

    @property
    def application_logo(self):
        if self.image_absolute_url:
            return self.mark_safe(
                '<a href="%s" target="_blank"><img src="%s" width="%d" alt="" /></a>'
                % (
                    self.admin_change_url,
                    self.image_absolute_url,
                    100,
                ))
        return ''

    @property
    def applicant_email_link(self):
        if self.applicant_email:
            return self.mark_safe(
                f'<a href="mailto:{self.applicant_email}">{self.applicant_email}</a>'
            )

    @property
    def datasets_links(self):
        queryset = self.datasets.order_by('title')
        res = '<br>'.join([
            f'<a href="{x.frontend_absolute_url}" target="_blank">{x.title}</a>'
            for x in queryset
        ])
        return self.mark_safe(res)

    @property
    def datasets_links_info(self):
        res = '\n'.join([
            obj.frontend_absolute_url
            for obj in self.datasets.order_by('title')
        ])
        return self.mark_safe(res)

    @property
    def external_datasets_links(self):
        res = ''
        for x in self.external_datasets:
            url = x.get('url')
            title = x.get('title')
            if url:
                res += '<a href="{}" target="_blank">{}</a><br>'.format(
                    url, title or url)
        return self.mark_safe(res)

    @property
    def external_datasets_info(self):
        res = ''
        for x in self.external_datasets:
            url = x.get('url')
            title = x.get('title')
            if url:
                res += '{}: {}\n'.format(title or url, url)
        return self.mark_safe(res)

    @cached_property
    def image_absolute_url(self):
        return self._get_absolute_url(self.image.url,
                                      use_lang=False) if self.image else ''

    @property
    def is_accepted(self):
        return self.decision == 'accepted'

    @property
    def is_converted_to_showcase(self):
        return self.showcase and not self.showcase.is_permanently_removed

    @property
    def is_rejected(self):
        return self.decision == 'rejected'

    @property
    def keywords_as_str(self):
        return ','.join([x for x in self.keywords])

    @classmethod
    def accusative_case(cls):
        return _('acc: showcase proposal')

    def convert_to_showcase(self):  # noqa
        created = False
        if self.is_converted_to_showcase:
            return created  # proposal is after convertion already. Do not convert.
        tag_model = apps.get_model('tags.Tag')
        data = model_to_dict(
            self,
            fields=[
                'category', 'notes', 'author', 'url', 'title', 'image',
                'illustrative_graphics', 'datasets', 'external_datasets',
                'keywords', 'created_by', 'modified_by', 'is_mobile_app',
                'is_desktop_app', 'mobile_apple_url', 'mobile_google_url',
                'desktop_windows_url', 'desktop_linux_url',
                'desktop_macos_url', 'license_type', 'file'
            ])
        data['status'] = 'draft'
        data['modified_by_id'] = data.pop('modified_by')
        data['created_by_id'] = data.pop(
            'created_by') or data['modified_by_id']
        image = data.pop('image')
        illustrative_graphics = data.pop('illustrative_graphics')
        file = data.pop('file')
        datasets = data.pop('datasets')
        keywords = data.pop('keywords')
        showcase = Showcase.objects.create(**data)
        if image:
            showcase.image = showcase.save_file(image,
                                                os.path.basename(image.path))
        if illustrative_graphics:
            showcase.illustrative_graphics = showcase.save_file(
                illustrative_graphics,
                os.path.basename(illustrative_graphics.path),
                field_name='illustrative_graphics')
        if file:
            showcase.file = showcase.save_file(file,
                                               os.path.basename(file.path),
                                               field_name='file')
        if image or illustrative_graphics or file:
            showcase.save()
        if datasets:
            showcase.datasets.set(datasets)
        if keywords:
            tag_ids = []
            for name in keywords:
                tag, created = tag_model.objects.get_or_create(
                    name=name,
                    language='pl',
                    defaults={'created_by_id': data['created_by_id']})
                tag_ids.append(tag.id)
            if tag_ids:
                showcase.tags.set(tag_ids)
        self.showcase = showcase
        self.save()
        created = True
        return created

    @classmethod
    def create(cls, data):
        image = data.pop('image', None)
        illustrative_graphics = data.pop('illustrative_graphics', None)
        datasets_ids = data.pop('datasets', [])
        name = cls.slugify(data['title'])
        if image:
            data['image'] = cls.decode_b64_image(image, name)
        if illustrative_graphics:
            data['illustrative_graphics'] = cls.decode_b64_image(
                illustrative_graphics, name)
        obj = cls.objects.create(**data)
        if datasets_ids:
            obj.datasets.set(datasets_ids)
        send_showcase_proposal_mail_task.s(obj.id).apply_async(countdown=1)
        return obj

    @classmethod
    def send_showcase_proposal_mail(cls, obj):
        emails = [
            config.TESTER_EMAIL
        ] if settings.DEBUG and config.TESTER_EMAIL else [config.CONTACT_MAIL]
        context = {'obj': obj, 'host': settings.BASE_URL}
        with translation.override('pl'):
            msg_plain = render_to_string('mails/showcaseproposal.txt', context)
            msg_html = render_to_string('mails/showcaseproposal.html', context)
            mail = EmailMultiAlternatives(
                'Powiadomienie - Nowe zgłoszenie PoCoTo',
                msg_plain,
                from_email=config.NO_REPLY_EMAIL,
                to=emails,
                connection=get_connection(settings.EMAIL_BACKEND),
            )
            mail.mixed_subtype = 'related'
            mail.attach_alternative(msg_html, 'text/html')
            mail.send()
Beispiel #10
0
class Application(ApplicationMixin):
    SIGNALS_MAP = {
        'updated': (generate_thumbnail, core_signals.notify_updated),
        'published': (generate_thumbnail, core_signals.notify_published),
        'restored': (search_signals.update_document_with_related,
                     generate_thumbnail, core_signals.notify_restored),
        'removed': (search_signals.remove_document_with_related,
                    core_signals.notify_removed),
        'pre_m2m_added': (core_signals.notify_m2m_added, ),
        'pre_m2m_removed': (core_signals.notify_m2m_removed, ),
        'pre_m2m_cleaned': (core_signals.notify_m2m_cleaned, ),
        'post_m2m_added': (
            update_application_document,
            search_signals.update_document_related,
        ),
        'post_m2m_removed': (
            update_application_document,
            search_signals.update_document_related,
        ),
        'post_m2m_cleaned': (
            update_application_document,
            search_signals.update_document_related,
        ),
    }

    image = models.ImageField(max_length=200,
                              storage=storages.get_storage('applications'),
                              upload_to='%Y%m%d',
                              blank=True,
                              null=True,
                              verbose_name=_("Image URL"))
    illustrative_graphics = models.ImageField(
        max_length=200,
        storage=storages.get_storage('applications'),
        upload_to='illustrative_graphics/%Y%m%d',
        blank=True,
        null=True,
        verbose_name=_('illustrative graphics'),
    )
    illustrative_graphics_alt = models.CharField(
        max_length=255,
        blank=True,
        verbose_name=_('illustrative graphics alternative text'))
    image_thumb = models.ImageField(
        storage=storages.get_storage('applications'),
        upload_to='%Y%m%d',
        blank=True,
        null=True)
    image_alt = models.CharField(max_length=255,
                                 null=True,
                                 blank=True,
                                 verbose_name=_("Alternative text"))
    datasets = models.ManyToManyField('datasets.Dataset',
                                      db_table='application_dataset',
                                      verbose_name=_('Datasets'),
                                      related_name='applications',
                                      related_query_name="application")
    tags = models.ManyToManyField('tags.Tag',
                                  blank=True,
                                  db_table='application_tag',
                                  verbose_name=_('Tag'),
                                  related_name='applications',
                                  related_query_name="application")

    main_page_position = models.PositiveSmallIntegerField(
        choices=MAIN_PAGE_ORDERING_CHOICES,
        blank=True,
        null=True,
        unique=True,
        verbose_name=_('Positioning on the main page'),
    )

    created_by = models.ForeignKey(User,
                                   models.DO_NOTHING,
                                   blank=False,
                                   editable=False,
                                   null=True,
                                   verbose_name=_("Created by"),
                                   related_name='applications_created')

    modified_by = models.ForeignKey(User,
                                    models.DO_NOTHING,
                                    blank=False,
                                    editable=False,
                                    null=True,
                                    verbose_name=_("Modified by"),
                                    related_name='applications_modified')

    @property
    def frontend_preview_url(self):
        return self._get_absolute_url(f'/application/preview/{self.ident}')

    @cached_property
    def image_url(self):
        url = self.image.url if self.image else ''
        if url:
            return self._get_absolute_url(url, use_lang=False)
        return url

    @cached_property
    def image_absolute_url(self):
        return self._get_absolute_url(self.image.url,
                                      use_lang=False) if self.image else ''

    @cached_property
    def image_thumb_url(self):
        url = self.image_thumb.url if self.image_thumb else ''
        if url:
            return self._get_absolute_url(url, use_lang=False)
        return url

    @cached_property
    def image_thumb_absolute_url(self):
        return self._get_absolute_url(
            self.image_thumb.url, use_lang=False) if self.image_thumb else ''

    @cached_property
    def has_image_thumb(self):
        return bool(self.image_thumb)

    def tags_as_str(self, lang):
        return ', '.join(
            sorted([tag.name for tag in self.tags.filter(language=lang)],
                   key=str.lower))

    @property
    def keywords_list(self):
        return [tag.to_dict for tag in self.tags.all()]

    @property
    def keywords(self):
        return self.tags

    @property
    def preview_link(self):
        return self.mark_safe(
            f'<a href="{self.frontend_preview_url}" class="btn" target="_blank">{_("Preview")}</a>'
        )

    @cached_property
    def users_following_list(self):
        return [user.id for user in self.users_following.all()]

    @property
    def application_logo(self):
        if self.image_thumb_absolute_url or self.image_absolute_url:
            return self.mark_safe(
                '<a href="%s" target="_blank"><img src="%s" width="%d" alt="%s" /></a>'
                % (self.admin_change_url, self.image_thumb_absolute_url
                   or self.image_absolute_url, 100, self.image_alt
                   if self.image_alt else f'Logo aplikacji {self.title}'))
        return ''

    @classmethod
    def accusative_case(cls):
        return _("acc: Application")

    def published_datasets(self):
        return self.datasets.filter(status='published')

    i18n = TranslationField(fields=("title", "notes", 'image_alt',
                                    'illustrative_graphics_alt'))

    objects = SoftDeletableManager()
    trash = TrashManager()
    tracker = FieldTracker()
    slugify_field = 'title'

    class Meta(ApplicationMixin.Meta):
        verbose_name = _("Application")
        verbose_name_plural = _("Applications")
        db_table = "application"
        indexes = [
            GinIndex(fields=["i18n"]),
        ]
Beispiel #11
0
class ApplicationProposal(ApplicationMixin):
    DECISION_CHOICES = (
        ('accepted', _('Proposal accepted')),
        ('rejected', _('Proposal rejected')),
    )
    applicant_email = models.EmailField(verbose_name=_('applicant email'),
                                        blank=True)
    image = models.ImageField(max_length=200,
                              storage=storages.get_storage('applications'),
                              upload_to='proposals/image/%Y%m%d',
                              blank=True,
                              null=True,
                              verbose_name=_('image URL'))
    illustrative_graphics = models.ImageField(
        max_length=200,
        storage=storages.get_storage('applications'),
        upload_to='proposals/illustrative_graphics/%Y%m%d',
        blank=True,
        null=True,
        verbose_name=_('illustrative graphics'),
    )
    datasets = models.ManyToManyField('datasets.Dataset',
                                      blank=True,
                                      verbose_name=_('datasets'),
                                      related_name='application_proposals')
    keywords = ArrayField(models.CharField(max_length=100),
                          verbose_name=_('keywords'),
                          default=list)
    report_date = models.DateField(verbose_name=_('report date'))

    decision = models.CharField(max_length=8,
                                verbose_name=_('decision'),
                                choices=DECISION_CHOICES,
                                blank=True)
    decision_date = models.DateField(verbose_name=_('decision date'),
                                     null=True,
                                     blank=True)
    comment = models.TextField(verbose_name=_('comment'), blank=True)
    application = models.OneToOneField('applications.Application',
                                       on_delete=models.CASCADE,
                                       null=True,
                                       blank=True,
                                       verbose_name=_('application'))
    created_by = models.ForeignKey(
        User,
        models.DO_NOTHING,
        blank=True,
        null=True,
        verbose_name=_('created by'),
        related_name='application_proposals_created')

    modified_by = models.ForeignKey(
        User,
        models.DO_NOTHING,
        blank=True,
        null=True,
        verbose_name=_('modified by'),
        related_name='application_proposals_modified')

    objects = ApplicationProposalManager()
    trash = ApplicationProposalTrashManager()
    i18n = TranslationField()
    tracker = FieldTracker()

    class Meta(ApplicationMixin.Meta):
        verbose_name = _('Application Proposal')
        verbose_name_plural = _('Application Proposals')

    @cached_property
    def image_absolute_url(self):
        return self._get_absolute_url(self.image.url,
                                      use_lang=False) if self.image else ''

    @property
    def application_logo(self):
        if self.image_absolute_url:
            return self.mark_safe(
                '<a href="%s" target="_blank"><img src="%s" width="%d" alt="" /></a>'
                % (
                    self.admin_change_url,
                    self.image_absolute_url,
                    100,
                ))
        return ''

    @property
    def datasets_admin(self):
        objs = self.datasets.order_by('title')
        links = [
            f'<a href="{x.frontend_absolute_url}" target="_blank">{x.title}</a>'
            for x in objs
        ]
        res = '<br>'.join(links)
        return self.mark_safe(res)

    @property
    def external_datasets_admin(self):
        res = ''
        for x in self.external_datasets:
            url = x.get('url')
            title = x.get('title')
            if url:
                res += '<a href="{}" target="_blank">{}</a><br>'.format(
                    url, title or url)
        return self.mark_safe(res)

    @property
    def is_accepted(self):
        return self.decision == 'accepted'

    @property
    def is_rejected(self):
        return self.decision == 'rejected'

    @property
    def keywords_as_str(self):
        return ','.join([x for x in self.keywords])

    @classmethod
    def convert_to_application(cls, app_proposal_id):  # noqa
        proposal = cls.objects.filter(id=app_proposal_id,
                                      application__isnull=True).first()
        if proposal:
            tag_model = apps.get_model('tags.Tag')
            data = model_to_dict(proposal,
                                 fields=[
                                     'notes', 'author', 'url', 'title',
                                     'image', 'illustrative_graphics',
                                     'datasets', 'external_datasets',
                                     'keywords', 'created_by', 'modified_by'
                                 ])
            data['status'] = 'draft'
            data['modified_by_id'] = data.pop('modified_by')
            data['created_by_id'] = data.pop(
                'created_by') or data['modified_by_id']
            image = data.pop('image')
            illustrative_graphics = data.pop('illustrative_graphics')
            datasets = data.pop('datasets')
            keywords = data.pop('keywords')
            application = Application.objects.create(**data)
            if image:
                application.image = application.save_file(
                    image, os.path.basename(image.path))
            if illustrative_graphics:
                application.illustrative_graphics = application.save_file(
                    illustrative_graphics,
                    os.path.basename(illustrative_graphics.path),
                    field_name='illustrative_graphics')
            if image or illustrative_graphics:
                application.save()
            if datasets:
                application.datasets.set(datasets)
            if keywords:
                tag_ids = []
                for name in keywords:
                    tag, created = tag_model.objects.get_or_create(
                        name=name,
                        language='pl',
                        defaults={'created_by_id': data['created_by_id']})
                    tag_ids.append(tag.id)
                if tag_ids:
                    application.tags.set(tag_ids)
            proposal.application = application
            proposal.save()
            return application

    @classmethod
    def create(cls, data):
        image = data.pop('image', None)
        illustrative_graphics = data.pop('illustrative_graphics', None)
        datasets_ids = data.pop('datasets', [])
        name = cls.slugify(data['title'])
        if image:
            data['image'] = cls.decode_b64_image(image, name)
        if illustrative_graphics:
            data['illustrative_graphics'] = cls.decode_b64_image(
                illustrative_graphics, name)
        obj = cls.objects.create(**data)
        if datasets_ids:
            obj.datasets.set(datasets_ids)
        return obj

    @classmethod
    def decode_b64_image(cls, encoded_img, img_name):
        data_parts = encoded_img.split(';base64,')
        img_data = data_parts[-1].encode('utf-8')
        try:
            extension = guess_extension(guess_type(encoded_img)[0])
        except Exception:
            extension = None
        name = f'{img_name}{extension}' if extension else img_name
        try:
            decoded_img = base64.b64decode(img_data)
        except Exception:
            decoded_img = None
        return ContentFile(decoded_img, name=name) if decoded_img else None

    @classmethod
    def accusative_case(cls):
        return _("acc: Application proposal")

    @classmethod
    def send_application_proposal_mail(cls, data):
        dataset_model = apps.get_model('datasets.Dataset')
        conn = get_connection(settings.EMAIL_BACKEND)

        title = data['title']
        img_data = data.get('image')
        illustrative_graphics = data.get('illustrative_graphics')
        img_name = cls.slugify(
            title) if img_data or illustrative_graphics else None

        if img_data:
            _data = img_data.split(';base64,')[-1].encode('utf-8')
            image = MIMEImage(base64.b64decode(_data))
            filename = f"{img_name}.{image.get_content_subtype()}"
            image.add_header('content-disposition',
                             'attachment',
                             filename=filename)
            image.add_header('Content-ID', '<app-logo>')

        if illustrative_graphics:
            _data = illustrative_graphics.split(';base64,')[-1].encode('utf-8')
            illustrative_graphics_img = MIMEImage(base64.b64decode(_data))
            filename = f'{img_name}_illustrative-graphics.{illustrative_graphics_img.get_content_subtype()}'
            illustrative_graphics_img.add_header('content-disposition',
                                                 'attachment',
                                                 filename=filename)
            illustrative_graphics_img.add_header('Content-ID',
                                                 '<illustrative-graphics>')

        datasets = dataset_model.objects.filter(
            id__in=data.get('datasets', []))
        data['datasets'] = '\n'.join(ds.frontend_absolute_url
                                     for ds in datasets)
        data['dataset_links'] = '<br />'.join(
            f"<a href=\"{ds.frontend_absolute_url}\">{ds.title}</a>\n"
            for ds in datasets)

        external_datasets = data.get('external_datasets', [])
        data['external_datasets'] = '\n'.join(
            f"{eds.get('title', '(nienazwany)')}: {eds.get('url', '(nie podano url)')}\n"
            for eds in external_datasets)
        data['external_dataset_links'] = '<br />'.join((
            f"{eds.get('title')}: <a href=\"{eds.get('url')}\">{eds.get('url')}</a>\n"
            if 'url' in eds else eds.get('title')
        ) for eds in external_datasets)
        data['host'] = settings.BASE_URL

        emails = [
            config.TESTER_EMAIL
        ] if settings.DEBUG and config.TESTER_EMAIL else [config.CONTACT_MAIL]
        html_template = 'applicationproposal' if is_enabled(
            'S39_mail_layout.be') else 'propose-application'
        with translation.override('pl'):
            msg_plain = render_to_string('mails/propose-application.txt', data)
            msg_html = render_to_string(f'mails/{html_template}.html', data)
            mail = EmailMultiAlternatives(
                'Zgłoszono propozycję aplikacji {}'.format(
                    title.replace('\n', ' ').replace('\r', '')),
                msg_plain,
                from_email=config.NO_REPLY_EMAIL,
                to=emails,
                connection=conn)
            mail.mixed_subtype = 'related'
            mail.attach_alternative(msg_html, 'text/html')
            if img_data:
                mail.attach(image)
            if illustrative_graphics:
                mail.attach(illustrative_graphics_img)
            mail.send()
Beispiel #12
0
class Newsletter(TimeStampedModel):
    NEWSLETTER_LANGUAGES = (
        ('pl', _('polish')),
        ('en', _('english')),
    )
    NEWSLETTER_STATUS_CHOICES = (
        ('awaits', _('Awaits')),
        ('sent', _('Sent')),
        ('error', _('Error')),
    )
    title = models.CharField(max_length=255, verbose_name=_('title'))
    lang = models.CharField(max_length=7,
                            choices=NEWSLETTER_LANGUAGES,
                            verbose_name=_('language version'))
    planned_sending_date = models.DateField(
        verbose_name=_('planned sending date'))
    sending_date = models.DateTimeField(verbose_name=_('sending date'),
                                        null=True,
                                        blank=True)
    status = models.CharField(max_length=7,
                              choices=NEWSLETTER_STATUS_CHOICES,
                              verbose_name=_('status'),
                              default=NEWSLETTER_STATUS_CHOICES[0][0])
    file = models.FileField(verbose_name=_('file'),
                            storage=storages.get_storage('newsletter'),
                            upload_to='%Y%m%d',
                            max_length=2000)
    created_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        models.DO_NOTHING,
        verbose_name=_('created by'),
        related_name='newsletters_created',
    )
    modified_by = models.ForeignKey(
        settings.AUTH_USER_MODEL,
        models.DO_NOTHING,
        blank=True,
        null=True,
        verbose_name=_('modified by'),
        related_name='newsletters_modified',
    )

    objects = NewsletterQuerySet.as_manager()

    def __str__(self):
        return self.title

    class Meta:
        verbose_name = _('newsletter')
        verbose_name_plural = _('newsletters')
        db_table = 'newsletter'

    @property
    def is_sent(self):
        return self.status == 'sent'

    def clean_fields(self, exclude=None):
        super().clean_fields(exclude=exclude)
        if not self.lang:
            self.lang = get_language()
        if not self.is_sent and self.planned_sending_date and self.planned_sending_date <= now(
        ).date():
            if exclude and 'planned_sending_date' in exclude:
                raise ValidationError(
                    _('Planned sending date must be in future!'))
            else:
                raise ValidationError({
                    'planned_sending_date':
                    _('This date must be in future!')
                })

    @staticmethod
    def _get_required_links(html):
        soup = BeautifulSoup(html, 'html.parser')
        required_links = soup.findAll('a', text='Rezygnacja')
        required_links += soup.findAll('a', text='Resignation')
        return required_links

    def clean(self):
        errors = {}
        if self.file:
            buffer = self.file.read()
            file_mime_type = magic.from_buffer(buffer, mime=True)
            if file_mime_type != 'text/html':
                errors['file'] = _('This is not html file!')
            else:
                try:
                    html = buffer.decode('utf8')
                except UnicodeDecodeError:
                    html = buffer.decode('cp1250')
                required_links = self._get_required_links(html)
                if not required_links:
                    errors['file'] = _(
                        'Resignation link in html is required <a href="#">Resign</a>!'
                    )
        if errors:
            raise ValidationError(errors)

    def send(self):
        with open(self.file.path, 'r') as f:
            html_template = f.read()
            required_links = [
                str(x) for x in self._get_required_links(html_template)
            ]
            for obj in Subscription.objects.filter(
                    # lang=self.lang,
                    is_active=True):
                html_message = str(html_template)
                for link in required_links:
                    updated_link = link.replace(
                        'href="#"',
                        'href="{}"'.format(obj.resign_newsletter_absolute_url))
                    html_message = html_message.replace(link, updated_link)
                send_newsletter_mail.s(self.id, obj.id,
                                       html_message).apply_async(countdown=1)
        self.sending_date = now()
        self.status = 'sent'
        self.save()
class Course(ExtendedModel):
    COURSE_STATES = {
        'planned': 'Planowane',
        'current': 'W trakcie',
        'finished': 'Zakończone',
    }
    title = models.CharField(max_length=300, verbose_name=_('title'))
    notes = models.TextField(verbose_name=_('description'))
    venue = models.CharField(max_length=300, verbose_name=_('venue'))
    participants_number = models.PositiveIntegerField(
        verbose_name=_('number of participants'))
    file = models.FileField(
        verbose_name=_('schedule file'),
        storage=storages.get_storage('courses'),
        upload_to='%Y%m%d',
        max_length=2000,
        null=True,
        blank=True,
    )
    materials_file = models.FileField(
        verbose_name=_('materials file'),
        storage=storages.get_storage('courses_materials'),
        upload_to='%Y%m%d',
        max_length=2000,
        null=True,
        blank=True,
    )

    objects = CourseManager()
    trash = CourseTrashManager()
    i18n = TranslationField()
    tracker = FieldTracker()

    def __str__(self):
        return self.title

    class Meta:
        default_manager_name = 'objects'
        verbose_name = _('course')
        verbose_name_plural = _('courses')

    def sessions(self):
        return self.modules.order_by('start')

    @property
    def start(self):
        return self.modules.earliest('start').start

    @property
    def end(self):
        return self.modules.latest('end').end

    @property
    def file_type(self):
        if self.file:
            _name, _ext = os.path.splitext(self.file.name)
            return _ext[1:]

    @property
    def file_url(self):
        return self._get_absolute_url(self.file.url,
                                      use_lang=False) if self.file else None

    @property
    def materials_file_type(self):
        if self.materials_file:
            _name, _ext = os.path.splitext(self.materials_file.name)
            return _ext[1:]

    @property
    def materials_file_url(self):
        return self._get_absolute_url(
            self.materials_file.url,
            use_lang=False) if self.materials_file else None
Beispiel #14
0
class Organization(ExtendedModel):
    SIGNALS_MAP = {
        'removed':
        (remove_related_datasets, search_signals.remove_document_with_related,
         core_signals.notify_removed),
    }

    title = models.CharField(max_length=100, verbose_name=_('Title'))
    description = models.TextField(blank=True,
                                   null=True,
                                   verbose_name=_('Description'))
    image = models.ImageField(max_length=254,
                              storage=storages.get_storage('organizations'),
                              upload_to='%Y%m%d',
                              blank=True,
                              null=True,
                              verbose_name=_('Image URL'))
    postal_code = models.CharField(max_length=6,
                                   null=True,
                                   verbose_name=_('Postal code'))
    city = models.CharField(max_length=200, null=True, verbose_name=_("City"))
    street_type = models.CharField(max_length=50,
                                   null=True,
                                   verbose_name=_("Street type"))
    street = models.CharField(max_length=200,
                              null=True,
                              verbose_name=_("Street"))
    street_number = models.CharField(max_length=200,
                                     null=True,
                                     blank=True,
                                     verbose_name=_("Street number"))
    flat_number = models.CharField(max_length=200,
                                   null=True,
                                   blank=True,
                                   verbose_name=_("Flat number"))

    email = models.CharField(max_length=300,
                             null=True,
                             verbose_name=_("Email"))
    epuap = models.CharField(max_length=500,
                             null=True,
                             verbose_name=_("EPUAP"))
    fax = models.CharField(max_length=50, null=True, verbose_name=_("Fax"))
    fax_internal = models.CharField(max_length=20,
                                    null=True,
                                    blank=True,
                                    verbose_name=_('int.'))

    institution_type = models.CharField(max_length=50,
                                        choices=INSTITUTION_TYPE_CHOICES,
                                        default=INSTITUTION_TYPE_CHOICES[1][0],
                                        verbose_name=_("Institution type"))
    regon = models.CharField(max_length=20, null=True, verbose_name=_("REGON"))
    tel = models.CharField(max_length=50, null=True, verbose_name=_("Phone"))
    tel_internal = models.CharField(max_length=20,
                                    null=True,
                                    blank=True,
                                    verbose_name=_('int.'))
    website = models.CharField(max_length=200,
                               null=True,
                               verbose_name=_("Website"))

    i18n = TranslationField(fields=('title', 'description', 'slug'))

    created_by = models.ForeignKey(User,
                                   models.DO_NOTHING,
                                   blank=False,
                                   editable=False,
                                   null=True,
                                   verbose_name=_("Created by"),
                                   related_name='organizations_created')
    modified_by = models.ForeignKey(User,
                                    models.DO_NOTHING,
                                    blank=False,
                                    editable=False,
                                    null=True,
                                    verbose_name=_("Modified by"),
                                    related_name='organizations_modified')

    def __str__(self):
        if self.title:
            return self.title
        return self.slug

    def get_url_path(self):
        if self.id:
            try:
                return reverse("admin:applications_application_change",
                               kwargs={"object_id": self.id})
            except NoReverseMatch:
                return ""
        return ""

    @property
    def image_url(self):
        try:
            return self.image.url
        except ValueError:
            return ''

    @property
    def short_description(self):
        clean_text = ""
        if self.description:
            clean_text = ''.join(
                BeautifulSoup(self.description,
                              "html.parser").stripped_strings)
        return clean_text

    @property
    def api_url(self):
        return '/institutions/{}'.format(self.id)

    @property
    def description_html(self):
        return format_html(self.description)

    @property
    def datasets_count(self):
        return self.datasets.count()

    @classmethod
    def accusative_case(cls):
        return _("acc: Institution")

    @property
    def published_datasets(self):
        return self.datasets.filter(status='published')

    @property
    def address_display(self):
        city = ' '.join(i.strip() for i in [self.postal_code, self.city] if i)
        if not city:
            return None
        number = '/'.join(i.strip()
                          for i in [self.street_number, self.flat_number] if i)
        addres_line = city
        if self.street:
            street = ' '.join(i.strip()
                              for i in [self.street_type, self.street, number]
                              if i)
            addres_line = ', '.join(i for i in [addres_line, street] if i)

        return addres_line

    @property
    def phone_display(self):
        if not self.tel:
            return None
        try:
            p = phonenumbers.parse(self.tel, 'PL')
            phone = phonenumbers.format_number(
                p, phonenumbers.PhoneNumberFormat.INTERNATIONAL)
        except phonenumbers.phonenumberutil.NumberParseException:
            return None
        return _(' int. ').join(i.strip() for i in [phone, self.tel_internal]
                                if i)

    @property
    def fax_display(self):
        if not self.fax:
            return None
        try:
            p = phonenumbers.parse(self.fax, 'PL')
            fax = phonenumbers.format_number(
                p, phonenumbers.PhoneNumberFormat.INTERNATIONAL)
        except phonenumbers.phonenumberutil.NumberParseException:
            return None
        return _(' int. ').join(i.strip() for i in [fax, self.fax_internal]
                                if i)

    raw = models.Manager()
    objects = SoftDeletableManager()
    deleted = DeletedManager()

    tracker = FieldTracker()
    slugify_field = 'title'

    short_description.fget.short_description = _("Description")

    class Meta:
        db_table = "organization"
        verbose_name = _("Institution")
        verbose_name_plural = _("Institutions")
        default_manager_name = "objects"
        indexes = [
            GinIndex(fields=["i18n"]),
        ]
class Showcase(ShowcaseMixin):
    MAIN_PAGE_ORDERING_CHOICES = [
        (1, _('First')),
        (2, _('Second')),
        (3, _('Third')),
        (4, _('Fourth')),
    ]
    SIGNALS_MAP = {
        'updated': (generate_thumbnail, core_signals.notify_updated),
        'published': (generate_thumbnail, core_signals.notify_published),
        'restored': (search_signals.update_document_with_related,
                     generate_thumbnail, core_signals.notify_restored),
        'removed': (search_signals.remove_document_with_related,
                    core_signals.notify_removed),
        'pre_m2m_added': (core_signals.notify_m2m_added, ),
        'pre_m2m_removed': (core_signals.notify_m2m_removed, ),
        'pre_m2m_cleaned': (core_signals.notify_m2m_cleaned, ),
        'post_m2m_added': (
            update_showcase_document,
            search_signals.update_document_related,
        ),
        'post_m2m_removed': (
            update_showcase_document,
            search_signals.update_document_related,
        ),
        'post_m2m_cleaned': (
            update_showcase_document,
            search_signals.update_document_related,
        ),
    }

    image = models.ImageField(max_length=200,
                              storage=storages.get_storage('showcases'),
                              upload_to='image/%Y%m%d',
                              blank=True,
                              null=True,
                              verbose_name=_('Image URL'))
    illustrative_graphics = models.ImageField(
        max_length=200,
        storage=storages.get_storage('showcases'),
        upload_to='illustrative_graphics/%Y%m%d',
        blank=True,
        null=True,
        verbose_name=_('illustrative graphics'),
    )
    illustrative_graphics_alt = models.CharField(
        max_length=255,
        blank=True,
        verbose_name=_('illustrative graphics alternative text'))
    image_thumb = models.ImageField(storage=storages.get_storage('showcases'),
                                    upload_to='image_thumb/%Y%m%d',
                                    blank=True,
                                    null=True)
    image_alt = models.CharField(max_length=255,
                                 null=True,
                                 blank=True,
                                 verbose_name=_('Alternative text'))
    file = models.FileField(
        verbose_name=_('attachement'),
        storage=storages.get_storage('showcases'),
        upload_to='file/%Y%m%d',
        max_length=2000,
        blank=True,
        null=True,
    )
    datasets = models.ManyToManyField('datasets.Dataset',
                                      db_table='showcase_dataset',
                                      verbose_name=_('Datasets'),
                                      related_name='showcases',
                                      related_query_name='showcase')
    tags = models.ManyToManyField('tags.Tag',
                                  blank=True,
                                  db_table='showcase_tag',
                                  verbose_name=_('Tag'),
                                  related_name='showcases',
                                  related_query_name='showcase')

    main_page_position = models.PositiveSmallIntegerField(
        choices=MAIN_PAGE_ORDERING_CHOICES,
        blank=True,
        null=True,
        unique=True,
        verbose_name=_('Positioning on the main page'),
    )
    created_by = models.ForeignKey(User,
                                   models.DO_NOTHING,
                                   blank=False,
                                   editable=False,
                                   null=True,
                                   verbose_name=_('Created by'),
                                   related_name='showcases_created')
    modified_by = models.ForeignKey(User,
                                    models.DO_NOTHING,
                                    blank=False,
                                    editable=False,
                                    null=True,
                                    verbose_name=_('Modified by'),
                                    related_name='showcases_modified')
    i18n = TranslationField(fields=('title', 'notes', 'image_alt',
                                    'illustrative_graphics_alt'))

    objects = ShowcaseManager()
    trash = ShowcaseTrashManager()

    tracker = FieldTracker()
    slugify_field = 'title'

    class Meta(ShowcaseMixin.Meta):
        verbose_name = _('Showcase')
        verbose_name_plural = _('Showcases')
        db_table = 'showcase'
        indexes = [
            GinIndex(fields=['i18n']),
        ]

    @property
    def frontend_preview_url(self):
        return self._get_absolute_url(f'/showcase/preview/{self.ident}')

    @cached_property
    def image_url(self):
        url = self.image.url if self.image else ''
        if url:
            return self._get_absolute_url(url, use_lang=False)
        return url

    @cached_property
    def image_absolute_url(self):
        return self._get_absolute_url(self.image.url,
                                      use_lang=False) if self.image else ''

    @cached_property
    def image_thumb_url(self):
        url = self.image_thumb.url if self.image_thumb else ''
        if url:
            return self._get_absolute_url(url, use_lang=False)
        return url

    @cached_property
    def image_thumb_absolute_url(self):
        return self._get_absolute_url(
            self.image_thumb.url, use_lang=False) if self.image_thumb else ''

    @cached_property
    def has_image_thumb(self):
        return bool(self.image_thumb)

    def tags_as_str(self, lang):
        return ', '.join(
            sorted([tag.name for tag in self.tags.filter(language=lang)],
                   key=str.lower))

    @property
    def keywords_list(self):
        return [tag.to_dict for tag in self.tags.all()]

    @property
    def keywords(self):
        return self.tags

    @property
    def preview_link(self):
        return self.mark_safe(
            f'<a href="{self.frontend_preview_url}" class="btn" target="_blank">{_("Preview")}</a>'
        )

    @property
    def application_logo(self):
        if self.image_thumb_absolute_url or self.image_absolute_url:
            return self.mark_safe(
                '<a href="%s" target="_blank"><img src="%s" width="%d" alt="%s" /></a>'
                % (self.admin_change_url, self.image_thumb_absolute_url
                   or self.image_absolute_url, 100, self.image_alt
                   if self.image_alt else f'Logo aplikacji {self.title}'))
        return ''

    @classmethod
    def accusative_case(cls):
        return _('acc: Showcase')

    def published_datasets(self):
        return self.datasets.filter(status='published')

    def generate_logo_thumbnail(self):
        if not self.image:
            self.image_thumb = None
        else:
            image = Image.open(self.image)
            if image.mode not in ('L', 'RGB', 'RGBA'):
                image = image.convert('RGB')

            image.thumbnail(settings.THUMB_SIZE, Image.ANTIALIAS)

            temp_handle = BytesIO()
            image.save(temp_handle, 'png')
            temp_handle.seek(0)

            suf = SimpleUploadedFile(os.path.split(self.image.name)[-1],
                                     temp_handle.read(),
                                     content_type='image/png')
            thumb_name = '.'.join(suf.name.split('.')[:-1]) + "_thumb.png"
            self.image_thumb.save(thumb_name, suf, save=False)
class Category(ExtendedModel):
    SIGNALS_MAP = {
        'updated': (update_related_datasets,),
        'published': (update_related_datasets,),
        'restored': (update_related_datasets,),
        'removed': (null_in_related_datasets, rdf_signals.update_related_graph),
    }
    code = models.CharField(max_length=100, verbose_name=_("Code"))
    title = models.CharField(max_length=100, verbose_name=_("Title"))
    description = models.TextField(null=True, verbose_name=_("Description"))
    color = models.CharField(max_length=20, default="#000000", null=True, verbose_name=_("Color"))
    image = models.ImageField(
        max_length=200, storage=storages.get_storage('common'),
        upload_to='', blank=True, null=True, verbose_name=_("Image URL")
    )
    created_by = models.ForeignKey(
        User,
        models.DO_NOTHING,
        blank=False,
        editable=False,
        null=True,
        verbose_name=_("Created by"),
        related_name='categories_created'
    )
    modified_by = models.ForeignKey(
        User,
        models.DO_NOTHING,
        blank=False,
        editable=False,
        null=True,
        verbose_name=_("Modified by"),
        related_name='categories_modified'
    )

    @classmethod
    def accusative_case(cls):
        return _("acc: Category")

    def __str__(self):
        return self.title_i18n

    @property
    def image_url(self):
        if not self.image or not self.image.url:
            return None
        return '{}{}'.format(settings.BASE_URL, self.image.url)

    i18n = TranslationField(fields=("title", "description"))

    objects = SoftDeletableManager()
    trash = TrashManager()

    tracker = FieldTracker()
    slugify_field = 'title'

    class Meta:
        db_table = "category"
        verbose_name = _("Category")
        verbose_name_plural = _("Categories")
        default_manager_name = "objects"
        indexes = [GinIndex(fields=["i18n"]), ]
Beispiel #17
0
def chart_thumb_path(user, slot=1):
    storage = storages.get_storage('chart_thumbs')
    delta = timezone.timedelta(days=36500)
    token = user._get_or_create_token(2, expiration_delta=delta)
    return storage.path(f'{token}-{slot}.png')