Skip to content

Commit 3e4dcea

Browse files
committed
Move legal hold device reporting to devices.py, write tests, and rescind previous edits to legalhold.py
1 parent ba63222 commit 3e4dcea

File tree

4 files changed

+237
-83
lines changed

4 files changed

+237
-83
lines changed

docs/userguides/legalhold.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -93,8 +93,4 @@ To view all custodians (including inactive) for a legal hold matter, enter
9393

9494
`code42 legal-hold show <matterID> --include-inactive`
9595

96-
To view all devices associated with selected custodians for a legal hold matter, enter
97-
98-
`code42 legal-hold show <matterID> --include-devices`
99-
10096
Learn more about the [Legal Hold](../commands/legalhold.md) commands.

src/code42cli/cmds/devices.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from datetime import date
22

33
import click
4+
import numpy as np
45
from pandas import concat
56
from pandas import DataFrame
67
from pandas import to_datetime
@@ -19,6 +20,8 @@
1920
from code42cli.options import sdk_options
2021
from code42cli.output_formats import DataFrameOutputFormatter
2122
from code42cli.output_formats import OutputFormatter
23+
from code42cli.cmds.legal_hold import _get_all_active_matters
24+
from code42cli.cmds.legal_hold import _get_legal_hold_memberships_for_matter
2225

2326

2427
@click.group(cls=OrderedGroup)
@@ -202,6 +205,14 @@ def _get_device_info(sdk, device_guid):
202205
is_flag=True,
203206
help="Include device settings in output.",
204207
)
208+
@click.option(
209+
"--include-legal-hold-membership",
210+
required=False,
211+
type=bool,
212+
default=False,
213+
is_flag=True,
214+
help="Include legal hold membership in output.",
215+
)
205216
@click.option(
206217
"--exclude-most-recently-connected",
207218
type=int,
@@ -243,6 +254,7 @@ def list_devices(
243254
include_backup_usage,
244255
include_usernames,
245256
include_settings,
257+
include_legal_hold_membership,
246258
exclude_most_recently_connected,
247259
last_connected_after,
248260
last_connected_before,
@@ -288,13 +300,59 @@ def list_devices(
288300
df = _add_settings_to_dataframe(state.sdk, df)
289301
if include_usernames:
290302
df = _add_usernames_to_device_dataframe(state.sdk, df)
303+
if include_legal_hold_membership:
304+
df = _add_legal_hold_membership_to_device_dataframe(state.sdk, df)
291305
if df.empty:
292306
click.echo("No results found.")
293307
else:
294308
formatter = DataFrameOutputFormatter(format)
295309
formatter.echo_formatted_dataframe(df)
296310

297311

312+
def _add_legal_hold_membership_to_device_dataframe(sdk, df):
313+
matters = _get_all_active_matters(sdk)
314+
matters = [matter["legalHoldUid"] for matter in matters]
315+
316+
legal_hold_members = []
317+
318+
for matter in matters:
319+
memberships = _get_legal_hold_memberships_for_matter(sdk, matter_id=matter, active=None)
320+
321+
legal_hold_members.extend(
322+
[
323+
[
324+
str(member["active"]),
325+
member["user"]["userUid"],
326+
member["legalHold"]["legalHoldUid"],
327+
member["legalHold"]["name"]
328+
]
329+
for member in memberships
330+
]
331+
)
332+
333+
legal_hold_member_dataframe = DataFrame.from_records(
334+
legal_hold_members, columns=[
335+
"legalHoldMemberActive",
336+
"userUid",
337+
"legalHoldUid",
338+
"legalHoldName"
339+
]
340+
).groupby(["userUid"]).agg(lambda col: ",".join(col))
341+
df = df.merge(legal_hold_member_dataframe, how="left", on="userUid")
342+
343+
df.loc[
344+
df["status"] == "Deactivated",
345+
[
346+
"legalHoldMemberActive",
347+
"legalHoldUid",
348+
"legalHoldName"
349+
]
350+
] = np.nan
351+
352+
return df
353+
354+
355+
298356
def _get_device_dataframe(
299357
sdk, columns, active=None, org_uid=None, include_backup_usage=False
300358
):

src/code42cli/cmds/legal_hold.py

Lines changed: 10 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55

66
import click
77
from click import echo
8-
from pandas import DataFrame
98

109
from code42cli.bulk import generate_template_cmd_factory
1110
from code42cli.bulk import run_bulk_process
@@ -93,19 +92,8 @@ def _list(state, format=None):
9392
is_flag=True,
9493
help="View details of the preservation policy associated with the legal hold matter.",
9594
)
96-
@click.option(
97-
"--include-devices",
98-
is_flag=True,
99-
help="View devices and storage associated with legal hold custodians.",
100-
)
10195
@sdk_options()
102-
def show(
103-
state,
104-
matter_id,
105-
include_inactive=False,
106-
include_policy=False,
107-
include_devices=False,
108-
):
96+
def show(state, matter_id, include_inactive=False, include_policy=False):
10997
"""Display details of a given legal hold matter."""
11098
matter = _check_matter_is_accessible(state.sdk, matter_id)
11199
matter["creator_username"] = matter["creator"]["username"]
@@ -117,32 +105,19 @@ def show(
117105
memberships = _get_legal_hold_memberships_for_matter(
118106
state.sdk, matter_id, active=active
119107
)
108+
active_usernames = [
109+
member["user"]["username"] for member in memberships if member["active"]
110+
]
111+
inactive_usernames = [
112+
member["user"]["username"] for member in memberships if not member["active"]
113+
]
120114

121115
formatter = OutputFormatter(OutputFormat.TABLE, _MATTER_KEYS_MAP)
122116
formatter.echo_formatted_list([matter])
123-
124-
users = [
125-
[member["active"], member["user"]["userUid"], member["user"]["username"]]
126-
for member in memberships
127-
]
128-
129-
usernames = [user[2] for user in users if user[0] is True]
130-
_print_matter_members(usernames, member_type="active")
117+
_print_matter_members(active_usernames, member_type="active")
118+
131119
if include_inactive:
132-
usernames = [user[2] for user in users if user[0] is not True]
133-
_print_matter_members(usernames, member_type="inactive")
134-
135-
if include_devices:
136-
user_dataframe = _build_user_dataframe(users)
137-
devices_dataframe = _merge_matter_members_with_devices(
138-
state.sdk, user_dataframe
139-
)
140-
if len(devices_dataframe.index):
141-
echo("\nMatter Members and Devices:\n")
142-
click.echo(devices_dataframe.to_csv())
143-
echo(_print_storage_by_org(devices_dataframe))
144-
else:
145-
echo("\nNo devices associated with matter.\n")
120+
_print_matter_members(inactive_usernames, member_type="inactive")
146121

147122
if include_policy:
148123
_get_and_print_preservation_policy(state.sdk, matter["holdPolicyUid"])
@@ -263,50 +238,6 @@ def _print_matter_members(username_list, member_type="active"):
263238
echo("No {} matter members.\n".format(member_type))
264239

265240

266-
def _merge_matter_members_with_devices(sdk, user_dataframe):
267-
devices_generator = sdk.devices.get_all(active=True, include_backup_usage=True)
268-
device_list = _get_total_archive_bytes_per_device(devices_generator)
269-
devices_dataframe = DataFrame.from_records(
270-
device_list,
271-
columns=[
272-
"userUid",
273-
"guid",
274-
"name",
275-
"osHostname",
276-
"status",
277-
"alertStates",
278-
"orgId",
279-
"lastConnected",
280-
"version",
281-
"archiveBytes",
282-
],
283-
)
284-
return user_dataframe.merge(
285-
devices_dataframe, how="inner", on="userUid"
286-
).reset_index(drop=True)
287-
288-
289-
def _build_user_dataframe(users):
290-
user_dataframe = DataFrame.from_records(
291-
users, columns=["activeMembership", "userUid", "username"]
292-
)
293-
return user_dataframe
294-
295-
296-
def _get_total_archive_bytes_per_device(devices_generator):
297-
device_list = [device for page in devices_generator for device in page["computers"]]
298-
for device in device_list:
299-
archive_bytes = [archive["archiveBytes"] for archive in device["backupUsage"]]
300-
device["archiveBytes"] = sum(archive_bytes)
301-
return device_list
302-
303-
304-
def _get_storage_by_org(devices_dataframe):
305-
echo("\nLegal Hold Storage by Org\n")
306-
devices_dataframe = devices_dataframe.filter(["orgId", "archiveBytes"])
307-
return devices_dataframe.groupby("orgId").sum()
308-
309-
310241
@lru_cache(maxsize=None)
311242
def _check_matter_is_accessible(sdk, matter_id):
312243
return sdk.legalhold.get_matter_by_uid(matter_id)

0 commit comments

Comments
 (0)