mirror of
https://github.com/cisagov/manage.get.gov.git
synced 2025-08-01 23:42:17 +02:00
Replace invitation date => joined date
This commit is contained in:
parent
4ab526ceb5
commit
981cdb31b6
3 changed files with 31 additions and 55 deletions
|
@ -805,6 +805,7 @@ class ExportDataTest(MockDbForIndividualTests, MockEppLib):
|
||||||
class MemberExportTest(MockDbForIndividualTests, MockEppLib):
|
class MemberExportTest(MockDbForIndividualTests, MockEppLib):
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
"""Override of the base setUp to add a request factory"""
|
||||||
super().setUp()
|
super().setUp()
|
||||||
self.factory = RequestFactory()
|
self.factory = RequestFactory()
|
||||||
|
|
||||||
|
@ -813,6 +814,12 @@ class MemberExportTest(MockDbForIndividualTests, MockEppLib):
|
||||||
@less_console_noise_decorator
|
@less_console_noise_decorator
|
||||||
def test_member_export(self):
|
def test_member_export(self):
|
||||||
"""Tests the member export report by comparing the csv output."""
|
"""Tests the member export report by comparing the csv output."""
|
||||||
|
# == Data setup == #
|
||||||
|
# Set last_login for some users
|
||||||
|
active_date = timezone.make_aware(datetime(2024, 2, 1))
|
||||||
|
User.objects.filter(id__in=[self.custom_superuser.id, self.custom_staffuser.id]).update(last_login=active_date)
|
||||||
|
|
||||||
|
# Create a logentry for meoward, created by lebowski to test invited_by.
|
||||||
content_type = ContentType.objects.get_for_model(PortfolioInvitation)
|
content_type = ContentType.objects.get_for_model(PortfolioInvitation)
|
||||||
LogEntry.objects.create(
|
LogEntry.objects.create(
|
||||||
user=self.lebowski_user,
|
user=self.lebowski_user,
|
||||||
|
@ -824,8 +831,7 @@ class MemberExportTest(MockDbForIndividualTests, MockEppLib):
|
||||||
action_time=timezone.make_aware(datetime(2023, 4, 12)),
|
action_time=timezone.make_aware(datetime(2023, 4, 12)),
|
||||||
)
|
)
|
||||||
|
|
||||||
# Create log entries for each remaining invitation.
|
# Create log entries for each remaining invitation. Exclude meoward and tired_user.
|
||||||
# Exclude meoward and tired_user (to test null dates, etc).
|
|
||||||
for invitation in PortfolioInvitation.objects.exclude(
|
for invitation in PortfolioInvitation.objects.exclude(
|
||||||
id__in=[self.portfolio_invitation_1.id, self.portfolio_invitation_3.id]
|
id__in=[self.portfolio_invitation_1.id, self.portfolio_invitation_3.id]
|
||||||
):
|
):
|
||||||
|
@ -838,9 +844,7 @@ class MemberExportTest(MockDbForIndividualTests, MockEppLib):
|
||||||
change_message="Created invitation",
|
change_message="Created invitation",
|
||||||
action_time=timezone.make_aware(datetime(2024, 1, 15)),
|
action_time=timezone.make_aware(datetime(2024, 1, 15)),
|
||||||
)
|
)
|
||||||
# Set last_login for some users
|
|
||||||
active_date = timezone.make_aware(datetime(2024, 2, 1))
|
|
||||||
User.objects.filter(id__in=[self.custom_superuser.id, self.custom_staffuser.id]).update(last_login=active_date)
|
|
||||||
# Retrieve invitations
|
# Retrieve invitations
|
||||||
with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class):
|
with boto3_mocking.clients.handler_for("sesv2", self.mock_client_class):
|
||||||
self.meoward_user.check_portfolio_invitations_on_login()
|
self.meoward_user.check_portfolio_invitations_on_login()
|
||||||
|
@ -849,6 +853,12 @@ class MemberExportTest(MockDbForIndividualTests, MockEppLib):
|
||||||
self.custom_superuser.check_portfolio_invitations_on_login()
|
self.custom_superuser.check_portfolio_invitations_on_login()
|
||||||
self.custom_staffuser.check_portfolio_invitations_on_login()
|
self.custom_staffuser.check_portfolio_invitations_on_login()
|
||||||
|
|
||||||
|
# Update the created at date on UserPortfolioPermission, so we can test a consistent date.
|
||||||
|
UserPortfolioPermission.objects.filter(portfolio=self.portfolio_1).update(
|
||||||
|
created_at=timezone.make_aware(datetime(2022, 4, 1))
|
||||||
|
)
|
||||||
|
# == End of data setup == #
|
||||||
|
|
||||||
# Create a request and add the user to the request
|
# Create a request and add the user to the request
|
||||||
request = self.factory.get("/")
|
request = self.factory.get("/")
|
||||||
request.user = self.user
|
request.user = self.user
|
||||||
|
@ -867,20 +877,20 @@ class MemberExportTest(MockDbForIndividualTests, MockEppLib):
|
||||||
csv_content = csv_file.read()
|
csv_content = csv_file.read()
|
||||||
expected_content = (
|
expected_content = (
|
||||||
# Header
|
# Header
|
||||||
"Email,Organization admin,Invited by,Invitation date,Last active,Domain requests,"
|
"Email,Organization admin,Invited by,Joined date,Last active,Domain requests,"
|
||||||
"Member management,Domain management,Number of domains,Domains\n"
|
"Member management,Domain management,Number of domains,Domains\n"
|
||||||
# Content
|
# Content
|
||||||
"meoward@rocks.com,False,big_lebowski@dude.co,2023-04-12,Invalid date,None,"
|
"meoward@rocks.com,False,big_lebowski@dude.co,2022-04-01,Invalid date,None,"
|
||||||
'Manager,True,2,"adomain2.gov,cdomain1.gov"\n'
|
'Manager,True,2,"adomain2.gov,cdomain1.gov"\n'
|
||||||
"big_lebowski@dude.co,False,help@get.gov,2024-01-15,Invalid date,None,Viewer,True,1,cdomain1.gov\n"
|
"big_lebowski@dude.co,False,help@get.gov,2022-04-01,Invalid date,None,Viewer,True,1,cdomain1.gov\n"
|
||||||
"tired_sleepy@igorville.gov,False,System,Unknown,Invalid date,Viewer,None,False,0,\n"
|
"tired_sleepy@igorville.gov,False,System,2022-04-01,Invalid date,Viewer,None,False,0,\n"
|
||||||
"icy_superuser@igorville.gov,True,help@get.gov,2024-01-15,2024-02-01,Viewer Requester,Manager,False,0,\n"
|
"icy_superuser@igorville.gov,True,help@get.gov,2022-04-01,2024-02-01,Viewer Requester,Manager,False,0,\n"
|
||||||
"cozy_staffuser@igorville.gov,True,help@get.gov,2024-01-15,2024-02-01,Viewer Requester,None,False,0,\n"
|
"cozy_staffuser@igorville.gov,True,help@get.gov,2022-04-01,2024-02-01,Viewer Requester,None,False,0,\n"
|
||||||
"nonexistentmember_1@igorville.gov,False,help@get.gov,2024-01-15,Invited,None,Manager,False,0,\n"
|
"nonexistentmember_1@igorville.gov,False,help@get.gov,Unretrieved,Invited,None,Manager,False,0,\n"
|
||||||
"nonexistentmember_2@igorville.gov,False,help@get.gov,2024-01-15,Invited,None,Viewer,False,0,\n"
|
"nonexistentmember_2@igorville.gov,False,help@get.gov,Unretrieved,Invited,None,Viewer,False,0,\n"
|
||||||
"nonexistentmember_3@igorville.gov,False,help@get.gov,2024-01-15,Invited,Viewer,None,False,0,\n"
|
"nonexistentmember_3@igorville.gov,False,help@get.gov,Unretrieved,Invited,Viewer,None,False,0,\n"
|
||||||
"nonexistentmember_4@igorville.gov,True,help@get.gov,2024-01-15,Invited,Viewer Requester,Manager,False,0,\n"
|
"nonexistentmember_4@igorville.gov,True,help@get.gov,Unretrieved,Invited,Viewer Requester,Manager,False,0,\n"
|
||||||
"nonexistentmember_5@igorville.gov,True,help@get.gov,2024-01-15,Invited,Viewer Requester,None,False,0,\n"
|
"nonexistentmember_5@igorville.gov,True,help@get.gov,Unretrieved,Invited,Viewer Requester,None,False,0,\n"
|
||||||
)
|
)
|
||||||
# Normalize line endings and remove commas,
|
# Normalize line endings and remove commas,
|
||||||
# spaces and leading/trailing whitespace
|
# spaces and leading/trailing whitespace
|
||||||
|
|
|
@ -176,7 +176,7 @@ class MemberExport(BaseExport):
|
||||||
"member_display",
|
"member_display",
|
||||||
"domain_info",
|
"domain_info",
|
||||||
"type",
|
"type",
|
||||||
"invitation_date",
|
"joined_date",
|
||||||
"invited_by",
|
"invited_by",
|
||||||
]
|
]
|
||||||
permissions = UserPortfolioPermissionModelAnnotation.get_annotated_queryset(portfolio, csv_report=True).values(
|
permissions = UserPortfolioPermissionModelAnnotation.get_annotated_queryset(portfolio, csv_report=True).values(
|
||||||
|
@ -197,7 +197,7 @@ class MemberExport(BaseExport):
|
||||||
"Email",
|
"Email",
|
||||||
"Organization admin",
|
"Organization admin",
|
||||||
"Invited by",
|
"Invited by",
|
||||||
"Invitation date",
|
"Joined date",
|
||||||
"Last active",
|
"Last active",
|
||||||
"Domain requests",
|
"Domain requests",
|
||||||
"Member management",
|
"Member management",
|
||||||
|
@ -226,7 +226,7 @@ class MemberExport(BaseExport):
|
||||||
"Email": model.get("email_display"),
|
"Email": model.get("email_display"),
|
||||||
"Organization admin": is_admin,
|
"Organization admin": is_admin,
|
||||||
"Invited by": model.get("invited_by"),
|
"Invited by": model.get("invited_by"),
|
||||||
"Invitation date": model.get("invitation_date"),
|
"Joined date": model.get("joined_date"),
|
||||||
"Last active": model.get("last_active"),
|
"Last active": model.get("last_active"),
|
||||||
"Domain requests": domain_request_display,
|
"Domain requests": domain_request_display,
|
||||||
"Member management": member_perm_display,
|
"Member management": member_perm_display,
|
||||||
|
|
|
@ -324,9 +324,7 @@ class UserPortfolioPermissionModelAnnotation(BaseModelAnnotation):
|
||||||
& Q(user__permissions__domain__domain_info__portfolio=portfolio),
|
& Q(user__permissions__domain__domain_info__portfolio=portfolio),
|
||||||
),
|
),
|
||||||
"type": Value("member", output_field=CharField()),
|
"type": Value("member", output_field=CharField()),
|
||||||
"invitation_date": PortfolioInvitationModelAnnotation.get_invitation_date_query(
|
"joined_date": Func(F("created_at"), Value("YYYY-MM-DD"), function="to_char", output_field=TextField()),
|
||||||
object_id_query=cls.get_portfolio_invitation_id_query()
|
|
||||||
),
|
|
||||||
"invited_by": PortfolioInvitationModelAnnotation.get_invited_by_query(
|
"invited_by": PortfolioInvitationModelAnnotation.get_invited_by_query(
|
||||||
object_id_query=cls.get_portfolio_invitation_id_query()
|
object_id_query=cls.get_portfolio_invitation_id_query()
|
||||||
),
|
),
|
||||||
|
@ -369,33 +367,6 @@ class PortfolioInvitationModelAnnotation(BaseModelAnnotation):
|
||||||
# Get all members on this portfolio
|
# Get all members on this portfolio
|
||||||
return Q(portfolio=portfolio)
|
return Q(portfolio=portfolio)
|
||||||
|
|
||||||
@classmethod
|
|
||||||
def get_invitation_date_query(cls, object_id_query):
|
|
||||||
"""Returns the date at which the given invitation was created.
|
|
||||||
Grabs this data from the audit log, given that a portfolio invitation object
|
|
||||||
is specified via object_id_query."""
|
|
||||||
return Coalesce(
|
|
||||||
Subquery(
|
|
||||||
LogEntry.objects.filter(
|
|
||||||
content_type=ContentType.objects.get_for_model(PortfolioInvitation),
|
|
||||||
object_id=object_id_query,
|
|
||||||
action_flag=ADDITION,
|
|
||||||
)
|
|
||||||
.annotate(
|
|
||||||
# Action time will always be equivalent to created_at in this context.
|
|
||||||
# Using this instead of created_at is a lot simpler and more performant,
|
|
||||||
# as otherwise a Case and Subquery need to be used.
|
|
||||||
display_date=Func(
|
|
||||||
F("action_time"), Value("YYYY-MM-DD"), function="to_char", output_field=TextField()
|
|
||||||
)
|
|
||||||
)
|
|
||||||
.order_by("action_time")
|
|
||||||
.values("display_date")[:1]
|
|
||||||
),
|
|
||||||
Value("Unknown"),
|
|
||||||
output_field=TextField(),
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def get_invited_by_query(cls, object_id_query):
|
def get_invited_by_query(cls, object_id_query):
|
||||||
"""Returns the user that created the given portfolio invitation.
|
"""Returns the user that created the given portfolio invitation.
|
||||||
|
@ -466,12 +437,7 @@ class PortfolioInvitationModelAnnotation(BaseModelAnnotation):
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
"type": Value("invitedmember", output_field=CharField()),
|
"type": Value("invitedmember", output_field=CharField()),
|
||||||
"invitation_date": cls.get_invitation_date_query(
|
"joined_date": Value("Unretrieved", output_field=CharField()),
|
||||||
object_id_query=Cast(OuterRef("id"), output_field=TextField())
|
|
||||||
),
|
|
||||||
# TODO - replace this with a "creator" field on portfolio invitation. This should be another ticket.
|
|
||||||
# Grab the invitation creator from the audit log. This will need to be replaced with a creator field.
|
|
||||||
# When that happens, just replace this with F("invitation__creator")
|
|
||||||
"invited_by": cls.get_invited_by_query(object_id_query=Cast(OuterRef("id"), output_field=TextField())),
|
"invited_by": cls.get_invited_by_query(object_id_query=Cast(OuterRef("id"), output_field=TextField())),
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue