text: #!/usr/bin/env python3
  # Copyright (C) The Arvados Authors. All rights reserved.
  #
  # SPDX-License-Identifier: AGPL-3.0
  
  import argparse
  import sys
  
  import arvados
  import arvados.util
  import datetime
  import ciso8601
  
  def parse_arguments(arguments):
      arg_parser = argparse.ArgumentParser()
      arg_parser.add_argument('--days', type=int, required=True)
      args = arg_parser.parse_args(arguments)
      return args
  
  def getowner(arv, uuid, owners):
      if uuid is None:
          return None
      if uuid[6:11] == "tpzed":
          return uuid
  
      if uuid not in owners:
          try:
              gp = arv.groups().get(uuid=uuid).execute()
              owners[uuid] = gp["owner_uuid"]
          except:
              owners[uuid] = None
  
      return getowner(arv, owners[uuid], owners)
  
  def getuserinfo(arv, uuid):
      u = arv.users().get(uuid=uuid).execute()
      prof = "\n".join("  %s: \"%s\"" % (k, v) for k, v in u["prefs"].get("profile", {}).items() if v)
      if prof:
          prof = "\n"+prof+"\n"
      return "%s %s <%s> (%susers/%s)%s" % (u["first_name"], u["last_name"], u["email"],
                                                         arv.config()["Services"]["Workbench1"]["ExternalURL"],
                                                         uuid, prof)
  
  collectionNameCache = {}
  def getCollectionName(arv, uuid):
      if uuid not in collectionNameCache:
          u = arv.collections().get(uuid=uuid).execute()
          collectionNameCache[uuid] = u["name"]
      return collectionNameCache[uuid]
  
  def getname(u):
      return "\"%s\" (%s)" % (u["name"], u["uuid"])
  
  def main(arguments=None):
      if arguments is None:
          arguments = sys.argv[1:]
  
      args = parse_arguments(arguments)
  
      arv = arvados.api()
  
      since = datetime.datetime.utcnow() - datetime.timedelta(days=args.days)
  
      print("User activity on %s between %s and %s\n" % (arv.config()["ClusterID"],
                                                         (datetime.datetime.now() - datetime.timedelta(days=args.days)).isoformat(sep=" ", timespec="minutes"),
                                                         datetime.datetime.now().isoformat(sep=" ", timespec="minutes")))
  
      events = arvados.util.keyset_list_all(arv.logs().list, filters=[["created_at", ">=", since.isoformat()]])
  
      users = {}
      owners = {}
  
      for e in events:
          owner = getowner(arv, e["object_owner_uuid"], owners)
          users.setdefault(owner, [])
          event_at = ciso8601.parse_datetime(e["event_at"]).astimezone().isoformat(sep=" ", timespec="minutes")
          # loguuid = e["uuid"]
          loguuid = ""
  
          if e["event_type"] == "create" and e["object_uuid"][6:11] == "tpzed":
              users.setdefault(e["object_uuid"], [])
              users[e["object_uuid"]].append("%s User account created" % event_at)
  
          elif e["event_type"] == "update" and e["object_uuid"][6:11] == "tpzed":
              pass
  
          elif e["event_type"] == "create" and e["object_uuid"][6:11] == "xvhdp":
              if e["properties"]["new_attributes"]["requesting_container_uuid"] is None:
                  users[owner].append("%s Ran container %s %s" % (event_at, getname(e["properties"]["new_attributes"]), loguuid))
  
          elif e["event_type"] == "update" and e["object_uuid"][6:11] == "xvhdp":
              pass
  
          elif e["event_type"] == "create" and e["object_uuid"][6:11] == "j7d0g":
              users[owner].append("%s Created project %s" %  (event_at, getname(e["properties"]["new_attributes"])))
  
          elif e["event_type"] == "delete" and e["object_uuid"][6:11] == "j7d0g":
              users[owner].append("%s Deleted project %s" % (event_at, getname(e["properties"]["old_attributes"])))
  
          elif e["event_type"] == "update" and e["object_uuid"][6:11] == "j7d0g":
              users[owner].append("%s Updated project %s" % (event_at, getname(e["properties"]["new_attributes"])))
  
          elif e["event_type"] in ("create", "update") and e["object_uuid"][6:11] == "gj3su":
              since_last = None
              if len(users[owner]) > 0 and users[owner][-1].endswith("activity"):
                  sp = users[owner][-1].split(" ")
                  start = sp[0]+" "+sp[1]
                  since_last = ciso8601.parse_datetime(event_at) - ciso8601.parse_datetime(sp[3]+" "+sp[4])
                  span = ciso8601.parse_datetime(event_at) - ciso8601.parse_datetime(start)
  
              if since_last is not None and since_last < datetime.timedelta(minutes=61):
                  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))
              else:
                  users[owner].append("%s to %s (0:00) Account activity" % (event_at, event_at))
  
          elif e["event_type"] == "create" and e["object_uuid"][6:11] == "o0j2j":
              if e["properties"]["new_attributes"]["link_class"] == "tag":
                  users[owner].append("%s Tagged %s" % (event_at, e["properties"]["new_attributes"]["head_uuid"]))
              elif e["properties"]["new_attributes"]["link_class"] == "permission":
                  users[owner].append("%s Shared %s with %s" % (event_at, e["properties"]["new_attributes"]["tail_uuid"], e["properties"]["new_attributes"]["head_uuid"]))
              else:
                  users[owner].append("%s %s %s %s" % (e["event_type"], e["object_kind"], e["object_uuid"], loguuid))
  
          elif e["event_type"] == "delete" and e["object_uuid"][6:11] == "o0j2j":
              if e["properties"]["old_attributes"]["link_class"] == "tag":
                  users[owner].append("%s Untagged %s" % (event_at, e["properties"]["old_attributes"]["head_uuid"]))
              elif e["properties"]["old_attributes"]["link_class"] == "permission":
                  users[owner].append("%s Unshared %s with %s" % (event_at, e["properties"]["old_attributes"]["tail_uuid"], e["properties"]["old_attributes"]["head_uuid"]))
              else:
                  users[owner].append("%s %s %s %s" % (e["event_type"], e["object_kind"], e["object_uuid"], loguuid))
  
          elif e["event_type"] == "create" and e["object_uuid"][6:11] == "4zz18":
              if e["properties"]["new_attributes"]["properties"].get("type") in ("log", "output", "intermediate"):
                  pass
              else:
                  users[owner].append("%s Created collection %s %s" % (event_at, getname(e["properties"]["new_attributes"]), loguuid))
  
          elif e["event_type"] == "update" and e["object_uuid"][6:11] == "4zz18":
              users[owner].append("%s Updated collection %s %s" % (event_at, getname(e["properties"]["new_attributes"]), loguuid))
  
          elif e["event_type"] == "delete" and e["object_uuid"][6:11] == "4zz18":
              if e["properties"]["old_attributes"]["properties"].get("type") in ("log", "output", "intermediate"):
                  pass
              else:
                  users[owner].append("%s Deleted collection %s %s" % (event_at, getname(e["properties"]["old_attributes"]), loguuid))
  
          elif e["event_type"] == "file_download":
                  users[e["object_uuid"]].append("%s Downloaded file \"%s\" from \"%s\" (%s) (%s)" % (event_at,
                                                                                         e["properties"].get("collection_file_path") or e["properties"].get("reqPath"),
                                                                                         getCollectionName(arv, e["properties"].get("collection_uuid")),
                                                                                         e["properties"].get("collection_uuid"),
                                                                                         e["properties"].get("portable_data_hash")))
  
          elif e["event_type"] == "file_upload":
                  users[e["object_uuid"]].append("%s Uploaded file \"%s\" to \"%s\" (%s)" % (event_at,
                                                                                      e["properties"].get("collection_file_path") or e["properties"].get("reqPath"),
                                                                                      getCollectionName(arv, e["properties"].get("collection_uuid")),
                                                                                      e["properties"].get("collection_uuid")))
  
          else:
              users[owner].append("%s %s %s %s" % (e["event_type"], e["object_kind"], e["object_uuid"], loguuid))
  
      for k,v in users.items():
          if k is None or k.endswith("-tpzed-000000000000000"):
              continue
          print(getuserinfo(arv, k))
          for ev in v:
              print("  %s" % ev)
          print("")
  
  if __name__ == "__main__":
      main()
  