Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/release/RELEASE-NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html

### What's new
* [OSDEV-2185](https://opensupplyhub.atlassian.net/browse/OSDEV-2185) - Enhanced partner field display on production location profiles by adding a `source_by` field to the `PartnerField` model. This allows administrators to provide rich text descriptions of data sources. The source information is displayed on the facility details page below each partner field value, supporting HTML formatting for links, emphasis, and lists. Updated the facility index serializer to include `source_by` in the partner fields response only when the field contains content.
* [OSDEV-2199](https://opensupplyhub.atlassian.net/browse/OSDEV-2199) - Added `unit` and `label` metadata from `PartnerField` to the serialized partner fields payload. Production Location detail pages now render the `unit` inline with field values and display custom partner field `label`.

### Release instructions
* Ensure that the following commands are included in the `post_deployment` command:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ def __init__(self,
self.fields: list = ['id', 'is_verified', 'value', 'created_at',
'updated_at', 'contributor_name',
'contributor_id', 'value_count', 'is_from_claim',
'field_name', 'verified_count', 'source_by']
'field_name', 'verified_count', 'source_by',
'unit', 'label']
self.data: list = []

if exclude_fields:
Expand All @@ -28,35 +29,33 @@ def __init__(self,
self._serialize_extended_field_list()

def _serialize_extended_field_list(self) -> None:
field_serializers = {
'created_at': self._get_created_at,
'updated_at': self._get_updated_at,
'contributor_name': self._get_contributor_name,
'contributor_id': self._get_contributor_id,
'is_from_claim': self._get_is_from_claim,
'verified_count': self._get_verified_count,
}
context_overrides = {'source_by', 'unit', 'label'}

for extended_field in self.extended_field_list:
serialized_extended_field = {}

for field in self.fields:
if field == 'created_at':
serialized_extended_field[field] = \
self._get_created_at(extended_field)
elif field == 'updated_at':
serialized_extended_field[field] = \
self._get_updated_at(extended_field)
elif field == 'contributor_name':
serialized_extended_field[field] = \
self._get_contributor_name(extended_field)
elif field == 'contributor_id':
serialized_extended_field[field] = \
self._get_contributor_id(extended_field)
elif field == 'is_from_claim':
serialized_extended_field[field] = \
self._get_is_from_claim(extended_field)
elif field == 'verified_count':
serialized_extended_field[field] = \
self._get_verified_count(extended_field)
elif (field == 'source_by' and
self.context.get('source_by', None) is not None):
serialized_extended_field[field] = \
self.context.get('source_by')
else:
serialized_extended_field[field] = extended_field.get(
field)
if field in field_serializers:
serialized_extended_field[field] = field_serializers[field](
extended_field
)
continue

if field in context_overrides:
context_value = self.context.get(field)
if context_value is not None:
serialized_extended_field[field] = context_value
continue

serialized_extended_field[field] = extended_field.get(field)

self.data.append(serialized_extended_field)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ def __serialize_and_sort_partner_fields(
for field in partner_fields:
field_name = field.name
source_by = field.source_by
unit = field.unit
label = field.label
fields = grouped_fields.get(field_name, [])
if not fields:
continue
Expand All @@ -115,7 +117,9 @@ def __serialize_and_sort_partner_fields(
context={
'user_can_see_detail': user_can_see_detail,
'embed_mode_active': embed_mode_active,
'source_by': source_by
'source_by': source_by,
'unit': unit,
'label': label
},
exclude_fields=(
['created_at'] if not use_main_created_at else []
Expand Down
156 changes: 156 additions & 0 deletions src/django/api/tests/test_facility_index_serializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,3 +218,159 @@ def test_partner_fields_source_by_is_none_when_not_set(self):
test_data_field[0]['source_by'],
None
)

def test_partner_fields_includes_unit(self):
self.partner_field_1.unit = 'kg'
self.partner_field_1.save()

extended_field = ExtendedField.objects.create(
facility=self.facility,
field_name='test_data_field',
value={'raw_value': 'Test Value'},
contributor=self.contrib_one
)

facility_index = FacilityIndex.objects.get(id=self.facility.id)
facility_index.extended_fields.append({
'id': extended_field.id,
'field_name': 'test_data_field',
'value': {'raw_value': 'Test Value'},
'contributor': {
'id': self.contrib_one.id,
'name': self.contrib_one.name,
'is_verified': self.contrib_one.is_verified,
},
'created_at': extended_field.created_at.isoformat(),
'updated_at': extended_field.updated_at.isoformat(),
'is_verified': False,
'facility_list_item_id': None,
'should_display_association': True,
'value_count': 1,
})
facility_index.save()
facility_index.refresh_from_db()

data = FacilityIndexSerializer(facility_index).data
partner_fields = data["properties"]["partner_fields"]
test_data_field = partner_fields['test_data_field']

self.assertEqual(len(test_data_field), 1)
self.assertIn('unit', test_data_field[0])
self.assertEqual(test_data_field[0]['unit'], 'kg')

def test_partner_fields_unit_is_none_when_not_set(self):
self.partner_field_1.unit = ''
self.partner_field_1.save()

extended_field = ExtendedField.objects.create(
facility=self.facility,
field_name='test_data_field',
value={'raw_value': 'Test Value'},
contributor=self.contrib_one
)

facility_index = FacilityIndex.objects.get(id=self.facility.id)
facility_index.extended_fields.append({
'id': extended_field.id,
'field_name': 'test_data_field',
'value': {'raw_value': 'Test Value'},
'contributor': {
'id': self.contrib_one.id,
'name': self.contrib_one.name,
'is_verified': self.contrib_one.is_verified,
},
'created_at': extended_field.created_at.isoformat(),
'updated_at': extended_field.updated_at.isoformat(),
'is_verified': False,
'facility_list_item_id': None,
'should_display_association': True,
'value_count': 1,
})
facility_index.save()
facility_index.refresh_from_db()

data = FacilityIndexSerializer(facility_index).data
partner_fields = data["properties"]["partner_fields"]
test_data_field = partner_fields['test_data_field']

self.assertEqual(len(test_data_field), 1)
self.assertIn('unit', test_data_field[0])
self.assertEqual(test_data_field[0]['unit'], '')

def test_partner_fields_includes_label(self):
self.partner_field_1.label = 'Emissions Data'
self.partner_field_1.save()

extended_field = ExtendedField.objects.create(
facility=self.facility,
field_name='test_data_field',
value={'raw_value': 'Test Value'},
contributor=self.contrib_one
)

facility_index = FacilityIndex.objects.get(id=self.facility.id)
facility_index.extended_fields.append({
'id': extended_field.id,
'field_name': 'test_data_field',
'value': {'raw_value': 'Test Value'},
'contributor': {
'id': self.contrib_one.id,
'name': self.contrib_one.name,
'is_verified': self.contrib_one.is_verified,
},
'created_at': extended_field.created_at.isoformat(),
'updated_at': extended_field.updated_at.isoformat(),
'is_verified': False,
'facility_list_item_id': None,
'should_display_association': True,
'value_count': 1,
})
facility_index.save()
facility_index.refresh_from_db()

data = FacilityIndexSerializer(facility_index).data
partner_fields = data["properties"]["partner_fields"]
test_data_field = partner_fields['test_data_field']

self.assertEqual(len(test_data_field), 1)
self.assertIn('label', test_data_field[0])
self.assertEqual(test_data_field[0]['label'], 'Emissions Data')

def test_partner_fields_label_is_empty_when_not_set(self):
self.partner_field_1.label = ''
self.partner_field_1.save()

extended_field = ExtendedField.objects.create(
facility=self.facility,
field_name='test_data_field',
value={'raw_value': 'Test Value'},
contributor=self.contrib_one
)

facility_index = FacilityIndex.objects.get(id=self.facility.id)
facility_index.extended_fields.append({
'id': extended_field.id,
'field_name': 'test_data_field',
'value': {'raw_value': 'Test Value'},
'contributor': {
'id': self.contrib_one.id,
'name': self.contrib_one.name,
'is_verified': self.contrib_one.is_verified,
},
'created_at': extended_field.created_at.isoformat(),
'updated_at': extended_field.updated_at.isoformat(),
'is_verified': False,
'facility_list_item_id': None,
'should_display_association': True,
'value_count': 1,
})
facility_index.save()
facility_index.refresh_from_db()

data = FacilityIndexSerializer(facility_index).data
partner_fields = data["properties"]["partner_fields"]
test_data_field = partner_fields['test_data_field']

self.assertEqual(len(test_data_field), 1)
self.assertIn('label', test_data_field[0])
self.assertEqual(test_data_field[0]['label'], '')
42 changes: 42 additions & 0 deletions src/react/src/__tests__/components/FacilityDetailsDetail.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ describe('FacilityDetailsDetail', () => {
detailsContainer: 'detailsContainer',
primaryText: 'primaryText',
sourceText: 'sourceText',
unitText: 'unitText',
},
};

Expand All @@ -75,6 +76,7 @@ describe('FacilityDetailsDetail', () => {
detailsContainer: 'detailsContainer',
primaryText: 'primaryText',
sourceText: 'sourceText',
unitText: 'unitText',
},
};

Expand All @@ -94,6 +96,7 @@ describe('FacilityDetailsDetail', () => {
detailsContainer: 'detailsContainer',
primaryText: 'primaryText',
sourceText: 'sourceText',
unitText: 'unitText',
},
};

Expand All @@ -113,6 +116,7 @@ describe('FacilityDetailsDetail', () => {
detailsContainer: 'detailsContainer',
primaryText: 'primaryText',
sourceText: 'sourceText',
unitText: 'unitText',
},
};

Expand All @@ -121,5 +125,43 @@ describe('FacilityDetailsDetail', () => {
const sourceElement = container.querySelector('.sourceText');
expect(sourceElement).not.toBeInTheDocument();
});

test('renders unit text inline with primary value', () => {
const props = {
primary: '100',
unit: 'kg',
classes: {
detailsContainer: 'detailsContainer',
primaryText: 'primaryText',
unitText: 'unitText',
},
};

const { container } = render(<FacilityDetailsDetail {...props} />);

const primaryElement = container.querySelector('.primaryText');
expect(primaryElement).toBeInTheDocument();
expect(primaryElement.textContent).toBe('100kg');

const unitElement = container.querySelector('.unitText');
expect(unitElement).toBeInTheDocument();
expect(unitElement.textContent).toBe('kg');
});

test('does not render unit text when unit is null', () => {
const props = {
primary: 'Test Primary',
unit: null,
classes: {
detailsContainer: 'detailsContainer',
primaryText: 'primaryText',
unitText: 'unitText',
},
};

const { container } = render(<FacilityDetailsDetail {...props} />);

expect(container.querySelector('.unitText')).not.toBeInTheDocument();
});
});

Loading
Loading