Skip to content

Commit 5404e47

Browse files
Merge pull request #5879 from uktrade/feature/TET-812-ingest-stova-attendees
Feature/tet 812 ingest stova attendees
2 parents 0642315 + e392cca commit 5404e47

14 files changed

+484
-1
lines changed

datahub/cleanup/management/commands/delete_old_records.py

+2
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ class Command(BaseCleanupCommand):
8181
Company._meta.get_field('wins'): (),
8282
Company._meta.get_field('legacy_wins'): (),
8383
Company._meta.get_field('eyb_leads'): (),
84+
Company._meta.get_field('stova_attendee'): (),
8485
Company._meta.get_field('great_export_enquiries'): (),
8586
},
8687
# We want to delete the relations below along with any expired companies
@@ -109,6 +110,7 @@ class Command(BaseCleanupCommand):
109110
Contact._meta.get_field('interactions'): (),
110111
Contact._meta.get_field('investment_projects'): (),
111112
Contact._meta.get_field('great_export_enquiries'): (),
113+
Contact._meta.get_field('stova_attendee'): (),
112114
Contact._meta.get_field('orders'): (),
113115
Contact._meta.get_field('referrals'): (),
114116
Contact._meta.get_field('pipeline_items_m2m'): (),

datahub/cleanup/management/commands/delete_orphans.py

+2
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class Command(BaseCleanupCommand):
2626
excluded_relations=(
2727
Contact._meta.get_field('wins'),
2828
Contact._meta.get_field('great_export_enquiries'),
29+
Contact._meta.get_field('stova_attendee'),
2930
),
3031
),
3132
'company.Company': ModelCleanupConfig(
@@ -36,6 +37,7 @@ class Command(BaseCleanupCommand):
3637
Company._meta.get_field('export_countries'),
3738
Company._meta.get_field('export_countries_history'),
3839
Company._meta.get_field('great_export_enquiries'),
40+
Company._meta.get_field('stova_attendee'),
3941
Company._meta.get_field('pipeline_list_items'),
4042
Company._meta.get_field('new_export_interaction_reminders'),
4143
Company._meta.get_field('no_recent_export_interaction_reminders'),

datahub/cleanup/test/commands/test_delete_old_records.py

+17
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@
4040
CompanyActivityInvestmentProjectFactory,
4141
CompanyActivityOmisOrderFactory,
4242
GreatExportEnquiryFactory,
43+
StovaAttendeeFactory,
4344
)
4445
from datahub.company_referral.test.factories import (
4546
CompanyReferralFactory,
@@ -230,6 +231,12 @@
230231
'expired_objects_kwargs': [],
231232
'unexpired_objects_kwargs': [],
232233
},
234+
{
235+
'factory': StovaAttendeeFactory,
236+
'field': 'company',
237+
'expired_objects_kwargs': [],
238+
'unexpired_objects_kwargs': [],
239+
},
233240
{
234241
'factory': EYBLeadFactory,
235242
'field': 'company',
@@ -452,6 +459,16 @@
452459
},
453460
],
454461
},
462+
{
463+
'factory': StovaAttendeeFactory,
464+
'field': 'contact',
465+
'expired_objects_kwargs': [],
466+
'unexpired_objects_kwargs': [
467+
{
468+
'created_on': CONTACT_DELETE_BEFORE_DATETIME - relativedelta(days=1),
469+
},
470+
],
471+
},
455472
{
456473
'factory': OrderFactory,
457474
'field': 'contact',

datahub/cleanup/test/commands/test_delete_orphaned_versions.py

+2
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
CompanyActivityIngestedFileFactory,
2626
CompanyActivityInteractionFactory,
2727
GreatExportEnquiryFactory,
28+
StovaAttendeeFactory,
2829
)
2930
from datahub.company_referral.test.factories import (
3031
CompanyReferralFactory,
@@ -77,6 +78,7 @@
7778
'company_activity.CompanyActivity': CompanyActivityInteractionFactory,
7879
'company_activity.GreatExportEnquiry': GreatExportEnquiryFactory,
7980
'company_activity.IngestedFile': CompanyActivityIngestedFileFactory,
81+
'company_activity.StovaAttendee': StovaAttendeeFactory,
8082
'company_list.CompanyListItem': CompanyListItemFactory,
8183
'company_list.PipelineItem': PipelineItemFactory,
8284
'company_referral.CompanyReferral': CompanyReferralFactory,

datahub/company/merge_company.py

+3-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@
2525
Objective,
2626
OneListCoreTeamMember,
2727
)
28-
from datahub.company_activity.models import CompanyActivity, GreatExportEnquiry
28+
from datahub.company_activity.models import CompanyActivity, GreatExportEnquiry, StovaAttendee
2929
from datahub.company_referral.models import CompanyReferral
3030
from datahub.dnb_api.utils import _get_rollback_version
3131
from datahub.export_win.models import LegacyExportWinsToDataHubCompany
@@ -72,6 +72,7 @@
7272
Objective.company.field,
7373
OneListCoreTeamMember.company.field,
7474
Order.company.field,
75+
StovaAttendee.company.field,
7576
Task.company.field,
7677

7778
# Merging is allowed if the source company has export countries, but note that
@@ -117,6 +118,7 @@
117118
MergeConfiguration(Order, ('company',), Company),
118119
MergeConfiguration(NewExportInteractionReminder, ('company',), Company),
119120
MergeConfiguration(NoRecentExportInteractionReminder, ('company',), Company),
121+
MergeConfiguration(StovaAttendee, ('company',), Company),
120122
MergeConfiguration(Task, ('company',), Company),
121123
MergeConfiguration(CompanyListItem, ('company',), Company, company_list_item_updater),
122124
MergeConfiguration(PipelineItem, ('company',), Company, pipeline_item_updater),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Generated by Django 4.2.17 on 2024-12-31 11:18
2+
3+
from django.db import migrations, models
4+
import django.db.models.deletion
5+
import uuid
6+
7+
8+
class Migration(migrations.Migration):
9+
10+
dependencies = [
11+
('company', '0144_update_company_export_experience'),
12+
('company_activity', '0021_match_ingested_stova_fields'),
13+
]
14+
15+
operations = [
16+
migrations.CreateModel(
17+
name='StovaAttendee',
18+
fields=[
19+
('id', models.UUIDField(default=uuid.uuid4, primary_key=True, serialize=False)),
20+
('stova_attendee_id', models.IntegerField(unique=True)),
21+
('stova_event_id', models.IntegerField(unique=True)),
22+
('created_by', models.CharField(max_length=255)),
23+
('created_date', models.DateTimeField()),
24+
('modified_by', models.CharField(max_length=255)),
25+
('modified_date', models.DateTimeField()),
26+
('email', models.CharField(max_length=255)),
27+
('first_name', models.CharField(max_length=255)),
28+
('last_name', models.CharField(max_length=255)),
29+
('attendee_questions', models.CharField(max_length=255)),
30+
('company_name', models.CharField(max_length=255)),
31+
('category', models.CharField(max_length=255)),
32+
('registration_status', models.CharField(max_length=255)),
33+
('virtual_event_attendance', models.CharField(max_length=255)),
34+
('language', models.CharField(max_length=255)),
35+
('last_lobby_login', models.DateTimeField()),
36+
('created_on', models.DateTimeField(auto_now_add=True)),
37+
('company', models.ForeignKey(blank=True, help_text='If a company match can be found from company_name, the relation is added.', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='stova_attendee', to='company.company')),
38+
('contact', models.ForeignKey(blank=True, help_text='If a contact match can be found from the email, the relation is added.', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='stova_attendee', to='company.contact')),
39+
],
40+
),
41+
]
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
from datahub.company_activity.models.company_activity import CompanyActivity
22
from datahub.company_activity.models.great import GreatExportEnquiry
33
from datahub.company_activity.models.ingested_file import IngestedFile
4+
from datahub.company_activity.models.stova_attendee import StovaAttendee
45
from datahub.company_activity.models.stova_event import StovaEvent
56

67

78
__all__ = (
89
CompanyActivity,
910
GreatExportEnquiry,
1011
IngestedFile,
12+
StovaAttendee,
1113
StovaEvent,
1214
)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
import uuid
2+
3+
from django.conf import settings
4+
from django.db import models
5+
6+
from datahub.company.models.company import Company
7+
from datahub.company.models.contact import Contact
8+
from datahub.core import reversion
9+
10+
11+
MAX_LENGTH = settings.CHAR_FIELD_MAX_LENGTH
12+
13+
14+
@reversion.register_base_model()
15+
class StovaAttendee(models.Model):
16+
"""
17+
Stova can also be known as Aventri.
18+
This model is filled and based off data from the S3 bucket: ExportAventriAttendees.
19+
"""
20+
21+
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
22+
stova_attendee_id = models.IntegerField(unique=True)
23+
stova_event_id = models.IntegerField(unique=True)
24+
25+
created_by = models.CharField(max_length=MAX_LENGTH)
26+
created_date = models.DateTimeField()
27+
modified_by = models.CharField(max_length=MAX_LENGTH)
28+
modified_date = models.DateTimeField()
29+
30+
email = models.CharField(max_length=MAX_LENGTH)
31+
first_name = models.CharField(max_length=MAX_LENGTH)
32+
last_name = models.CharField(max_length=MAX_LENGTH)
33+
attendee_questions = models.CharField(max_length=MAX_LENGTH)
34+
35+
company_name = models.CharField(max_length=MAX_LENGTH)
36+
category = models.CharField(max_length=MAX_LENGTH)
37+
registration_status = models.CharField(max_length=MAX_LENGTH)
38+
39+
virtual_event_attendance = models.CharField(max_length=MAX_LENGTH)
40+
language = models.CharField(max_length=MAX_LENGTH)
41+
42+
last_lobby_login = models.DateTimeField()
43+
44+
# Data Hub Fields
45+
created_on = models.DateTimeField(auto_now_add=True)
46+
company = models.ForeignKey(
47+
Company,
48+
on_delete=models.PROTECT,
49+
null=True,
50+
blank=True,
51+
related_name='stova_attendee',
52+
help_text='If a company match can be found from company_name, the relation is added.',
53+
)
54+
contact = models.ForeignKey(
55+
Contact,
56+
on_delete=models.PROTECT,
57+
null=True,
58+
blank=True,
59+
related_name='stova_attendee',
60+
help_text='If a contact match can be found from the email, the relation is added.',
61+
)

datahub/company_activity/tasks/__init__.py

+2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
from datahub.company_activity.tasks.ingest_great_data import ingest_great_data
2+
from datahub.company_activity.tasks.ingest_stova_attendees import stova_attendee_ingestion_task
23
from datahub.company_activity.tasks.ingest_stova_events import (
34
ingest_stova_event_data,
45
stova_ingestion_task,
@@ -7,5 +8,6 @@
78
__all__ = (
89
ingest_great_data,
910
ingest_stova_event_data,
11+
stova_attendee_ingestion_task,
1012
stova_ingestion_task,
1113
)

datahub/company_activity/tasks/constants.py

+1
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@
55
BUCKET = f"data-flow-bucket-{env('ENVIRONMENT', default='')}"
66
PREFIX = 'data-flow/exports/'
77
GREAT_PREFIX = f'{PREFIX}ExportGreatContactFormData/'
8+
STOVA_ATTENDEE_PREFIX = f'{PREFIX}ExportAventriAttendees/'
89
STOVA_EVENT_PREFIX = f'{PREFIX}ExportAventriEvents/'
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import logging
2+
3+
from django.core.exceptions import ValidationError
4+
from django.db import IntegrityError
5+
6+
from datahub.company_activity.models import StovaAttendee
7+
from datahub.company_activity.tasks.constants import STOVA_ATTENDEE_PREFIX
8+
from datahub.ingest.boto3 import S3ObjectProcessor
9+
from datahub.ingest.tasks import BaseObjectIdentificationTask, BaseObjectIngestionTask
10+
11+
12+
logger = logging.getLogger(__name__)
13+
DATE_FORMAT = '%Y-%m-%dT%H:%M:%S.%f'
14+
15+
16+
def ingest_stova_attendee_data() -> None:
17+
"""Identifies the most recent file to be ingested and schedules a task to ingest it"""
18+
logger.info('Stova attendee identification task started.')
19+
identification_task = StovaAttendeeIndentificationTask(prefix=STOVA_ATTENDEE_PREFIX)
20+
identification_task.identify_new_objects(stova_attendee_ingestion_task)
21+
logger.info('Stova attendee identification task finished.')
22+
23+
24+
def stova_attendee_ingestion_task(object_key: str) -> None:
25+
"""Ingest the given key (file) from S3"""
26+
logger.info(f'Stova attendee ingestion task started for file {object_key}.')
27+
ingestion_task = StovaAttendeeIngestionTask(
28+
object_key=object_key,
29+
s3_processor=S3ObjectProcessor(prefix=STOVA_ATTENDEE_PREFIX),
30+
)
31+
ingestion_task.ingest_object()
32+
logger.info(f'Stova attendee ingestion task finished for file {object_key}.')
33+
34+
35+
class StovaAttendeeIndentificationTask(BaseObjectIdentificationTask):
36+
pass
37+
38+
39+
class StovaAttendeeIngestionTask(BaseObjectIngestionTask):
40+
41+
existing_ids = []
42+
43+
def _process_record(self, record: dict) -> None:
44+
"""Saves an attendee from Stova from the S3 bucket into a `Stovaattendee`"""
45+
if not self.existing_ids:
46+
self.existing_ids = set(
47+
StovaAttendee.objects.values_list('stova_attendee_id', flat=True),
48+
)
49+
50+
stova_attendee_id = record.get('id')
51+
if stova_attendee_id in self.existing_ids:
52+
logger.info(f'Record already exists for stova_attendee_id: {stova_attendee_id}')
53+
return
54+
55+
values = {
56+
'stova_attendee_id': stova_attendee_id,
57+
'stova_event_id': record.get('event_id', ''),
58+
'created_date': record.get('created_date'),
59+
'email': record.get('email', ''),
60+
'first_name': record.get('first_name', ''),
61+
'last_name': record.get('last_name', ''),
62+
'company_name': record.get('company_name', ''),
63+
'category': record.get('category', ''),
64+
'registration_status': record.get('registration_status', ''),
65+
'created_by': record.get('created_by', ''),
66+
'language': record.get('language', ''),
67+
'modified_date': record.get('modified_date'),
68+
'virtual_event_attendance': record.get('virtual_event_attendance', ''),
69+
'last_lobby_login': record.get('last_lobby_login', ''),
70+
'attendee_questions': record.get('attendee_questions', ''),
71+
'modified_by': record.get('modified_by', ''),
72+
}
73+
74+
try:
75+
StovaAttendee.objects.create(**values)
76+
except IntegrityError as error:
77+
logger.error(
78+
f'Error processing Stova attendee record, stova_attendee_id: {stova_attendee_id}. '
79+
f'Error: {error}',
80+
)
81+
except ValidationError as error:
82+
logger.error(
83+
'Got unexpected value for a field when processing Stova attendee record, '
84+
f'stova_attendee_id: {stova_attendee_id}. '
85+
f'Error: {error}',
86+
)

datahub/company_activity/tests/factories.py

+29
Original file line numberDiff line numberDiff line change
@@ -272,3 +272,32 @@ class StovaEventFactory(factory.django.DjangoModelFactory):
272272

273273
class Meta:
274274
model = 'company_activity.StovaEvent'
275+
276+
277+
class StovaAttendeeFactory(factory.django.DjangoModelFactory):
278+
"""
279+
Ingested Stova Attendee data factory.
280+
"""
281+
282+
stova_event_id = factory.Faker('pyint', min_value=0, max_value=999999999)
283+
stova_attendee_id = factory.Faker('pyint', min_value=0, max_value=999999999)
284+
285+
created_by = 'John'
286+
created_date = now()
287+
modified_by = 'John'
288+
modified_date = now()
289+
290+
email = 'john@test.com'
291+
first_name = 'John'
292+
last_name = 'Smith'
293+
attendee_questions = 'This is a question'
294+
295+
company_name = 'A company name'
296+
category = 'A category'
297+
registration_status = 'The registration status'
298+
virtual_event_attendance = 'Virtual Event Attendance'
299+
language = 'English'
300+
last_lobby_login = now()
301+
302+
class Meta:
303+
model = 'company_activity.StovaAttendee'

0 commit comments

Comments
 (0)