• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

chiefonboarding / ChiefOnboarding / 6700616467

31 Oct 2023 01:00AM UTC coverage: 92.452% (-1.2%) from 93.66%
6700616467

Pull #383

github

web-flow
Merge 300d02535 into fb838f71e
Pull Request #383: Add offboarding sequences

6161 of 6664 relevant lines covered (92.45%)

0.92 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.11
back/admin/sequences/tasks.py
1
from datetime import timedelta
1✔
2

3
from django.contrib.auth import get_user_model
1✔
4
from django.utils import timezone
1✔
5
from django.utils.translation import gettext as _
1✔
6
from django_q.tasks import async_task
1✔
7

8
from admin.badges.models import Badge
1✔
9
from admin.introductions.models import Introduction
1✔
10
from admin.sequences.emails import send_sequence_update_message
1✔
11
from admin.sequences.models import Condition
1✔
12
from organization.models import Notification, Organization
1✔
13
from slack_bot.slack_intro import SlackIntro
1✔
14
from slack_bot.slack_resource import SlackResource
1✔
15
from slack_bot.slack_to_do import SlackToDo
1✔
16
from slack_bot.utils import Slack, paragraph
1✔
17
from users.models import ResourceUser, ToDoUser
1✔
18

19

20
def process_condition(condition_id, user_id, send_email=True):
1✔
21
    """
22
    Processing triggered condition
23

24
    :param condition_id int: the condition that got triggered
25
    :param user_id int: the user that it got triggered for
26
    :param send_email bool: should send update email (not for portal)
27
    """
28

29
    condition = Condition.objects.get(id=condition_id)
1✔
30
    user = get_user_model().objects.get(id=user_id)
1✔
31
    condition.process_condition(user)
1✔
32

33
    # Send notifications to user
34
    notifications = Notification.objects.filter(
1✔
35
        notification_type__in=[
36
            Notification.Type.ADDED_TODO,
37
            Notification.Type.ADDED_RESOURCE,
38
            Notification.Type.ADDED_BADGE,
39
            Notification.Type.ADDED_INTRODUCTION,
40
        ],
41
        created_for=user,
42
        notified_user=False,
43
    )
44

45
    if not notifications.exists():
1✔
46
        return
1✔
47

48
    if user.has_slack_account:
1✔
49
        to_do_blocks = [
1✔
50
            SlackToDo(
51
                ToDoUser.objects.get(to_do__id=notif.item_id, user=user), user
52
            ).get_block()
53
            for notif in notifications.filter(
54
                notification_type=Notification.Type.ADDED_TODO
55
            )
56
        ]
57

58
        resource_blocks = [
1✔
59
            SlackResource(
60
                ResourceUser.objects.get(user=user, resource__id=notif.item_id), user
61
            ).get_block()
62
            for notif in notifications.filter(
63
                notification_type=Notification.Type.ADDED_RESOURCE
64
            )
65
        ]
66

67
        badge_blocks = []
1✔
68
        for notif in notifications.filter(
1✔
69
            notification_type=Notification.Type.ADDED_BADGE
70
        ):
71
            badge_blocks.append(
1✔
72
                paragraph(
73
                    _("*Congrats, you unlocked: %(item_name)s *")
74
                    % {
75
                        "item_name": user.personalize(
76
                            Badge.objects.get(id=notif.item_id).name
77
                        ),
78
                    },
79
                ),
80
            )
81
            badge_blocks += Badge.objects.get(id=notif.item_id).to_slack_block(user)
1✔
82

83
        intro_blocks = [
1✔
84
            SlackIntro(Introduction.objects.get(id=notif.item_id), user).format_block()
85
            for notif in notifications.filter(
86
                notification_type=Notification.Type.ADDED_INTRODUCTION
87
            )
88
        ]
89

90
        if len(to_do_blocks):
1✔
91
            # Send to do items separate as we need to update this block
92
            Slack().send_message(
1✔
93
                text=_("Here are some new items for you!"),
94
                blocks=[
95
                    paragraph(_("Here are some new items for you!")),
96
                    *to_do_blocks,
97
                ],
98
                channel=user.slack_user_id,
99
            )
100

101
            if len(resource_blocks) or len(badge_blocks) or len(intro_blocks):
1✔
102
                Slack().send_message(
×
103
                    text=_("Here are some new items for you!"),
104
                    blocks=[
105
                        *intro_blocks,
106
                        *badge_blocks,
107
                        *resource_blocks,
108
                    ],
109
                    channel=user.slack_user_id,
110
                )
111

112
        else:
113
            Slack().send_message(
1✔
114
                text=_("Here are some new items for you!"),
115
                blocks=[
116
                    paragraph(_("Here are some new items for you!")),
117
                    *intro_blocks,
118
                    *badge_blocks,
119
                    *resource_blocks,
120
                ],
121
                channel=user.slack_user_id,
122
            )
123
    elif send_email:
1✔
124
        send_sequence_update_message(notifications, user)
1✔
125

126
    # Update notifications to not notify user again
127
    notifications.update(notified_user=True)
1✔
128

129
    # Update user amount completed
130
    user.update_progress()
1✔
131

132

133
def timed_triggers():
1✔
134
    """
135
    This gets triggered every 5 minutes to trigger conditions within sequences.
136
    These conditions are already assigned to new hires.
137
    """
138
    org = Organization.object.get()
1✔
139
    if org is None:
1✔
140
        return
×
141

142
    current_datetime = timezone.now()
1✔
143
    last_updated = org.timed_triggers_last_check
1✔
144

145
    # Round downwards (based on 5 minutes) - check if we might not be on 5/0 anymore.
146
    # A time of 16 minutes becomes 15
147
    off_by_minutes = current_datetime.minute % 5
1✔
148
    current_datetime = current_datetime.replace(
1✔
149
        minute=current_datetime.minute - off_by_minutes, second=0, microsecond=0
150
    )
151

152
    # Same for last_updated (though, this should always be rounded on 5 or 0 already)
153
    off_by_minutes = last_updated.minute % 5
1✔
154
    last_updated = last_updated.replace(
1✔
155
        minute=last_updated.minute - off_by_minutes, second=0, microsecond=0
156
    )
157

158
    # Generally this loop will only go through once. In the case of an outage, it will
159
    # walk through all the 5 minutes that it needs to catch up on based on the last
160
    # updated variable
161
    while current_datetime > last_updated:
1✔
162
        last_updated += timedelta(minutes=5)
1✔
163
        org.timed_triggers_last_check = last_updated
1✔
164
        org.save()
1✔
165

166
        for user in get_user_model().new_hires.all():
1✔
167
            amount_days = user.workday
1✔
168
            amount_days_before = user.days_before_starting
1✔
169
            current_time = user.get_local_time(last_updated).time()
1✔
170

171
            # Get conditions before/after they started
172
            # Generally, this should be only one, but just in case, we can handle more
173
            conditions = Condition.objects.none()
1✔
174
            if amount_days == 0:
1✔
175
                # Before starting
176
                conditions = user.conditions.filter(
×
177
                    condition_type=Condition.Type.BEFORE,
178
                    days=amount_days_before,
179
                    time=current_time,
180
                )
181
            elif user.get_local_time(last_updated).weekday() < 5:
1✔
182
                # On workday x
183
                conditions = user.conditions.filter(
1✔
184
                    condition_type=Condition.Type.AFTER,
185
                    days=amount_days,
186
                    time=current_time,
187
                )
188

189
            # Schedule conditions to be executed with new scheduled task, we do this to
190
            # avoid long standing tasks. I.e. sending lots of emails might take more
191
            # time.
192
            for i in conditions:
1✔
193
                async_task(
1✔
194
                    process_condition,
195
                    i.id,
196
                    user.id,
197
                    task_name=f"Process condition: {i.id} for {user.full_name}",
198
                )
199

200
        for user in get_user_model().offboarding.all():
1✔
201
            amount_days_before = user.days_before_termination_date
×
202

203
            if (
×
204
                amount_days_before == -1
205
                or user.get_local_time(last_updated).weekday() < 5
206
            ):
207
                # we are past the termination date or in a weekend, move to the next
208
                continue
×
209

210
            current_time = user.get_local_time(last_updated).time()
×
211
            conditions = user.conditions.filter(
×
212
                condition_type=Condition.Type.BEFORE,
213
                days=amount_days_before,
214
                time=current_time,
215
            )
216

217
            # Schedule conditions to be executed with new scheduled task, we do this to
218
            # avoid long standing tasks. I.e. sending lots of emails might take more
219
            # time.
220
            for i in conditions:
×
221
                async_task(
×
222
                    process_condition,
223
                    i.id,
224
                    user.id,
225
                    task_name=f"Process condition: {i.id} for {user.full_name}",
226
                )
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc