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 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
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)
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)
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
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
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
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
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
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")
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)
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
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
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
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"
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)
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))
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
def form_valid(self, form): response = super().form_valid(form) TeamService().team_created(self.object, self.request.user) return response
def get(self, request: HttpRequest) -> HttpResponse: root_team = TeamService().get_root_team() return redirect(reverse("team-view", kwargs={"slug": root_team.slug}))
def get(self, request): team_select_data = list(TeamService().get_team_select_data()) return JsonResponse(team_select_data, safe=False)