Skip to content

Commit 91c9bf7

Browse files
CLS2-1031 Add EYB Lead to Company Activity and Opensearch (#5828)
* Add EYB Lead to Company Activity * Add EYB Lead / Company Activity to Opensearch
1 parent 8ea5471 commit 91c9bf7

18 files changed

+335
-9
lines changed

datahub/cleanup/test/commands/test_delete_orphaned_versions.py

+1
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@
112112
GreatExportEnquiryFactory,
113113
InvestmentProjectFactory,
114114
OrderFactory,
115+
EYBLeadFactory,
115116
]
116117

117118

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# Generated by Django 4.2.16 on 2024-11-27 15:47
2+
3+
from django.db import migrations, models
4+
import django.db.models.deletion
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
('investment_lead', '0009_alter_eyblead_marketing_hashed_uuid'),
11+
('company_activity', '0017_ingestedfile_file_created_and_more'),
12+
]
13+
14+
operations = [
15+
migrations.AddField(
16+
model_name='companyactivity',
17+
name='eyb_lead',
18+
field=models.ForeignKey(blank=True, help_text='If related to an EYB lead, must not have relations to any other activity (referral, event etc)', null=True, on_delete=django.db.models.deletion.CASCADE, related_name='activity', to='investment_lead.eyblead', unique=True),
19+
),
20+
migrations.AlterField(
21+
model_name='companyactivity',
22+
name='activity_source',
23+
field=models.CharField(choices=[('interaction', 'interaction'), ('referral', 'referral'), ('event', 'event'), ('investment', 'investment'), ('order', 'order'), ('great_export_enquiry', 'great_export_enquiry'), ('eyb_lead', 'eyb_lead')], help_text='The type of company activity, such as an interaction, event, referral etc.', max_length=255),
24+
),
25+
]

datahub/company_activity/models/company_activity.py

+14
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ class ActivitySource(models.TextChoices):
3030
investment = ('investment', 'investment')
3131
order = ('order', 'order')
3232
great_export_enquiry = ('great_export_enquiry', 'great_export_enquiry')
33+
eyb_lead = ('eyb_lead', 'eyb_lead')
3334

3435
id = models.UUIDField(primary_key=True, default=uuid.uuid4)
3536
company = models.ForeignKey(
@@ -114,6 +115,19 @@ class ActivitySource(models.TextChoices):
114115
),
115116
)
116117

118+
eyb_lead = models.ForeignKey(
119+
'investment_lead.EYBLead',
120+
unique=True,
121+
null=True,
122+
blank=True,
123+
related_name='activity',
124+
on_delete=models.CASCADE,
125+
help_text=(
126+
'If related to an EYB lead, must not have relations to any other activity '
127+
'(referral, event etc)'
128+
),
129+
)
130+
117131
def __str__(self):
118132
"""Readable name for CompanyActivity"""
119133
return f'Activity from "{self.activity_source}" for company: {self.company.name}'

datahub/company_activity/tasks/sync.py

+36-5
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from datahub.core.queues.scheduler import LONG_RUNNING_QUEUE
88
from datahub.interaction.models import Interaction
99
from datahub.investment.project.models import InvestmentProject
10+
from datahub.investment_lead.models import EYBLead
1011
from datahub.omis.order.models import Order
1112

1213

@@ -15,7 +16,7 @@
1516

1617
def relate_company_activity_to_interactions(batch_size=500):
1718
"""
18-
Grabs all interactions so they can be related to in the
19+
Grabs all interactions so they can be related to the
1920
`CompanyActivity` model with bulk_create. Excludes any
2021
interactions already associated in the CompanyActivity model.
2122
@@ -46,7 +47,7 @@ def relate_company_activity_to_interactions(batch_size=500):
4647

4748
def relate_company_activity_to_referrals(batch_size=500):
4849
"""
49-
Grabs all referrals so they can be related to in the
50+
Grabs all referrals so they can be related to the
5051
`CompanyActivity` model with a bulk_create. Excludes any
5152
referrals already associated in the CompanyActivity model.
5253
@@ -76,7 +77,7 @@ def relate_company_activity_to_referrals(batch_size=500):
7677

7778
def relate_company_activity_to_investment_projects(batch_size=500):
7879
"""
79-
Grabs all investment projects so they can be related to in the
80+
Grabs all investment projects so they can be related to the
8081
`CompanyActivity` model with a bulk_create. Excludes any
8182
investment projects already associated in the CompanyActivity model.
8283
"""
@@ -106,7 +107,7 @@ def relate_company_activity_to_investment_projects(batch_size=500):
106107

107108
def relate_company_activity_to_orders(batch_size=500):
108109
"""
109-
Grabs all omis orders so they can be related to in the
110+
Grabs all omis orders so they can be related to the
110111
`CompanyActivity` model with a bulk_create. Excludes any
111112
order projects already associated in the CompanyActivity model.
112113
"""
@@ -136,7 +137,7 @@ def relate_company_activity_to_orders(batch_size=500):
136137

137138
def relate_company_activity_to_great(batch_size=500):
138139
"""
139-
Grabs all great export enquiry so they can be related to in the
140+
Grabs all great export enquiry so they can be related to the
140141
`CompanyActivity` model with a bulk_create. Excludes any
141142
great export enquiry already associated in the CompanyActivity model.
142143
"""
@@ -164,6 +165,36 @@ def relate_company_activity_to_great(batch_size=500):
164165
bulk_create_activity(objs, batch_size)
165166

166167

168+
def relate_company_activity_to_eyb_lead(batch_size=500):
169+
"""
170+
Grabs all EYB leads so they can be related to the
171+
`CompanyActivity` model with a bulk_create. Excludes any
172+
EYB leads already associated in the CompanyActivity model.
173+
"""
174+
activity = set(
175+
CompanyActivity.objects.filter(
176+
eyb_lead__isnull=False,
177+
).values_list('eyb_lead', flat=True),
178+
)
179+
180+
eyb_leads = EYBLead.objects.filter(
181+
company__isnull=False,
182+
).values('id', 'created_on', 'company_id')
183+
184+
objs = [
185+
CompanyActivity(
186+
eyb_lead_id=eyb_lead['id'],
187+
date=eyb_lead['created_on'],
188+
company_id=eyb_lead['company_id'],
189+
activity_source=CompanyActivity.ActivitySource.eyb_lead,
190+
)
191+
for eyb_lead in eyb_leads
192+
if eyb_lead['id'] not in activity
193+
]
194+
195+
bulk_create_activity(objs, batch_size)
196+
197+
167198
def schedule_sync_data_to_company_activity(relate_function):
168199
"""
169200
Schedules a task for the given function.

datahub/company_activity/tests/factories.py

+23
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from datahub.company_referral.test.factories import CompanyReferralFactory
77
from datahub.interaction.test.factories import CompanyInteractionFactory
88
from datahub.investment.project.test.factories import InvestmentProjectFactory
9+
from datahub.investment_lead.test.factories import EYBLeadFactory
910
from datahub.metadata.test.factories import CountryFactory, SectorFactory
1011
from datahub.omis.order.test.factories import OrderFactory
1112

@@ -205,3 +206,25 @@ def _create(cls, model_class, *args, **kwargs):
205206
"""
206207
obj = model_class(*args, **kwargs)
207208
return CompanyActivity.objects.get(great_export_enquiry_id=obj.great_export_enquiry_id)
209+
210+
211+
class CompanyActivityEYBLeadFactory(CompanyActivityBaseFactory):
212+
"""
213+
CompanyActivity factory with an EYB lead.
214+
"""
215+
216+
activity_source = CompanyActivity.ActivitySource.eyb_lead
217+
eyb_lead = factory.SubFactory(EYBLeadFactory)
218+
219+
class Meta:
220+
model = 'company_activity.CompanyActivity'
221+
222+
@classmethod
223+
def _create(cls, model_class, *args, **kwargs):
224+
"""
225+
Overwrite the _create to prevent the CompanyActivity from saving to the database.
226+
This is due to the EYB lead already creating the CompanyActivity inside its
227+
model save.
228+
"""
229+
obj = model_class(*args, **kwargs)
230+
return CompanyActivity.objects.get(eyb_lead_id=obj.eyb_lead_id)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
from unittest import mock
2+
3+
import pytest
4+
5+
from datahub.company_activity.models import CompanyActivity
6+
from datahub.company_activity.tasks.sync import (
7+
relate_company_activity_to_eyb_lead,
8+
schedule_sync_data_to_company_activity,
9+
)
10+
from datahub.investment_lead.test.factories import EYBLeadFactory
11+
12+
13+
@pytest.mark.django_db
14+
class TestCompanyActivityEYBLeadTasks:
15+
"""
16+
Tests for the schedule_sync_data_to_company_activity task.
17+
"""
18+
19+
def test_eyb_leads_are_copied_to_company_activity(self):
20+
"""
21+
Test that eyb leads are added to the CompanyActivity model.
22+
"""
23+
eyb_leads = EYBLeadFactory.create_batch(5)
24+
25+
# Remove the created CompanyActivities added by the eyb lead `save` method
26+
# to mimic already existing data in staging and prod database.
27+
CompanyActivity.objects.all().delete()
28+
assert CompanyActivity.objects.count() == 0
29+
30+
# Check the "existing" eyb leads are added to the company activity model
31+
schedule_sync_data_to_company_activity(relate_company_activity_to_eyb_lead)
32+
assert CompanyActivity.objects.count() == len(eyb_leads)
33+
34+
company_activity = CompanyActivity.objects.get(eyb_lead=eyb_leads[0])
35+
assert company_activity.date == eyb_leads[0].created_on
36+
assert company_activity.activity_source == CompanyActivity.ActivitySource.eyb_lead
37+
assert company_activity.eyb_lead_id == eyb_leads[0].id
38+
39+
@mock.patch('datahub.company_activity.models.CompanyActivity.objects.bulk_create')
40+
def test_eyb_leads_are_bulk_created_in_batches(self, mocked_bulk_create, caplog):
41+
"""
42+
Test that eyb leads are bulk created in batches.
43+
"""
44+
caplog.set_level('INFO')
45+
batch_size = 5
46+
47+
EYBLeadFactory.create_batch(10)
48+
49+
# Delete any activity created through the investments save method.
50+
CompanyActivity.objects.all().delete()
51+
assert CompanyActivity.objects.count() == 0
52+
53+
# Ensure eyb leads are bulk_created
54+
relate_company_activity_to_eyb_lead(batch_size)
55+
assert mocked_bulk_create.call_count == 2
56+
57+
assert (
58+
f'Creating in batches of: {batch_size} CompanyActivities. 10 remaining.' in caplog.text
59+
)
60+
assert (
61+
f'Creating in batches of: {batch_size} CompanyActivities. 5 remaining.' in caplog.text
62+
)
63+
assert 'Finished bulk creating CompanyActivities.' in caplog.text
64+
65+
def test_eyb_leads_with_a_company_activity_are_not_added_again(self):
66+
"""
67+
Test that eyb leads which are already part of the `CompanyActivity` model
68+
are not added again.
69+
"""
70+
EYBLeadFactory.create_batch(4)
71+
72+
assert CompanyActivity.objects.count() == 4
73+
74+
# Check count remains unchanged.
75+
schedule_sync_data_to_company_activity(relate_company_activity_to_eyb_lead)
76+
assert CompanyActivity.objects.count() == 4

datahub/company_activity/tests/test_tasks/test_order_task.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@
1313
@pytest.mark.django_db
1414
class TestCompanyActivityOrderTasks:
1515
"""
16-
Tests for the schedule_sync_investments_to_company_activity task.
16+
Tests for the schedule_sync_data_to_company_activity task.
1717
"""
1818

1919
def test_orders_are_copied_to_company_activity(self):
2020
"""
21-
Test that investments are added to the CompanyActivity model.
21+
Test that omis orders are added to the CompanyActivity model.
2222
"""
2323
orders = OrderFactory.create_batch(5)
2424

datahub/investment_lead/models.py

+16-1
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,10 @@
66
MaxLengthValidator,
77
MinLengthValidator,
88
)
9-
from django.db import models
9+
from django.db import models, transaction
1010
from mptt.fields import TreeForeignKey
1111

12+
from datahub.company_activity.models import CompanyActivity
1213
from datahub.core import reversion
1314
from datahub.core.models import ArchivableModel
1415

@@ -221,3 +222,17 @@ def name(self):
221222
if self.company:
222223
return f'EYB Lead ({shortened_pk}...) for {self.company.name}'
223224
return f'EYB Lead ({shortened_pk}...)'
225+
226+
def save(self, *args, **kwargs):
227+
"""
228+
Creates a CompanyActivity when a EYB Lead is saved
229+
"""
230+
with transaction.atomic():
231+
super().save(*args, **kwargs)
232+
if not self.company:
233+
return
234+
CompanyActivity.objects.update_or_create(
235+
eyb_lead_id=self.id,
236+
activity_source=CompanyActivity.ActivitySource.eyb_lead,
237+
defaults={'date': self.created_on, 'company_id': self.company_id},
238+
)
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,30 @@
11
import pytest
22

3+
from datahub.company_activity.models import CompanyActivity
4+
from datahub.investment_lead.test.factories import EYBLeadFactory
5+
36

47
@pytest.mark.django_db
58
class TestEYBLead:
69
"""Tests EYB Lead model"""
710

11+
def test_save_without_company_no_company_activity(self):
12+
assert not CompanyActivity.objects.all().exists()
13+
EYBLeadFactory(company=None)
14+
assert not CompanyActivity.objects.all().exists()
15+
16+
def test_save_with_company_creates_company_activity(self):
17+
assert not CompanyActivity.objects.all().exists()
18+
19+
eyb_lead = EYBLeadFactory()
20+
21+
assert CompanyActivity.objects.all().count() == 1
22+
23+
company_activity = CompanyActivity.objects.get(eyb_lead=eyb_lead.id)
24+
assert company_activity.company_id == eyb_lead.company.id
25+
assert company_activity.date == eyb_lead.created_on
26+
assert company_activity.activity_source == CompanyActivity.ActivitySource.eyb_lead
27+
828
def test_str(self, eyb_lead_instance_from_db):
929
"""Test the human friendly string representation of the object"""
1030
assert str(eyb_lead_instance_from_db) == eyb_lead_instance_from_db.name

datahub/search/company_activity/apps.py

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class CompanyActivitySearchApp(SearchApp):
3131
'order__created_by',
3232
'great_export_enquiry',
3333
'great_export_enquiry__contact',
34+
'eyb_lead',
3435
).prefetch_related(
3536
'interaction__contacts',
3637
Prefetch(

datahub/search/company_activity/dict_utils.py

+13
Original file line numberDiff line numberDiff line change
@@ -85,3 +85,16 @@ def activity_great_dict(obj):
8585
'meta_subject': obj.meta_subject,
8686
'data_enquiry': obj.data_enquiry,
8787
}
88+
89+
90+
def activity_eyb_lead_dict(obj):
91+
"""Creates a dictionary for an eyb lead."""
92+
if obj is None:
93+
return None
94+
95+
return {
96+
'id': str(obj.id),
97+
'created_on': obj.created_on,
98+
'company_name': obj.company_name,
99+
'duns_number': obj.duns_number,
100+
}

datahub/search/company_activity/fields.py

+11
Original file line numberDiff line numberDiff line change
@@ -78,3 +78,14 @@ def activity_great_field():
7878
'data_enquiry': Text(index=False),
7979
},
8080
)
81+
82+
83+
def activity_eyb_lead_field():
84+
return Object(
85+
properties={
86+
'id': Keyword(),
87+
'created_on': Date(),
88+
'company_name': Text(index=False),
89+
'duns_number': Text(index=False),
90+
},
91+
)

0 commit comments

Comments
 (0)