"""Table classes and column helpers for sample and measurement listings."""
import django_tables2 as tables
from controlled_vocabularies.fields import MultiConceptField
from django.core.exceptions import FieldDoesNotExist
from django.utils.html import format_html, format_html_join
from django.utils.translation import gettext_lazy as _
from easy_icons import icon
from fairdm.utils.choices import Visibility
[docs]
def render_concept_many_to_many(value):
"""Render a concept many-to-many value as comma-separated links.
Args:
value: The related manager for the concept field.
Returns:
HTML with one link per concept, or an empty string when there are none.
"""
if not value:
return ""
return format_html_join(
", ",
'<a href="{}">{}</a>',
((concept.uri, concept.display_label()) for concept in value.all()),
)
field_map = {
"CharField": "char",
"TextField": "char",
"IntegerField": "num",
"BigIntegerField": "num",
"PositiveIntegerField": "num",
"PositiveSmallIntegerField": "num",
"SmallIntegerField": "num",
"BooleanField": "bool",
"DateField": "date",
"DateTimeField": "datetime",
"TimeField": "time",
"DecimalField": "num",
"FloatField": "num",
"ForeignKey": "rel",
"ManyToManyField": "rel",
}
[docs]
class BaseTable(tables.Table):
"""Base table for all listings, adding type-based column classes and header tooltips.
Args:
*args: Passed to ``django_tables2.Table``.
**kwargs: Passed to ``django_tables2.Table``.
Attributes:
id: The hidden UUID column.
dataset: The dataset icon column.
"""
id = tables.Column(verbose_name="UUID", visible=False)
dataset = tables.Column(orderable=False, verbose_name="")
[docs]
def render_dataset(self, value):
"""Link the dataset icon unless the dataset is private.
A published dataset can still be private, and its records stay in the listing
without a link to a page the visitor cannot read.
"""
if value.visibility != Visibility.PRIVATE:
return format_html(
'<a href="{}">{}</a>', value.get_absolute_url(), icon("dataset")
)
return icon("dataset")
[docs]
def value_dataset(self, value):
"""Export the dataset's UUID."""
return value.uuid
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.base_columns["id"].visible = False
self.configure_column_attrs()
self.update_concept_field_render_methods()
[docs]
def update_concept_field_render_methods(self):
"""Render concept many-to-many columns as links."""
for c in self.columns.columns.values():
try:
field = self._meta.model._meta.get_field(c.accessor)
except FieldDoesNotExist:
continue
if isinstance(field, MultiConceptField):
c.render = render_concept_many_to_many
[docs]
class SampleTable(BaseTable):
"""Listing table for samples."""
name = tables.Column(linkify=True)
# A geometry field has no `location__y` lookup to order by.
latitude = tables.Column(
accessor="location.y", verbose_name=_("Latitude"), orderable=False
)
longitude = tables.Column(
accessor="location.x", verbose_name=_("Longitude"), orderable=False
)
[docs]
class MeasurementTable(BaseTable):
"""Listing table for measurements."""
name = tables.Column(linkify=True, verbose_name=_("Name"))
sample = tables.Column()
# The queryset carries `effective_location` from `with_effective_location(user)`: the
# measurement's own point, else its sample's for a viewer who may open the sample.
latitude = tables.Column(
accessor="effective_location.y", verbose_name=_("Latitude"), orderable=False
)
longitude = tables.Column(
accessor="effective_location.x", verbose_name=_("Longitude"), orderable=False
)
[docs]
def render_sample(self, value):
"""Link the sample only when its dataset is published."""
if value.dataset.published:
return format_html('<a href="{}">{}</a>', value.get_absolute_url(), value)
return _("Unpublished")