wbcrm 1.56.8__py2.py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (182) hide show
  1. wbcrm/__init__.py +1 -0
  2. wbcrm/admin/__init__.py +5 -0
  3. wbcrm/admin/accounts.py +60 -0
  4. wbcrm/admin/activities.py +104 -0
  5. wbcrm/admin/events.py +43 -0
  6. wbcrm/admin/groups.py +8 -0
  7. wbcrm/admin/products.py +9 -0
  8. wbcrm/apps.py +5 -0
  9. wbcrm/configurations/__init__.py +1 -0
  10. wbcrm/configurations/base.py +16 -0
  11. wbcrm/dynamic_preferences_registry.py +38 -0
  12. wbcrm/factories/__init__.py +14 -0
  13. wbcrm/factories/accounts.py +57 -0
  14. wbcrm/factories/activities.py +124 -0
  15. wbcrm/factories/groups.py +24 -0
  16. wbcrm/factories/products.py +11 -0
  17. wbcrm/filters/__init__.py +10 -0
  18. wbcrm/filters/accounts.py +80 -0
  19. wbcrm/filters/activities.py +204 -0
  20. wbcrm/filters/groups.py +21 -0
  21. wbcrm/filters/products.py +38 -0
  22. wbcrm/filters/signals.py +95 -0
  23. wbcrm/fixtures/wbcrm.json +1215 -0
  24. wbcrm/kpi_handlers/activities.py +171 -0
  25. wbcrm/locale/de/LC_MESSAGES/django.mo +0 -0
  26. wbcrm/locale/de/LC_MESSAGES/django.po +1557 -0
  27. wbcrm/locale/de/LC_MESSAGES/django.po.translated +1630 -0
  28. wbcrm/locale/en/LC_MESSAGES/django.mo +0 -0
  29. wbcrm/locale/en/LC_MESSAGES/django.po +1466 -0
  30. wbcrm/locale/fr/LC_MESSAGES/django.mo +0 -0
  31. wbcrm/locale/fr/LC_MESSAGES/django.po +1467 -0
  32. wbcrm/migrations/0001_initial_squashed_squashed_0032_productcompanyrelationship_alter_product_prospects_and_more.py +3948 -0
  33. wbcrm/migrations/0002_alter_activity_repeat_choice.py +32 -0
  34. wbcrm/migrations/0003_remove_activity_external_id_and_more.py +63 -0
  35. wbcrm/migrations/0004_alter_activity_status.py +28 -0
  36. wbcrm/migrations/0005_account_accountrole_accountroletype_and_more.py +182 -0
  37. wbcrm/migrations/0006_alter_activity_location.py +17 -0
  38. wbcrm/migrations/0007_alter_account_status.py +23 -0
  39. wbcrm/migrations/0008_alter_activity_options.py +16 -0
  40. wbcrm/migrations/0009_alter_account_is_public.py +19 -0
  41. wbcrm/migrations/0010_alter_account_reference_id.py +17 -0
  42. wbcrm/migrations/0011_activity_summary.py +22 -0
  43. wbcrm/migrations/0012_alter_activity_summary.py +17 -0
  44. wbcrm/migrations/0013_account_action_plan_account_relationship_status_and_more.py +34 -0
  45. wbcrm/migrations/0014_alter_account_relationship_status.py +24 -0
  46. wbcrm/migrations/0015_alter_activity_type.py +23 -0
  47. wbcrm/migrations/0016_auto_20241205_1015.py +106 -0
  48. wbcrm/migrations/0017_event.py +40 -0
  49. wbcrm/migrations/0018_activity_search_vector.py +24 -0
  50. wbcrm/migrations/__init__.py +0 -0
  51. wbcrm/models/__init__.py +5 -0
  52. wbcrm/models/accounts.py +648 -0
  53. wbcrm/models/activities.py +1419 -0
  54. wbcrm/models/events.py +15 -0
  55. wbcrm/models/groups.py +119 -0
  56. wbcrm/models/llm/activity_summaries.py +41 -0
  57. wbcrm/models/llm/analyze_relationship.py +50 -0
  58. wbcrm/models/products.py +86 -0
  59. wbcrm/models/recurrence.py +280 -0
  60. wbcrm/preferences.py +13 -0
  61. wbcrm/report/activity_report.py +110 -0
  62. wbcrm/serializers/__init__.py +23 -0
  63. wbcrm/serializers/accounts.py +141 -0
  64. wbcrm/serializers/activities.py +525 -0
  65. wbcrm/serializers/groups.py +30 -0
  66. wbcrm/serializers/products.py +58 -0
  67. wbcrm/serializers/recurrence.py +91 -0
  68. wbcrm/serializers/signals.py +71 -0
  69. wbcrm/static/wbcrm/markdown/documentation/activity.md +86 -0
  70. wbcrm/static/wbcrm/markdown/documentation/activitytype.md +20 -0
  71. wbcrm/static/wbcrm/markdown/documentation/group.md +2 -0
  72. wbcrm/static/wbcrm/markdown/documentation/product.md +11 -0
  73. wbcrm/synchronization/__init__.py +0 -0
  74. wbcrm/synchronization/activity/__init__.py +0 -0
  75. wbcrm/synchronization/activity/admin.py +73 -0
  76. wbcrm/synchronization/activity/backend.py +214 -0
  77. wbcrm/synchronization/activity/backends/__init__.py +0 -0
  78. wbcrm/synchronization/activity/backends/google/__init__.py +2 -0
  79. wbcrm/synchronization/activity/backends/google/google_calendar_backend.py +406 -0
  80. wbcrm/synchronization/activity/backends/google/request_utils/__init__.py +16 -0
  81. wbcrm/synchronization/activity/backends/google/request_utils/external_to_internal/create.py +75 -0
  82. wbcrm/synchronization/activity/backends/google/request_utils/external_to_internal/delete.py +78 -0
  83. wbcrm/synchronization/activity/backends/google/request_utils/external_to_internal/update.py +155 -0
  84. wbcrm/synchronization/activity/backends/google/request_utils/internal_to_external/update.py +181 -0
  85. wbcrm/synchronization/activity/backends/google/tasks.py +21 -0
  86. wbcrm/synchronization/activity/backends/google/tests/__init__.py +0 -0
  87. wbcrm/synchronization/activity/backends/google/tests/conftest.py +1 -0
  88. wbcrm/synchronization/activity/backends/google/tests/test_data.py +81 -0
  89. wbcrm/synchronization/activity/backends/google/tests/test_google_backend.py +319 -0
  90. wbcrm/synchronization/activity/backends/google/tests/test_utils.py +274 -0
  91. wbcrm/synchronization/activity/backends/google/typing_informations.py +139 -0
  92. wbcrm/synchronization/activity/backends/google/utils.py +217 -0
  93. wbcrm/synchronization/activity/backends/outlook/__init__.py +0 -0
  94. wbcrm/synchronization/activity/backends/outlook/backend.py +593 -0
  95. wbcrm/synchronization/activity/backends/outlook/msgraph.py +436 -0
  96. wbcrm/synchronization/activity/backends/outlook/parser.py +432 -0
  97. wbcrm/synchronization/activity/backends/outlook/tests/__init__.py +0 -0
  98. wbcrm/synchronization/activity/backends/outlook/tests/conftest.py +1 -0
  99. wbcrm/synchronization/activity/backends/outlook/tests/fixtures.py +606 -0
  100. wbcrm/synchronization/activity/backends/outlook/tests/test_admin.py +118 -0
  101. wbcrm/synchronization/activity/backends/outlook/tests/test_backend.py +274 -0
  102. wbcrm/synchronization/activity/backends/outlook/tests/test_controller.py +249 -0
  103. wbcrm/synchronization/activity/backends/outlook/tests/test_parser.py +174 -0
  104. wbcrm/synchronization/activity/controller.py +627 -0
  105. wbcrm/synchronization/activity/dynamic_preferences_registry.py +119 -0
  106. wbcrm/synchronization/activity/preferences.py +27 -0
  107. wbcrm/synchronization/activity/shortcuts.py +16 -0
  108. wbcrm/synchronization/activity/tasks.py +21 -0
  109. wbcrm/synchronization/activity/urls.py +7 -0
  110. wbcrm/synchronization/activity/utils.py +46 -0
  111. wbcrm/synchronization/activity/views.py +41 -0
  112. wbcrm/synchronization/admin.py +1 -0
  113. wbcrm/synchronization/apps.py +14 -0
  114. wbcrm/synchronization/dynamic_preferences_registry.py +1 -0
  115. wbcrm/synchronization/management.py +36 -0
  116. wbcrm/synchronization/tasks.py +1 -0
  117. wbcrm/synchronization/urls.py +5 -0
  118. wbcrm/tasks.py +264 -0
  119. wbcrm/templates/email/activity.html +98 -0
  120. wbcrm/templates/email/activity_report.html +6 -0
  121. wbcrm/templates/email/daily_summary.html +72 -0
  122. wbcrm/templates/email/global_daily_summary.html +85 -0
  123. wbcrm/tests/__init__.py +0 -0
  124. wbcrm/tests/accounts/__init__.py +0 -0
  125. wbcrm/tests/accounts/test_models.py +393 -0
  126. wbcrm/tests/accounts/test_viewsets.py +88 -0
  127. wbcrm/tests/conftest.py +76 -0
  128. wbcrm/tests/disable_signals.py +62 -0
  129. wbcrm/tests/e2e/__init__.py +1 -0
  130. wbcrm/tests/e2e/e2e_wbcrm_utility.py +83 -0
  131. wbcrm/tests/e2e/test_e2e.py +370 -0
  132. wbcrm/tests/test_assignee_methods.py +40 -0
  133. wbcrm/tests/test_chartviewsets.py +112 -0
  134. wbcrm/tests/test_dto.py +64 -0
  135. wbcrm/tests/test_filters.py +52 -0
  136. wbcrm/tests/test_models.py +217 -0
  137. wbcrm/tests/test_recurrence.py +292 -0
  138. wbcrm/tests/test_report.py +21 -0
  139. wbcrm/tests/test_serializers.py +171 -0
  140. wbcrm/tests/test_tasks.py +95 -0
  141. wbcrm/tests/test_viewsets.py +967 -0
  142. wbcrm/tests/tests.py +121 -0
  143. wbcrm/typings.py +109 -0
  144. wbcrm/urls.py +67 -0
  145. wbcrm/viewsets/__init__.py +22 -0
  146. wbcrm/viewsets/accounts.py +122 -0
  147. wbcrm/viewsets/activities.py +341 -0
  148. wbcrm/viewsets/buttons/__init__.py +7 -0
  149. wbcrm/viewsets/buttons/accounts.py +27 -0
  150. wbcrm/viewsets/buttons/activities.py +89 -0
  151. wbcrm/viewsets/buttons/signals.py +17 -0
  152. wbcrm/viewsets/display/__init__.py +12 -0
  153. wbcrm/viewsets/display/accounts.py +110 -0
  154. wbcrm/viewsets/display/activities.py +444 -0
  155. wbcrm/viewsets/display/groups.py +22 -0
  156. wbcrm/viewsets/display/products.py +105 -0
  157. wbcrm/viewsets/endpoints/__init__.py +8 -0
  158. wbcrm/viewsets/endpoints/accounts.py +25 -0
  159. wbcrm/viewsets/endpoints/activities.py +30 -0
  160. wbcrm/viewsets/endpoints/groups.py +7 -0
  161. wbcrm/viewsets/endpoints/products.py +9 -0
  162. wbcrm/viewsets/groups.py +38 -0
  163. wbcrm/viewsets/menu/__init__.py +8 -0
  164. wbcrm/viewsets/menu/accounts.py +18 -0
  165. wbcrm/viewsets/menu/activities.py +49 -0
  166. wbcrm/viewsets/menu/groups.py +16 -0
  167. wbcrm/viewsets/menu/products.py +20 -0
  168. wbcrm/viewsets/mixins.py +35 -0
  169. wbcrm/viewsets/previews/__init__.py +1 -0
  170. wbcrm/viewsets/previews/activities.py +10 -0
  171. wbcrm/viewsets/products.py +57 -0
  172. wbcrm/viewsets/recurrence.py +27 -0
  173. wbcrm/viewsets/titles/__init__.py +13 -0
  174. wbcrm/viewsets/titles/accounts.py +23 -0
  175. wbcrm/viewsets/titles/activities.py +61 -0
  176. wbcrm/viewsets/titles/products.py +13 -0
  177. wbcrm/viewsets/titles/utils.py +46 -0
  178. wbcrm/workflows/__init__.py +1 -0
  179. wbcrm/workflows/assignee_methods.py +25 -0
  180. wbcrm-1.56.8.dist-info/METADATA +11 -0
  181. wbcrm-1.56.8.dist-info/RECORD +182 -0
  182. wbcrm-1.56.8.dist-info/WHEEL +5 -0
@@ -0,0 +1,171 @@
1
+ from collections import OrderedDict
2
+ from unittest import TestCase
3
+
4
+ import pytest
5
+ from django.core.exceptions import ValidationError as DjangoValidationError
6
+ from rest_framework.exceptions import ValidationError
7
+ from rest_framework.test import APIRequestFactory
8
+ from wbcore.contrib.authentication.factories import UserFactory
9
+ from wbcore.contrib.directory.factories import PersonFactory
10
+ from wbcore.contrib.directory.models import Entry
11
+
12
+ from wbcrm.factories import ActivityFactory, GroupFactory, ProductFactory
13
+ from wbcrm.serializers import ActivityModelSerializer, ProductModelSerializer
14
+ from wbcrm.serializers.activities import handle_representation
15
+
16
+
17
+ @pytest.mark.django_db
18
+ class TestActivitySerializers(TestCase):
19
+ @pytest.mark.unit
20
+ def test_remove_group_member_from_instance(self):
21
+ group_member_a = PersonFactory()
22
+ group_member_a_entry = Entry.objects.get(id=group_member_a.id)
23
+ group_member_b = PersonFactory()
24
+ group_member_b_entry = Entry.objects.get(id=group_member_b.id)
25
+ group = GroupFactory(members=(group_member_a_entry, group_member_b_entry))
26
+ activity = ActivityFactory(groups=(group,), participants=(group_member_a, group_member_b))
27
+
28
+ request = APIRequestFactory().get("")
29
+ request.user = UserFactory()
30
+ request.parser_context = {}
31
+ serializer = ActivityModelSerializer(activity, context={"request": request})
32
+ new_data = {
33
+ "participants": [group_member_b],
34
+ }
35
+ assert serializer.data
36
+ assert group_member_a.id in serializer.data["participants"]
37
+ assert group_member_b.id in serializer.data["participants"]
38
+ with self.assertRaises(ValidationError):
39
+ serializer.validate(new_data)
40
+
41
+ @pytest.mark.unit
42
+ def test_remove_all_group_members_from_instance(self):
43
+ group_member_a = PersonFactory()
44
+ group_member_a_entry = Entry.objects.get(id=group_member_a.id)
45
+ group_member_b = PersonFactory()
46
+ group_member_b_entry = Entry.objects.get(id=group_member_b.id)
47
+ group = GroupFactory(members=(group_member_a_entry, group_member_b_entry))
48
+ activity = ActivityFactory(groups=(group,), participants=(group_member_a, group_member_b))
49
+
50
+ request = APIRequestFactory().get("")
51
+ request.user = UserFactory()
52
+ request.parser_context = {}
53
+ serializer = ActivityModelSerializer(activity, context={"request": request})
54
+ new_data = {
55
+ "participants": [],
56
+ }
57
+ assert serializer.data
58
+ assert set(serializer.data["participants"]) == {group_member_a.id, group_member_b.id}
59
+ with self.assertRaises(ValidationError):
60
+ serializer.validate(new_data)
61
+
62
+ @pytest.mark.unit
63
+ def test_remove_all_group_members_and_groups_from_instance(self):
64
+ group_member_a = PersonFactory()
65
+ group_member_a_entry = Entry.objects.get(id=group_member_a.id)
66
+ group_member_b = PersonFactory()
67
+ group_member_b_entry = Entry.objects.get(id=group_member_b.id)
68
+ group = GroupFactory(members=(group_member_a_entry, group_member_b_entry))
69
+ activity = ActivityFactory(groups=(group,), participants=(group_member_a, group_member_b))
70
+
71
+ request = APIRequestFactory().get("")
72
+ request.user = UserFactory()
73
+ request.parser_context = {}
74
+ serializer = ActivityModelSerializer(activity, context={"request": request})
75
+ new_data = {
76
+ "participants": [],
77
+ "groups": [],
78
+ }
79
+ assert serializer.data
80
+ try:
81
+ validated_data = serializer.validate(new_data)
82
+ except DjangoValidationError:
83
+ self.fail("Activity threw error in validation method!")
84
+ else:
85
+ self.assertEqual(validated_data, new_data)
86
+
87
+ @pytest.mark.unit
88
+ def test_remove_not_group_membersfrom_instance(self):
89
+ group_member_a = PersonFactory()
90
+ group_member_a_entry = Entry.objects.get(id=group_member_a.id)
91
+ not_group_member = PersonFactory()
92
+ not_group_member_b_entry = Entry.objects.get(id=not_group_member.id)
93
+ group = GroupFactory(members=(group_member_a_entry, not_group_member_b_entry))
94
+ activity = ActivityFactory(groups=(group,), participants=(group_member_a, not_group_member))
95
+
96
+ request = APIRequestFactory().get("")
97
+ request.user = UserFactory()
98
+ request.parser_context = {}
99
+ serializer = ActivityModelSerializer(activity, context={"request": request})
100
+ new_data = {
101
+ "participants": [
102
+ group_member_a.id,
103
+ ],
104
+ "groups": [],
105
+ }
106
+ assert serializer.data
107
+ try:
108
+ validated_data = serializer.validate(new_data)
109
+ except DjangoValidationError:
110
+ self.fail("Activity threw error in validation method!")
111
+ else:
112
+ self.assertEqual(validated_data, new_data)
113
+
114
+
115
+ @pytest.mark.django_db
116
+ class TestActivitySerializersHelperFunction:
117
+ @pytest.mark.parametrize("is_private, is_confidential", [(True, False), (True, False)])
118
+ def test_handle_representaion(self, is_private, is_confidential):
119
+ test_representation = OrderedDict(
120
+ [
121
+ ("id", "Test-ID"),
122
+ ("title", "Test-Title"),
123
+ ("is_private", is_private),
124
+ ("is_confidential", is_confidential),
125
+ ("foo", "Foo"),
126
+ ("bar", "Bar"),
127
+ ]
128
+ )
129
+ test_representation = handle_representation(test_representation)
130
+ assert test_representation["id"] == "Test-ID"
131
+ if is_private:
132
+ assert test_representation["title"] == "Private Activity"
133
+ assert test_representation["foo"] is None
134
+ assert test_representation["bar"] is None
135
+ elif not is_private and is_confidential:
136
+ assert test_representation["title"] == "Confidential Activity"
137
+ assert test_representation["foo"] is None
138
+ assert test_representation["bar"] is None
139
+ elif not is_private and not is_confidential:
140
+ assert test_representation["title"] == "Test-Title"
141
+ assert test_representation["foo"] == "Foo"
142
+ assert test_representation["bar"] == "Bar"
143
+
144
+
145
+ @pytest.mark.django_db
146
+ class TestUtilsSerializers:
147
+ def test_duplicate_company_product(self):
148
+ product = ProductFactory(is_competitor=False)
149
+ serializer = ProductModelSerializer()
150
+ data = {"title": product.title, "is_competitor": product.is_competitor}
151
+ with pytest.raises(DjangoValidationError):
152
+ serializer.validate(data)
153
+
154
+ def test_non_duplicate_competitors_product(self):
155
+ product = ProductFactory(is_competitor=False)
156
+ serializer = ProductModelSerializer()
157
+ data = {"title": product.title, "is_competitor": not product.is_competitor}
158
+ assert serializer.validate(data)
159
+
160
+ def test_non_duplicate_company_product(self):
161
+ product = ProductFactory(is_competitor=True)
162
+ serializer = ProductModelSerializer()
163
+ data = {"title": product.title, "is_competitor": not product.is_competitor}
164
+ assert serializer.validate(data)
165
+
166
+ def test_duplicate_competitors_product(self):
167
+ product = ProductFactory(is_competitor=True)
168
+ serializer = ProductModelSerializer()
169
+ data = {"title": product.title, "is_competitor": product.is_competitor}
170
+ with pytest.raises(DjangoValidationError):
171
+ serializer.validate(data)
@@ -0,0 +1,95 @@
1
+ from datetime import timedelta
2
+
3
+ import pytest
4
+ from django.utils import timezone
5
+ from faker import Faker
6
+ from wbcore.contrib.directory.models import Company
7
+
8
+ from wbcrm.factories import ActivityTypeFactory
9
+ from wbcrm.models import Activity, ActivityType
10
+ from wbcrm.tasks import default_activity_heat_calculation
11
+
12
+ fake = Faker()
13
+
14
+
15
+ @pytest.mark.django_db
16
+ class TestSpecificTasks:
17
+ pass
18
+
19
+
20
+ @pytest.mark.django_db
21
+ class TestActivityHeatCalculationTasks:
22
+ @pytest.mark.parametrize("end", [fake.date_time()])
23
+ def test_high_activity_heat(self, company_factory, activity_factory, end):
24
+ end = timezone.make_aware(end)
25
+
26
+ high_activity_company_a = company_factory(name="Company A", activity_heat=0.0)
27
+ high_activity_company_b = company_factory(name="Company B", activity_heat=0.0)
28
+ high_activity_company_c = company_factory(name="Company C", activity_heat=0.0)
29
+ activity_factory(
30
+ status=Activity.Status.REVIEWED,
31
+ companies=[high_activity_company_a.id, high_activity_company_b.id],
32
+ type=ActivityTypeFactory(score=ActivityType.Score.HIGH),
33
+ start=end - timedelta(hours=2),
34
+ end=end - timedelta(hours=1),
35
+ )
36
+
37
+ activity_factory(
38
+ status=Activity.Status.REVIEWED,
39
+ companies=[high_activity_company_b.id],
40
+ type=ActivityTypeFactory(score=ActivityType.Score.LOW),
41
+ start=end - timedelta(hours=2),
42
+ end=end - timedelta(hours=1),
43
+ )
44
+
45
+ activity_factory(
46
+ status=Activity.Status.REVIEWED,
47
+ companies=[
48
+ high_activity_company_c.id,
49
+ ],
50
+ type=ActivityTypeFactory(score=ActivityType.Score.HIGH),
51
+ start=end - timedelta(days=1, hours=2),
52
+ end=end - timedelta(days=1, hours=1),
53
+ )
54
+
55
+ default_activity_heat_calculation(end)
56
+ high_activity_company_a: Company = Company.objects.get(id=high_activity_company_a.id)
57
+ high_activity_company_b: Company = Company.objects.get(id=high_activity_company_b.id)
58
+ high_activity_company_c: Company = Company.objects.get(id=high_activity_company_c.id)
59
+ date_score = 365 / 365
60
+ target_score_a = (float(ActivityType.Score.HIGH) * date_score) / float(ActivityType.Score.MAX)
61
+ target_score_b = (
62
+ (float(ActivityType.Score.HIGH) * date_score) + (float(ActivityType.Score.LOW) * date_score)
63
+ ) / float(ActivityType.Score.MAX)
64
+ target_score_c = (float(ActivityType.Score.HIGH) * (364 / 365)) / float(ActivityType.Score.MAX)
65
+ assert round(high_activity_company_a.activity_heat, 1) == round(target_score_a, 1)
66
+ assert round(high_activity_company_b.activity_heat, 1) == round(target_score_b, 1)
67
+ assert round(high_activity_company_c.activity_heat, 1) == round(target_score_c, 1)
68
+
69
+ @pytest.mark.parametrize("end", [fake.date_time()])
70
+ def test_low_activity_heat(self, company_factory, activity_factory, end):
71
+ end = timezone.make_aware(end)
72
+
73
+ low_activity_company_a = company_factory(name="Company A", activity_heat=0.0)
74
+ low_activity_company_b = company_factory(name="Company B", activity_heat=0.0)
75
+ activity_factory(
76
+ status=Activity.Status.REVIEWED,
77
+ companies=[low_activity_company_a.id],
78
+ type=ActivityTypeFactory(score=ActivityType.Score.LOW),
79
+ start=end - timedelta(hours=2),
80
+ end=end - timedelta(hours=1),
81
+ )
82
+ activity_factory(
83
+ status=Activity.Status.REVIEWED,
84
+ companies=[low_activity_company_b.id],
85
+ type=ActivityTypeFactory(score=ActivityType.Score.HIGH),
86
+ start=end - timedelta(hours=2),
87
+ end=end - timedelta(hours=1),
88
+ )
89
+
90
+ default_activity_heat_calculation(end)
91
+ low_activity_company_a = Company.objects.get(id=low_activity_company_a.id)
92
+ date_score = (365 - 100) / 365
93
+ target_score_a = (float(ActivityType.Score.LOW) * date_score) / float(ActivityType.Score.MAX)
94
+ assert round(low_activity_company_a.activity_heat, 1) == round(target_score_a, 1)
95
+ assert low_activity_company_b.activity_heat == 0.0