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,174 @@
1
+ from datetime import datetime
2
+
3
+ import pytest
4
+ from psycopg.types.range import TimestamptzRange
5
+ from wbcore.contrib.agenda.factories import ConferenceRoomFactory
6
+
7
+ from wbcrm.models.recurrence import Recurrence
8
+ from wbcrm.synchronization.activity.backends.outlook.parser import OutlookParser
9
+
10
+ from .fixtures import TestOutlookSyncFixture
11
+
12
+
13
+ @pytest.mark.django_db
14
+ class TestOutlookSyncParser(TestOutlookSyncFixture):
15
+ def test_convert_string_to_datetime(self, organizer_event_fixture_parsed):
16
+ start = OutlookParser.convert_string_to_datetime(
17
+ organizer_event_fixture_parsed["start.date_time"], organizer_event_fixture_parsed["start.time_zone"]
18
+ )
19
+ assert isinstance(start, datetime)
20
+
21
+ def test_convert_to_all_day_period(self, organizer_event_fixture_parsed):
22
+ start = OutlookParser.convert_string_to_datetime(
23
+ organizer_event_fixture_parsed["start.date_time"], organizer_event_fixture_parsed["start.time_zone"]
24
+ )
25
+ end = OutlookParser.convert_string_to_datetime(
26
+ organizer_event_fixture_parsed["end.date_time"], organizer_event_fixture_parsed["end.time_zone"]
27
+ )
28
+ period = OutlookParser.convert_to_all_day_period(TimestamptzRange(start, end))
29
+ assert period
30
+ assert isinstance(period, TimestamptzRange)
31
+
32
+ def test_deserialize_participants(self, organizer_event_fixture_parsed):
33
+ participants, conference_rooms = OutlookParser.deserialize_participants(organizer_event_fixture_parsed)
34
+ assert len(conference_rooms) == 0
35
+ assert len(participants) == len(organizer_event_fixture_parsed["attendees"]) + 1 # include the organizer
36
+
37
+ def test_deserialize_recurring_activities(
38
+ self, organizer_event_fixture_parsed, organizer_master_event_fixture_parsed
39
+ ):
40
+ assert OutlookParser.deserialize_recurring_activities(organizer_event_fixture_parsed) == {
41
+ "recurrence_count": 0,
42
+ "recurrence_end": None,
43
+ "repeat_choice": "NEVER",
44
+ }
45
+ assert OutlookParser.deserialize_recurring_activities(organizer_master_event_fixture_parsed) != {
46
+ "recurrence_count": 0,
47
+ "recurrence_end": None,
48
+ "repeat_choice": "NEVER",
49
+ }
50
+
51
+ def test_deserialize_person(self, organizer_event_fixture_parsed):
52
+ person_dto = OutlookParser.deserialize_person("test@test.ch", "test")
53
+ person_dto1 = OutlookParser.deserialize_person("test@test.ch", "first_name_test last_name_test")
54
+ person_dto2 = OutlookParser.deserialize_person(
55
+ organizer_event_fixture_parsed["organizer.email_address.address"],
56
+ organizer_event_fixture_parsed["organizer.email_address.name"],
57
+ )
58
+ assert person_dto.email == person_dto1.email == "test@test.ch"
59
+ assert person_dto2.email == organizer_event_fixture_parsed["organizer.email_address.address"]
60
+ assert person_dto.first_name == person_dto.last_name == "test"
61
+ assert person_dto1.first_name == "first_name_test"
62
+ assert person_dto1.last_name == "last_name_test"
63
+
64
+ @pytest.mark.parametrize(
65
+ "visibility, sensitivity",
66
+ [("PUBLIC", "normal"), ("PRIVATE", "private"), ("CONFIDENTIAL", "confidential"), ("PUBLIC", None)],
67
+ )
68
+ def test_convert_sensitivity_to_visibility(self, visibility, sensitivity):
69
+ assert OutlookParser.convert_sensitivity_to_visibility(sensitivity) == visibility
70
+
71
+ @pytest.mark.parametrize(
72
+ "visibility, sensitivity",
73
+ [("PUBLIC", "normal"), ("PRIVATE", "private"), ("CONFIDENTIAL", "confidential"), (None, "normal")],
74
+ )
75
+ def test_convert_visibility_to_sensitivity(self, visibility, sensitivity):
76
+ assert OutlookParser.convert_visibility_to_sensitivity(visibility) == sensitivity
77
+
78
+ @pytest.mark.parametrize(
79
+ "reminder_choice, reminder_minute",
80
+ [
81
+ ("NEVER", -1),
82
+ ("EVENT_TIME", 0),
83
+ ("MINUTES_5", 5),
84
+ ("MINUTES_15", 15),
85
+ ("MINUTES_30", 30),
86
+ ("HOURS_1", 60),
87
+ ("HOURS_2", 120),
88
+ ("HOURS_12", 720),
89
+ ("WEEKS_1", 10080),
90
+ ("MINUTES_15", None),
91
+ ],
92
+ )
93
+ def test_convert_reminder_minutes_to_choice(self, reminder_choice, reminder_minute):
94
+ assert OutlookParser.convert_reminder_minutes_to_choice(reminder_minute) == reminder_choice
95
+
96
+ @pytest.mark.parametrize(
97
+ "reminder_choice, reminder_minute",
98
+ [
99
+ ("NEVER", -1),
100
+ ("EVENT_TIME", 0),
101
+ ("MINUTES_5", 5),
102
+ ("MINUTES_15", 15),
103
+ ("MINUTES_30", 30),
104
+ ("HOURS_1", 60),
105
+ ("HOURS_2", 120),
106
+ ("HOURS_12", 720),
107
+ ("WEEKS_1", 10080),
108
+ ],
109
+ )
110
+ def test_convert_reminder_choice_to_minutes(self, reminder_choice, reminder_minute):
111
+ assert OutlookParser.convert_reminder_choice_to_minutes(reminder_choice) == reminder_minute
112
+
113
+ def test_serialize_person(self, person_factory):
114
+ person_dto = person_factory()._build_dto()
115
+ person_dict = OutlookParser.serialize_person(person_dto)
116
+ assert set(person_dict.keys()) == {"emailAddress"}
117
+ assert person_dict["emailAddress"]["address"] == person_dto.email
118
+ assert person_dict["emailAddress"]["name"] == f"{person_dto.first_name} {person_dto.last_name}"
119
+
120
+ @pytest.mark.parametrize(
121
+ "participant_status, attendee_status",
122
+ [
123
+ ("NOTRESPONDED", "notResponded"),
124
+ ("ATTENDS", "accepted"),
125
+ ("CANCELLED", "declined"),
126
+ ("MAYBE", "tentativelyAccepted"),
127
+ ("ATTENDS_DIGITALLY", "accepted"),
128
+ ],
129
+ )
130
+ def test_convert_participant_status_to_attendee_status(self, participant_status, attendee_status):
131
+ assert OutlookParser.convert_participant_status_to_attendee_status(participant_status) == attendee_status
132
+
133
+ @pytest.mark.parametrize(
134
+ "participant_status, attendee_status",
135
+ [
136
+ ("NOTRESPONDED", "notResponded"),
137
+ ("ATTENDS", "accepted"),
138
+ ("CANCELLED", "declined"),
139
+ ("MAYBE", "tentativelyAccepted"),
140
+ ("NOTRESPONDED", None),
141
+ ("ATTENDS", "organizer"),
142
+ ],
143
+ )
144
+ def test_convert_attendee_status_to_participant_status(self, participant_status, attendee_status):
145
+ assert OutlookParser.convert_attendee_status_to_participant_status(attendee_status) == participant_status
146
+
147
+ def test_serialize_participants(self, activity_factory, person_factory):
148
+ activity = activity_factory(preceded_by=None, participants=(person_factory(),))
149
+ activity_dto = activity._build_dto()
150
+ assert activity_dto.participants
151
+ attendees = OutlookParser.serialize_participants(activity_dto.participants)
152
+ assert attendees
153
+ assert attendees[0]
154
+
155
+ def test_serialize_conference_room(self):
156
+ cf_dto = ConferenceRoomFactory()._build_dto()
157
+ cf_dict = OutlookParser.serialize_conference_room(cf_dto)
158
+ assert set(cf_dict.keys()) == {"emailAddress", "type"}
159
+ assert cf_dict["type"] == "resource"
160
+ assert cf_dict["emailAddress"]["address"] == cf_dto.email
161
+ assert cf_dict["emailAddress"]["name"] == cf_dto.name
162
+
163
+ def test_serialize_recurring_activities(self, activity_factory):
164
+ act_dto0 = activity_factory()._build_dto()
165
+ act_dto = activity_factory(
166
+ repeat_choice=Recurrence.ReoccuranceChoice.DAILY, recurrence_count=3, preceded_by=None
167
+ )._build_dto()
168
+ reccurence_dict = OutlookParser.serialize_recurring_activities(act_dto)
169
+ assert OutlookParser.serialize_recurring_activities(act_dto0) == {}
170
+ assert set(reccurence_dict.keys()) == {"recurrence"}
171
+ assert reccurence_dict["recurrence"]["pattern"]["type"] == "daily"
172
+ assert reccurence_dict["recurrence"]["range"]["type"] == "numbered"
173
+ assert reccurence_dict["recurrence"]["range"]["numberOfOccurrences"] == 4
174
+ assert reccurence_dict["recurrence"]["range"]["startDate"] == act_dto.period.lower.strftime("%Y-%m-%d")