Skip to content

Commit

Permalink
[OSDEV-1117] Contribution record integration (#454)
Browse files Browse the repository at this point in the history
Fix [OSDEV-1117](https://opensupplyhub.atlassian.net/browse/OSDEV-1117)

1. Connect `GET api/v1/moderation-events/{moderation_id}/`.
2. Connect `GET
api/v1/production-locations?name={productionLocationName}&country={countryCode}&address={address}`
to get potential matches using OpenSearch engine.
3. Connect `PATCH /v1/moderation-events/{moderation_id}/` (using for
Reject button).
4. Connect `POST
/v1/moderation-events/{moderation_id}/production-locations/` (using for
Create New Location button).
5. Connect `PATCH
/v1/moderation-events/{moderation_id}/production-locations/{os_id}/`
(using for Confirm potential match button).
6. UI improvements: added toast component on moderation event update and
backdrop to prevent accident clicks on other buttons while updation
process.
7. Apply Django Signal for moderation-events index.
8. Update FE tests.
9. Add integration tests.

[OSDEV-1117]:
https://opensupplyhub.atlassian.net/browse/OSDEV-1117?atlOrigin=eyJpIjoiNWRkNTljNzYxNjVmNDY3MDlhMDU5Y2ZhYzA5YTRkZjUiLCJwIjoiZ2l0aHViLWNvbS1KU1cifQ
  • Loading branch information
VadimKovalenkoSNF authored Jan 9, 2025
1 parent 1155fa2 commit 9b95531
Show file tree
Hide file tree
Showing 21 changed files with 1,269 additions and 429 deletions.
10 changes: 10 additions & 0 deletions doc/release/RELEASE-NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,16 @@ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html

### What's new
* [OSDEV-40](https://opensupplyhub.atlassian.net/browse/OSDEV-40) - Created new page for `/contribute` to choose between multiple & single location upload. Replaced current multiple list upload to `/contribute/multiple-locations`. Changed `Upload Data` to `Add Data` text.
* [OSDEV-1117](https://opensupplyhub.atlassian.net/browse/OSDEV-1117) - Implemented integration of Contribution Record Page (`/dashboard/moderation-queue/contribution-record/{moderation_id}`):
- Connected GET `api/v1/moderation-events/{moderation_id}/`.
- Connected GET `api/v1/production-locations?name={productionLocationName}&country={countryCode}&address={address}` to get potential matches using OpenSearch engine.
- Connected PATCH `/v1/moderation-events/{moderation_id}/` (for Reject button).
- Connected POST `/v1/moderation-events/{moderation_id}/production-locations/` (for Create New Location button).
- Connected PATCH `/v1/moderation-events/{moderation_id}/production-locations/{os_id}/` (for Confirm potential match button).
- UI improvements:
- Added a toast component to display notifications during moderation event updates.
- Introduced a backdrop to prevent accidental clicks on other buttons during the update process.
- Applied Django Signal for moderation-events OpenSearch index.
* [OSDEV-1524](https://opensupplyhub.atlassian.net/browse/OSDEV-1524) - Updated salutations in automated emails to ensure a consistent and professional experience of communication from OS Hub.
* [OSDEV-1129](https://opensupplyhub.atlassian.net/browse/OSDEV-1129) - The UI for the results page for name and address search was implemented. It includes the following screens:
* Successful Search: If the search is successful, the results screen displays a list of production locations. Each item includes the following information about the production location: name, OS ID, address, and country name. Users can either select a specific production location or press the "I don’t see my Location" button, which triggers a confirmation dialog window.
Expand Down
24 changes: 16 additions & 8 deletions src/django/api/management/commands/make_token.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,21 @@

class Command(BaseCommand):
help = ('Usage: Create an API token for the user during the database '
'reset.')
'reset, and set admin rights.')

def handle(self, *args, **options):
call_command('shell',
'-c',
("from rest_framework.authtoken.models import Token; "
"from api.models import User; token = "
"Token.objects.create(user=User.objects.get(id=2),"
"key='1d18b962d6f976b0b7e8fcf9fcc39b56cf278051'); "
"print('Token for [email protected]'); print(token)"))
call_command(
'shell',
'-c',
(
"from rest_framework.authtoken.models import Token;"
"from api.models import User;"
"user = User.objects.get(id=2);"
"user.is_staff = True;"
"user.is_superuser = True;"
"user.save();"
"token = Token.objects.create(user=user,"
"key='1d18b962d6f976b0b7e8fcf9fcc39b56cf278051');"
"print(f'Token for {user.email}: {token.key}')"
)
)
80 changes: 61 additions & 19 deletions src/django/api/signals.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import logging
import json

from django.db.models.signals import post_delete
from django.db.models.signals import post_delete, post_save
from django.dispatch import receiver
from opensearchpy.exceptions import ConnectionError

from api.models.facility.facility import Facility
from api.models.moderation_event import ModerationEvent
from api.services.opensearch.opensearch import OpenSearchServiceConnection
from oar.rollbar import report_error_to_rollbar
from api.views.v1.index_names import OpenSearchIndexNames
Expand All @@ -14,31 +15,72 @@
log = logging.getLogger(__name__)


def signal_error_notifier(error_log_message, response):
log.error(error_log_message)
report_error_to_rollbar(
message=error_log_message,
extra_data={
'response_result': json.dumps(response),
}
)


@receiver(post_delete, sender=Facility)
def location_post_delete_handler_for_opensearch(sender, **kwargs):
def location_post_delete_handler_for_opensearch(instance, **kwargs):
opensearch = OpenSearchServiceConnection()
location_instance = kwargs.get('instance')
try:
response = opensearch.client.delete(
index=OpenSearchIndexNames.PRODUCTION_LOCATIONS_INDEX,
id=location_instance.id
id=instance.id,
)
except ConnectionError:
log.error(
'[Location Deletion] Lost connection to OpenSearch cluster.'
)
raise

if response and response.get('result') == 'not_found':
error_log_message = (
"[Location Deletion] Facility not found "
"in OpenSearch, indicating "
"data inconsistency."
)
signal_error_notifier(error_log_message, response)


@receiver(post_save, sender=ModerationEvent)
def moderation_event_update_handler_for_opensearch(
instance,
created,
**kwargs
):
if created:
return

opensearch = OpenSearchServiceConnection()
try:
response = opensearch.client.update(
index=OpenSearchIndexNames.MODERATION_EVENTS_INDEX,
id=str(instance.uuid),
body={
"doc": {
"uuid": str(instance.uuid),
"status": str(instance.status),
"os": instance.os.id if instance.os else None
}
},
)
except ConnectionError:
log.error(('[Location Deletion] The Django app lost the connection '
'with the OpenSearch cluster.'))
log.error(
"[Moderation Event Updating] "
"Lost connection to OpenSearch cluster."
)
raise

if (response and response.get('result') == 'not_found'):
log.error(("[Location Deletion] The same location wasn't found in the "
'OpenSearch data store, indicating critical data '
'inconsistency.'))
report_error_to_rollbar(
message=(
"[Location Deletion] The same location wasn't found in the "
'OpenSearch data store, indicating critical data '
'inconsistency.'
),
extra_data={
'response_result': json.dumps(response),
}
if response and response.get('result') == 'not_found':
error_log_message = (
"[Moderation Event Updating] "
"ModerationEvent not found in OpenSearch, "
"indicating data inconsistency."
)
signal_error_notifier(error_log_message, response)
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from django.test import override_settings
from django.utils.timezone import now
from django.db.models.signals import post_save

from rest_framework.test import APITestCase

Expand All @@ -10,11 +11,20 @@
from api.models.nonstandard_field import NonstandardField
from api.models.source import Source

from api.signals import moderation_event_update_handler_for_opensearch


@override_settings(DEBUG=True)
class BaseModerationEventsProductionLocationTest(APITestCase):
def setUp(self):
super().setUp()
# Disconnect moderation event save propagation to
# OpenSearch cluster, as it is outside the scope
# of Django unit testing.
post_save.disconnect(
moderation_event_update_handler_for_opensearch,
ModerationEvent
)

self.email = "[email protected]"
self.password = "example123"
Expand Down
Loading

0 comments on commit 9b95531

Please sign in to comment.