2 # Copyright (C) The Arvados Authors. All rights reserved.
4 # SPDX-License-Identifier: AGPL-3.0
14 def parse_arguments(arguments):
15 arg_parser = argparse.ArgumentParser()
16 arg_parser.add_argument('--start', help='Start date for the report in YYYY-MM-DD format')
17 arg_parser.add_argument('--end', help='End date for the report in YYYY-MM-DD format')
18 arg_parser.add_argument('--days', type=int, help='Number of days before now() to start the report')
19 args = arg_parser.parse_args(arguments)
21 if args.days and (args.start or args.end):
23 print("Error: either specify --days or both --start and --end")
26 if not args.days and (not args.start or not args.end):
28 print("Error: either specify --days or both --start and --end")
31 if (args.start and not args.end) or (args.end and not args.start):
33 print("Error: no start or end date found, either specify --days or both --start and --end")
38 def getowner(arv, uuid, owners):
41 if uuid[6:11] == "tpzed":
44 if uuid not in owners:
46 gp = arv.groups().get(uuid=uuid).execute()
47 owners[uuid] = gp["owner_uuid"]
51 return getowner(arv, owners[uuid], owners)
53 def getuserinfo(arv, uuid):
55 u = arv.users().get(uuid=uuid).execute()
57 return "deleted user (%susers/%s)" % (arv.config()["Services"]["Workbench1"]["ExternalURL"],
59 prof = "\n".join(" %s: \"%s\"" % (k, v) for k, v in u["prefs"].get("profile", {}).items() if v)
62 return "%s %s <%s> (%susers/%s)%s" % (u["first_name"], u["last_name"], u["email"],
63 arv.config()["Services"]["Workbench1"]["ExternalURL"],
66 collectionNameCache = {}
67 def getCollectionName(arv, pdh):
68 if pdh not in collectionNameCache:
69 u = arv.collections().list(filters=[["portable_data_hash","=",pdh]]).execute().get("items")
72 collectionNameCache[pdh] = u[0]["name"]
73 return collectionNameCache[pdh]
76 return "\"%s\" (%s)" % (u["name"], u["uuid"])
78 def main(arguments=None):
80 arguments = sys.argv[1:]
82 args = parse_arguments(arguments)
87 to = datetime.datetime.utcnow()
88 since = to - datetime.timedelta(days=args.days)
92 since = datetime.datetime.strptime(args.start,"%Y-%m-%d")
95 print("Error: start date must be in YYYY-MM-DD format")
100 to = datetime.datetime.strptime(args.end,"%Y-%m-%d")
103 print("Error: end date must be in YYYY-MM-DD format")
106 print("User activity on %s between %s and %s\n" % (arv.config()["ClusterID"],
107 since.isoformat(sep=" ", timespec="minutes"),
108 to.isoformat(sep=" ", timespec="minutes")))
110 events = arvados.util.keyset_list_all(arv.logs().list, filters=[["created_at", ">=", since.isoformat()],["created_at", "<", to.isoformat()]])
116 owner = getowner(arv, e["object_owner_uuid"], owners)
117 users.setdefault(owner, [])
118 event_at = ciso8601.parse_datetime(e["event_at"]).astimezone().isoformat(sep=" ", timespec="minutes")
119 # loguuid = e["uuid"]
122 if e["event_type"] == "create" and e["object_uuid"][6:11] == "tpzed":
123 users.setdefault(e["object_uuid"], [])
124 users[e["object_uuid"]].append("%s User account created" % event_at)
126 elif e["event_type"] == "update" and e["object_uuid"][6:11] == "tpzed":
129 elif e["event_type"] == "create" and e["object_uuid"][6:11] == "xvhdp":
130 if e["properties"]["new_attributes"]["requesting_container_uuid"] is None:
131 users[owner].append("%s Ran container %s %s" % (event_at, getname(e["properties"]["new_attributes"]), loguuid))
133 elif e["event_type"] == "update" and e["object_uuid"][6:11] == "xvhdp":
136 elif e["event_type"] == "create" and e["object_uuid"][6:11] == "j7d0g":
137 users[owner].append("%s Created project %s" % (event_at, getname(e["properties"]["new_attributes"])))
139 elif e["event_type"] == "delete" and e["object_uuid"][6:11] == "j7d0g":
140 users[owner].append("%s Deleted project %s" % (event_at, getname(e["properties"]["old_attributes"])))
142 elif e["event_type"] == "update" and e["object_uuid"][6:11] == "j7d0g":
143 users[owner].append("%s Updated project %s" % (event_at, getname(e["properties"]["new_attributes"])))
145 elif e["event_type"] in ("create", "update") and e["object_uuid"][6:11] == "gj3su":
147 if len(users[owner]) > 0 and users[owner][-1].endswith("activity"):
148 sp = users[owner][-1].split(" ")
149 start = sp[0]+" "+sp[1]
150 since_last = ciso8601.parse_datetime(event_at) - ciso8601.parse_datetime(sp[3]+" "+sp[4])
151 span = ciso8601.parse_datetime(event_at) - ciso8601.parse_datetime(start)
153 if since_last is not None and since_last < datetime.timedelta(minutes=61):
154 users[owner][-1] = "%s to %s (%02d:%02d) Account activity" % (start, event_at, span.days*24 + int(span.seconds/3600), int((span.seconds % 3600)/60))
156 users[owner].append("%s to %s (0:00) Account activity" % (event_at, event_at))
158 elif e["event_type"] == "create" and e["object_uuid"][6:11] == "o0j2j":
159 if e["properties"]["new_attributes"]["link_class"] == "tag":
160 users[owner].append("%s Tagged %s" % (event_at, e["properties"]["new_attributes"]["head_uuid"]))
161 elif e["properties"]["new_attributes"]["link_class"] == "permission":
162 users[owner].append("%s Shared %s with %s" % (event_at, e["properties"]["new_attributes"]["tail_uuid"], e["properties"]["new_attributes"]["head_uuid"]))
164 users[owner].append("%s %s %s %s %s" % (event_at, e["event_type"], e["object_kind"], e["object_uuid"], loguuid))
166 elif e["event_type"] == "delete" and e["object_uuid"][6:11] == "o0j2j":
167 if e["properties"]["old_attributes"]["link_class"] == "tag":
168 users[owner].append("%s Untagged %s" % (event_at, e["properties"]["old_attributes"]["head_uuid"]))
169 elif e["properties"]["old_attributes"]["link_class"] == "permission":
170 users[owner].append("%s Unshared %s with %s" % (event_at, e["properties"]["old_attributes"]["tail_uuid"], e["properties"]["old_attributes"]["head_uuid"]))
172 users[owner].append("%s %s %s %s %s" % (event_at, e["event_type"], e["object_kind"], e["object_uuid"], loguuid))
174 elif e["event_type"] == "create" and e["object_uuid"][6:11] == "4zz18":
175 if e["properties"]["new_attributes"]["properties"].get("type") in ("log", "output", "intermediate"):
178 users[owner].append("%s Created collection %s %s" % (event_at, getname(e["properties"]["new_attributes"]), loguuid))
180 elif e["event_type"] == "update" and e["object_uuid"][6:11] == "4zz18":
181 users[owner].append("%s Updated collection %s %s" % (event_at, getname(e["properties"]["new_attributes"]), loguuid))
183 elif e["event_type"] == "delete" and e["object_uuid"][6:11] == "4zz18":
184 if e["properties"]["old_attributes"]["properties"].get("type") in ("log", "output", "intermediate"):
187 users[owner].append("%s Deleted collection %s %s" % (event_at, getname(e["properties"]["old_attributes"]), loguuid))
189 elif e["event_type"] == "file_download":
190 users.setdefault(e["object_uuid"], [])
191 users[e["object_uuid"]].append("%s Downloaded file \"%s\" from \"%s\" (%s) (%s)" % (event_at,
192 e["properties"].get("collection_file_path") or e["properties"].get("reqPath"),
193 getCollectionName(arv, e["properties"].get("portable_data_hash")),
194 e["properties"].get("collection_uuid"),
195 e["properties"].get("portable_data_hash")))
197 elif e["event_type"] == "file_upload":
198 users.setdefault(e["object_uuid"], [])
199 users[e["object_uuid"]].append("%s Uploaded file \"%s\" to \"%s\" (%s)" % (event_at,
200 e["properties"].get("collection_file_path") or e["properties"].get("reqPath"),
201 getCollectionName(arv, e["properties"].get("portable_data_hash")),
202 e["properties"].get("collection_uuid")))
205 users[owner].append("%s %s %s %s %s" % (event_at, e["event_type"], e["object_kind"], e["object_uuid"], loguuid))
207 for k,v in users.items():
208 if k is None or k.endswith("-tpzed-000000000000000"):
210 print(getuserinfo(arv, k))
215 if __name__ == "__main__":