Example #1
0
    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)

        self.team_service = TeamService()

        # creating a new team
        # `_state.adding` is safer than pk/id check as that doesn't work for uuids with
        # a default value
        # https://docs.djangoproject.com/en/3.2/ref/models/instances/#other-attributes
        self.creating = self.instance._state.adding
        # editing an existing team
        self.editing = not self.creating

        if self.editing:
            parent_team = self.team_service.get_immediate_parent_team(
                self.instance)

            # parent_team will be None if the root team is being edited.
            if parent_team:
                self.initial.update(parent_team=parent_team.pk)

        self.is_root_team = self.team_service.get_root_team() == self.instance

        # parent_team is not required if we are editing the root team
        if self.is_root_team:
            self.fields["parent_team"].required = False
Example #2
0
    def get_context_data(self, **kwargs: dict) -> dict:
        context = super().get_context_data(**kwargs)

        team = context["team"]
        team_service = TeamService()

        context["parent_teams"] = team_service.get_all_parent_teams(team)

        return context
Example #3
0
def create_audit_log() -> None:
    team_service = TeamService()

    for team in Team.objects.all():
        team_service.team_created(team, created_by=None)

    person_service = PersonService()

    for person in Person.objects.all():
        person_service.profile_created(person, created_by=None)
Example #4
0
    def delete(self, request, *args, **kwargs):
        team = self.get_object()

        can_team_be_deleted, _ = TeamService().can_team_be_deleted(team)

        if not can_team_be_deleted:
            raise SuspiciousOperation("Team cannot be deleted")

        TeamService().team_deleted(team, self.request.user)

        return super().delete(request, *args, **kwargs)
Example #5
0
    def clean(self):
        cleaned_data = super().clean()

        team_service = TeamService()
        root_team = team_service.get_root_team()

        # Someone is trying to set a new head of the DIT.
        if cleaned_data[
                "team"] == root_team and cleaned_data["head_of_team"] is True:
            # If there already is one, don't let them do it.
            if root_team.members.filter(head_of_team=True).exists():
                self.add_error(None,
                               f"There is already a head of the {root_team}")

        return cleaned_data
Example #6
0
    def get_context_data(self, **kwargs: dict) -> dict:
        context = super().get_context_data(**kwargs)

        profile = context["profile"]
        roles = profile.roles.select_related("team").all()

        context["roles"] = roles
        context["title"] = profile.full_name

        if roles:
            # TODO: How do we know which team to select as the main one?
            team = roles[0].team
            context["team"] = team
            # TODO: `parent_teams` is common to all views. Perhaps we should
            # refactor this into a common base view or mixin?
            context["parent_teams"] = list(TeamService().get_all_parent_teams(team)) + [
                team
            ]

        if self.request.user == profile.user or self.request.user.has_perm(
            "peoplefinder.view_auditlog"
        ):
            context["profile_audit_log"] = AuditLogService.get_audit_log(profile)
            context["profile_audit_log_excluded_keys"] = [
                "user_id",
                "manager_id",
                "created_at",
                "updated_at",
                "edited_or_confirmed_at",
            ]

        return context
Example #7
0
    def get_context_data(self, **kwargs: dict) -> dict:
        context = super().get_context_data(**kwargs)

        (
            context["can_team_be_deleted"],
            context["reasons_team_cannot_be_deleted"],
        ) = TeamService().can_team_be_deleted(context["team"])

        return context
Example #8
0
    def get_context_data(self, **kwargs: dict) -> dict:
        context = super().get_context_data(**kwargs)

        page = self.request.GET.get("page", 1)

        team = context["team"]
        team_service = TeamService()

        context["parent_teams"] = team_service.get_all_parent_teams(team)
        context["sub_teams"] = team_service.get_all_child_teams(team)

        members = self.get_team_members(team, context["sub_teams"])
        paginator = Paginator(members, 40)
        context["team_members"] = paginator.page(page)
        context["page_numbers"] = list(paginator.get_elided_page_range(page))

        context["heading"] = self.heading

        return context
Example #9
0
    def get_context_data(self, **kwargs: dict) -> dict:
        context = super().get_context_data(**kwargs)

        team = context["team"]
        team_service = TeamService()

        context["parent_team"] = team_service.get_immediate_parent_team(team)
        context["is_root_team"] = team_service.get_root_team() == team

        members = [{
            "pk": member.pk,
            "name": member.person.full_name
        } for member in team.leaders]
        context["team_leaders_order_component"] = {
            "ordering": team.leaders_ordering,
            "members": members,
        }

        return context
Example #10
0
def migrate_teams():
    team_service = TeamService()

    count = 0
    queue = deque([None])

    while queue:
        ancestor = queue.popleft()

        if ancestor:
            groups = Groups.objects.filter(
                Q(ancestry=ancestor.pk) | Q(ancestry__endswith=f"/{ancestor.pk}")
            )
        else:
            groups = Groups.objects.filter(ancestry__isnull=True)

        queue.extend(groups)

        parent = get_team_for_legacy_group(ancestor) if ancestor else None

        for group in groups:
            team = Team.objects.create(
                name=group.name,
                abbreviation=group.acronym if group.acronym else None,
                slug=group.slug,
                description=group.description and group.description.strip(),
            )

            team_service.add_team(team, parent or team)

            count += 1

            if count % 100 == 0:
                logger.info("Created 100 teams")

    logger.info(f"Created {count} teams in total")
Example #11
0
    def __init__(self,
                 *args: tuple,
                 initial: Dict[str, Any] = None,
                 instance: Team = None,
                 **kwargs: dict) -> None:
        """Initialise the form with the correct parent team."""
        if not initial:
            initial = {}

        team = instance

        initial["parent_team"] = None

        if team:
            initial["parent_team"] = TeamService().get_immediate_parent_team(
                team)

        super().__init__(*args, initial=initial, instance=instance, **kwargs)
Example #12
0
    def form_valid(self, form):
        response = super().form_valid(form)

        # Update the order of the team leaders.
        if (self.object.leaders_ordering == Team.LeadersOrdering.ALPHABETICAL
                or not form.cleaned_data["leaders_positions"]):
            for member in self.object.members.all():
                member.leaders_position = None
                member.save()
        elif self.object.leaders_ordering == Team.LeadersOrdering.CUSTOM:
            for i, member_pk in enumerate(
                    form.cleaned_data["leaders_positions"]):
                member = TeamMember.objects.get(pk=member_pk)
                member.leaders_position = i
                member.save()

        if form.has_changed():
            TeamService().team_updated(self.object, self.request.user)

        return response
Example #13
0
    def get_context_data(self, **kwargs: dict) -> dict:
        context = super().get_context_data(**kwargs)

        team = context["team"]
        team_service = TeamService()

        context["parent_teams"] = team_service.get_all_parent_teams(team)
        context["sub_teams"] = team_service.get_immediate_child_teams(team)

        if self.request.user.has_perm("peoplefinder.delete_team"):
            (
                context["can_team_be_deleted"],
                context["reasons_team_cannot_be_deleted"],
            ) = team_service.can_team_be_deleted(team)

        # Must be a leaf team.
        if not context["sub_teams"]:
            context["members"] = team.members.all().order_by(
                "person__first_name", "person__last_name")
        else:
            context[
                "people_outside_subteams_count"] = TeamMember.objects.filter(
                    team=team).count()

            # Warning: Multiple requests per sub-team. This might need optimising in the
            # future.
            for sub_team in context["sub_teams"]:
                sub_team.avg_profile_completion = (
                    Person.objects.with_profile_completion().filter(teams__in=[
                        sub_team,
                        *team_service.get_all_child_teams(sub_team),
                    ]).aggregate(
                        Avg("profile_completion"))["profile_completion__avg"])

        if self.request.user.has_perms(
            ["peoplefinder.change_team", "peoplefinder.view_auditlog"]):
            context["team_audit_log"] = AuditLogService.get_audit_log(team)

        return context
Example #14
0
    def test_order_team_leaders(self, team_admin_user, software_team):
        red_leader = UserFactory(
            first_name="Red",
            last_name="Leader",
            email="*****@*****.**",
            legacy_sso_user_id=None,
            username="******",
            sso_contact_email="*****@*****.**",
        )
        gold_leader = UserFactory(
            first_name="Gold",
            last_name="Leader",
            email="*****@*****.**",
            legacy_sso_user_id=None,
            username="******",
            sso_contact_email="*****@*****.**",
        )

        call_command("create_user_profiles")

        red_leader_role, _ = red_leader.profile.roles.get_or_create(
            team=software_team,
            job_title="Product Manager",
            head_of_team=True,
        )
        gold_leader_role, _ = gold_leader.profile.roles.get_or_create(
            team=software_team,
            job_title="Delivery Manager",
            head_of_team=True,
        )

        r = self.client.get(reverse("team-edit", kwargs={"slug": "software"}))

        assert (r.context["team_leaders_order_component"]["ordering"] ==
                Team.LeadersOrdering.ALPHABETICAL)

        members = r.context["team_leaders_order_component"]["members"]

        assert len(members) == 2
        assert members[0]["pk"] == gold_leader_role.pk
        assert members[1]["pk"] == red_leader_role.pk

        r = self.client.post(
            reverse("team-edit", kwargs={"slug": "software"}),
            data={
                "name":
                software_team.name,
                "abbreviation":
                software_team.abbreviation or "",
                "description":
                software_team.description,
                "parent_team":
                (TeamService().get_immediate_parent_team(software_team).pk),
                "leaders_ordering":
                Team.LeadersOrdering.CUSTOM,
                "leaders_positions":
                ",".join(map(str, [red_leader_role.pk, gold_leader_role.pk])),
            },
            follow=True,
        )
        assert r.status_code == 200

        red_leader_role.refresh_from_db()
        gold_leader_role.refresh_from_db()

        assert red_leader_role.leaders_position == 0
        assert gold_leader_role.leaders_position == 1
Example #15
0
def test_team_service(db):
    """
    .
    └── DIT
        ├── COO
        │   ├── Analysis
        │   └── Change
        └── GTI
            ├── Investment
            └── DEFEND
    """
    test_case = unittest.TestCase()

    # We need to start from fresh for these tests.
    Team.objects.all().delete()

    team_service = TeamService()

    dit = Team.objects.create(name="DIT", slug="dit")
    coo = Team.objects.create(name="COO", slug="coo")
    gti = Team.objects.create(name="GTI", slug="gti")
    team_service.add_team(team=dit, parent=dit)
    team_service.add_team(team=coo, parent=dit)
    team_service.add_team(team=gti, parent=dit)

    coo_analysis = Team.objects.create(name="Analysis", slug="analysis")
    coo_change = Team.objects.create(name="Change", slug="change")
    team_service.add_team(team=coo_analysis, parent=coo)
    team_service.add_team(team=coo_change, parent=coo)

    gti_investment = Team.objects.create(name="Investment", slug="investment")
    gti_defence = Team.objects.create(name="DEFEND", slug="defend")
    team_service.add_team(team=gti_investment, parent=gti)
    team_service.add_team(team=gti_defence, parent=gti)

    test_case.assertCountEqual(
        list(team_service.get_all_child_teams(parent=dit)),
        [
            coo,
            gti,
            coo_analysis,
            coo_change,
            gti_investment,
            gti_defence,
        ],
    )

    test_case.assertCountEqual(
        list(team_service.get_immediate_child_teams(parent=dit)), [coo, gti])

    test_case.assertCountEqual(
        list(team_service.get_all_parent_teams(child=coo_change)), [dit, coo])

    assert team_service.get_root_team() == dit

    assert team_service.get_immediate_parent_team(gti_defence) == gti

    # test update
    team_service.update_team_parent(gti, coo)

    assert team_service.get_immediate_parent_team(gti) == coo

    test_case.assertCountEqual(
        list(team_service.get_all_child_teams(coo)),
        [
            gti,
            coo_analysis,
            coo_change,
            gti_investment,
            gti_defence,
        ],
    )

    test_case.assertCountEqual(
        list(team_service.get_immediate_child_teams(dit)), [coo])

    # revert update
    team_service.update_team_parent(gti, dit)

    assert team_service.get_immediate_parent_team(gti) == dit

    # test team select methods
    assert list(team_service.get_team_select_data()) == [
        {
            "team_id": dit.id,
            "team_name": dit.name,
            "parent_id": None,
            "parent_name": None,
        },
        {
            "team_id": coo.id,
            "team_name": coo.name,
            "parent_id": dit.id,
            "parent_name": dit.name,
        },
        {
            "team_id": gti.id,
            "team_name": gti.name,
            "parent_id": dit.id,
            "parent_name": dit.name,
        },
        {
            "team_id": coo_analysis.id,
            "team_name": coo_analysis.name,
            "parent_id": coo.id,
            "parent_name": coo.name,
        },
        {
            "team_id": coo_change.id,
            "team_name": coo_change.name,
            "parent_id": coo.id,
            "parent_name": coo.name,
        },
        {
            "team_id": gti_investment.id,
            "team_name": gti_investment.name,
            "parent_id": gti.id,
            "parent_name": gti.name,
        },
        {
            "team_id": gti_defence.id,
            "team_name": gti_defence.name,
            "parent_id": gti.id,
            "parent_name": gti.name,
        },
    ]

    # test `validate_team_parent_update` through `update_team_parent`
    with pytest.raises(TeamServiceError,
                       match="A team's parent cannot be the team itself"):
        team_service.update_team_parent(gti, gti)

    with pytest.raises(TeamServiceError,
                       match="A team's parent cannot be a team's child"):
        team_service.update_team_parent(gti, gti_investment)

    with pytest.raises(TeamServiceError,
                       match="Cannot update the parent of the root team"):
        team_service.update_team_parent(dit, Team(name="Test"))

    # test `generate_team_slug`
    assert team_service.generate_team_slug(coo_analysis) == "analysis"

    coo_analysis.name = "investment"

    assert team_service.generate_team_slug(coo_analysis) == "coo-investment"

    coo_analysis.name = "analysis"
Example #16
0
    def save_model(self, request, obj, form, change):  # type: ignore
        """Save the model and update the team hierarchy."""
        super().save_model(request, obj, form, change)

        team_service = TeamService()

        current_parent_team = team_service.get_immediate_parent_team(obj)
        new_parent_team = form.cleaned_data["parent_team"]

        if change:
            if current_parent_team != new_parent_team:
                team_service.update_team_parent(obj, new_parent_team)
                team_service.team_updated(obj, request.user)
        else:
            team_service.add_team(obj, new_parent_team)
            team_service.team_created(obj, request.user)
Example #17
0
    def handle(self, *args, **options):
        team_service = TeamService()

        # SpaceX
        spacex, spacex_created = Team.objects.get_or_create(name="SpaceX",
                                                            slug="spacex")
        astronauts, astronauts_created = Team.objects.get_or_create(
            name="Astronauts", slug="astronauts")
        engineering, engineering_created = Team.objects.get_or_create(
            name="Engineering", slug="engineering")
        software, software_created = Team.objects.get_or_create(
            name="Software", slug="software")
        hr, hr_created = Team.objects.get_or_create(name="Human Resources",
                                                    abbreviation="HR",
                                                    slug="human-resources")
        catering, catering_created = Team.objects.get_or_create(
            name="Catering", slug="catering")
        bakery, bakery_created = Team.objects.get_or_create(name="Bakery",
                                                            slug="bakery")

        if spacex_created:
            team_service.add_team(spacex, spacex)
            team_service.team_created(spacex, created_by=None)

        if astronauts_created:
            team_service.add_team(astronauts, spacex)
            team_service.team_created(astronauts, created_by=None)

        if engineering_created:
            team_service.add_team(engineering, spacex)
            team_service.team_created(engineering, created_by=None)

        if software_created:
            team_service.add_team(software, engineering)
            team_service.team_created(software, created_by=None)

        if hr_created:
            team_service.add_team(hr, spacex)
            team_service.team_created(hr, created_by=None)

        if catering_created:
            team_service.add_team(catering, spacex)
            team_service.team_created(catering, created_by=None)

        if bakery_created:
            team_service.add_team(bakery, catering)
            team_service.team_created(bakery, created_by=None)

        self.stdout.write(self.style.SUCCESS("Job completed successfully"))
        self.stdout.write(self.style.SUCCESS(TREE_HELP))
Example #18
0
class TeamForm(forms.ModelForm):
    use_required_attribute = False

    class Meta:
        model = Team
        fields = [
            "name",
            "abbreviation",
            "description",
            "parent_team",
            "leaders_ordering",
            "leaders_positions",
        ]

    parent_team = forms.IntegerField()
    # Example: "23,56,34"
    leaders_positions = forms.CharField(required=False, empty_value=None)

    def __init__(self, *args, **kwargs) -> None:
        super().__init__(*args, **kwargs)

        self.team_service = TeamService()

        # creating a new team
        # `_state.adding` is safer than pk/id check as that doesn't work for uuids with
        # a default value
        # https://docs.djangoproject.com/en/3.2/ref/models/instances/#other-attributes
        self.creating = self.instance._state.adding
        # editing an existing team
        self.editing = not self.creating

        if self.editing:
            parent_team = self.team_service.get_immediate_parent_team(
                self.instance)

            # parent_team will be None if the root team is being edited.
            if parent_team:
                self.initial.update(parent_team=parent_team.pk)

        self.is_root_team = self.team_service.get_root_team() == self.instance

        # parent_team is not required if we are editing the root team
        if self.is_root_team:
            self.fields["parent_team"].required = False

    def clean_leaders_positions(self):
        leaders_positions = self.cleaned_data["leaders_positions"]

        if not leaders_positions:
            return None

        return list(map(int, leaders_positions.split(",")))

    def clean(self):
        cleaned_data = super().clean()

        self.new_parent_team = None

        if cleaned_data["parent_team"]:
            try:
                self.new_parent_team = Team.objects.get(
                    pk=cleaned_data["parent_team"])
            except Team.DoesNotExist:
                self.add_error("parent_team", "Invalid parent team")

        if (self.editing and ("parent_team" in self.changed_data)
                and self.new_parent_team):
            try:
                self.team_service.validate_team_parent_update(
                    self.instance, self.new_parent_team)
            except TeamServiceError as e:
                self.add_error("parent_team", str(e))

        return cleaned_data

    def save(self, commit=True):
        super().save(commit=commit)

        if self.creating:
            self.team_service.add_team(self.instance, self.new_parent_team)
        # editing and...
        elif "parent_team" in self.changed_data:
            self.team_service.update_team_parent(self.instance,
                                                 self.new_parent_team)

        # This depends on the team's parent so it has to happen after we update the team
        # tree.
        if "name" in self.changed_data:
            self.instance.slug = self.team_service.generate_team_slug(
                self.instance)
            self.instance.save()

        return self.instance
Example #19
0
    def form_valid(self, form):
        response = super().form_valid(form)

        TeamService().team_created(self.object, self.request.user)

        return response
Example #20
0
    def get(self, request: HttpRequest) -> HttpResponse:
        root_team = TeamService().get_root_team()

        return redirect(reverse("team-view", kwargs={"slug": root_team.slug}))
Example #21
0
    def get(self, request):
        team_select_data = list(TeamService().get_team_select_data())

        return JsonResponse(team_select_data, safe=False)