From 37da1d16bae7f23ffd8cfea811f974715391ab6f Mon Sep 17 00:00:00 2001 From: Mats Rynge Date: Wed, 14 Jan 2026 10:04:15 -0800 Subject: [PATCH] Removed osgconnect-report - users have been migrated to Comanage --- opensciencegrid/osgconnect-report/Dockerfile | 18 - .../osgconnect-report/build-config.json | 7 - .../osgconnect-report/.gitignore | 2 - .../osgconnect-report/README.md | 39 -- .../osgconnect-report/__init__.py | 0 .../osgconnect-report/client.py | 103 ---- .../osgconnect-report/create-venv | 9 - .../osgconnect-report/generate_user_report.py | 539 ------------------ .../osgconnect-report/requirements.txt | 17 - .../osgconnect-report/runme.sh | 10 - .../osgconnect-report/test/__init__.py | 0 .../test/test_generate_user_report.py | 333 ----------- .../osgconnect-report/training_groups.json | 9 - 13 files changed, 1086 deletions(-) delete mode 100644 opensciencegrid/osgconnect-report/Dockerfile delete mode 100644 opensciencegrid/osgconnect-report/build-config.json delete mode 100644 opensciencegrid/osgconnect-report/osgconnect-report/.gitignore delete mode 100644 opensciencegrid/osgconnect-report/osgconnect-report/README.md delete mode 100644 opensciencegrid/osgconnect-report/osgconnect-report/__init__.py delete mode 100644 opensciencegrid/osgconnect-report/osgconnect-report/client.py delete mode 100755 opensciencegrid/osgconnect-report/osgconnect-report/create-venv delete mode 100755 opensciencegrid/osgconnect-report/osgconnect-report/generate_user_report.py delete mode 100644 opensciencegrid/osgconnect-report/osgconnect-report/requirements.txt delete mode 100755 opensciencegrid/osgconnect-report/osgconnect-report/runme.sh delete mode 100644 opensciencegrid/osgconnect-report/osgconnect-report/test/__init__.py delete mode 100644 opensciencegrid/osgconnect-report/osgconnect-report/test/test_generate_user_report.py delete mode 100644 opensciencegrid/osgconnect-report/osgconnect-report/training_groups.json diff --git a/opensciencegrid/osgconnect-report/Dockerfile b/opensciencegrid/osgconnect-report/Dockerfile deleted file mode 100644 index a203e003..00000000 --- a/opensciencegrid/osgconnect-report/Dockerfile +++ /dev/null @@ -1,18 +0,0 @@ -ARG BASE_YUM_REPO=release -ARG BASE_OSG_SERIES=23 - -FROM opensciencegrid/software-base:$BASE_OSG_SERIES-el9-$BASE_YUM_REPO - -LABEL maintainer OSG Software - -RUN yum -y install \ - vim && \ - yum -y clean all - -ADD osgconnect-report /opt/osgconnect-report - -RUN cd /opt/osgconnect-report && ./create-venv - -ENTRYPOINT ["/opt/osgconnect-report/runme.sh"] - - diff --git a/opensciencegrid/osgconnect-report/build-config.json b/opensciencegrid/osgconnect-report/build-config.json deleted file mode 100644 index 781ae34c..00000000 --- a/opensciencegrid/osgconnect-report/build-config.json +++ /dev/null @@ -1,7 +0,0 @@ -{ - "standard_build": true, - "repo_build": false, - "base_os": ["el9"], - "osg_series": ["23", "24"], - "base_repo": ["release"] - } diff --git a/opensciencegrid/osgconnect-report/osgconnect-report/.gitignore b/opensciencegrid/osgconnect-report/osgconnect-report/.gitignore deleted file mode 100644 index 735466f5..00000000 --- a/opensciencegrid/osgconnect-report/osgconnect-report/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -snapshots -*.token diff --git a/opensciencegrid/osgconnect-report/osgconnect-report/README.md b/opensciencegrid/osgconnect-report/osgconnect-report/README.md deleted file mode 100644 index bc4adf93..00000000 --- a/opensciencegrid/osgconnect-report/osgconnect-report/README.md +++ /dev/null @@ -1,39 +0,0 @@ -# User Account Reporting Tool - -This user account reporting tool has been developed to automate the process of -reporting the number of weekly new account requests and new accounts accepted -through OSG Connect. - -To accomplish this, the script `generate_user_report.py` pulls user information -from the OSG Connect User Database, saves a snapshot of the database (only -information needed to generate the report is saved), and compares it to a -previously saved snapshot in order to count the metrics previously mentioned. - -`generate_user_report.py` will send an HTML email report to the given recipients -upon completion. - -## Prerequisites - -1. a file in the current working directory called `osgconnect.token` with a - token that has access to use the OSG User Account Database REST endpoint -1. a file in the current working directory called `mailgun.token` with a - token that has access to use the ci-connect mailgun - -## Usage - -``` -usage: generate_user_report.py [-h] [--recipients RECIPIENTS [RECIPIENTS ...]] [--start START] [--end END] [--debug] - -Collects user account metrics, generates an html report, and sends it to the given recipients. - -optional arguments: - -h, --help show this help message and exit - --recipients RECIPIENTS [RECIPIENTS ...] - recipients to which the report will be sent - --start START snapshot to start from, used to 'replay' from a specific snapshot - --end END snapshot to end with, used to 'replay' from a specific snapshot - --debug enable debug mode -``` - -Example: `python3 generate_user_report.py --recipients email@domain` - diff --git a/opensciencegrid/osgconnect-report/osgconnect-report/__init__.py b/opensciencegrid/osgconnect-report/osgconnect-report/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/opensciencegrid/osgconnect-report/osgconnect-report/client.py b/opensciencegrid/osgconnect-report/osgconnect-report/client.py deleted file mode 100644 index 8740db69..00000000 --- a/opensciencegrid/osgconnect-report/osgconnect-report/client.py +++ /dev/null @@ -1,103 +0,0 @@ -#!/usr/bin/env python3 -import os -import requests -import logging -import sys -import json -from pathlib import Path -from typing import List, Dict - -# https://github.com/maniaclab/ci-connect-api/blob/master/resources/api_specification/api.raml -class UserApiClient: - """Simple client class for making GET requests to OSG Connect Database""" - - def __init__(self, token_file_path: Path): - """Constructor - - :param token_file_path: path to file containing API token - :type token_file_path: str - """ - self.log = logging.getLogger("Client") - self.url_prefix = "https://api.ci-connect.net:18080/v1alpha1" - try: - with token_file_path.open("r") as f: - self._token = f.read().strip() - except FileNotFoundError: - self.log.exception("Unable to find token file.") - sys.exit(1) - - def _get(self, route: str) -> Dict: - """Internal use GET method - - :param route: route to GET - :type route: str - :return: dict representation of response - :rtype: Dict - :raises requests.exceptions.HTTPError: encountered 4XX client error or 5XX server error response - """ - resp = requests.get("{}{}?token={}".format( - self.url_prefix, route, self._token - )) - - # ensure status code 200 - resp.raise_for_status() - - return resp.json() - - ### User Level Requests #################################################### - def get_users(self) -> List[Dict]: - """Get all users - - :return: each users with detailed information (not including group memberships) - :rtype: List[Dict] - """ - resp = self._get("/users") - return resp["items"] - - def get_user(self, uid: str) -> Dict: - """Get detailed information about a specific user - - :param uid: user's id such as "jimhalpert" - :type uid: str - :return: detailed user information - :rtype: Dict - """ - resp = self._get("/users/{uid}".format(uid=uid)) - return resp - - ### Group Level Requests ################################################### - def get_group_list(self) -> List[str]: - """Get a list of all group names - - :return: all group names - :rtype: List[str] - """ - resp = self._get("/groups") - return [group["name"] for group in resp["groups"]] - - def get_group(self, group_name: str) -> Dict: - """Get a specific group - - :param group_name: name of the group such as root.osg.NameOfGroup - :type group_name: str - :return: group information not including members - :rtype: Dict - """ - resp = self._get("/groups/{g_name}".format(g_name=group_name)) - return resp - - def get_group_members(self, group_name: str) -> List[Dict]: - """Get all members in the specified group - - :param group_name: name of the group such as root.osg.NameOfGroup - :type group_name: str - :return: each member in the given group - :rtype: List[Dict] - """ - resp = self._get("/groups/{g_name}/members".format(g_name=group_name)) - return [member for member in resp["memberships"]] - - - - - diff --git a/opensciencegrid/osgconnect-report/osgconnect-report/create-venv b/opensciencegrid/osgconnect-report/osgconnect-report/create-venv deleted file mode 100755 index b4e6bc00..00000000 --- a/opensciencegrid/osgconnect-report/osgconnect-report/create-venv +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/bash - -set -e - -python3 -m venv venv -. venv/bin/activate -python3 -m pip install --upgrade pip -python3 -m pip install --requirement requirements.txt - diff --git a/opensciencegrid/osgconnect-report/osgconnect-report/generate_user_report.py b/opensciencegrid/osgconnect-report/osgconnect-report/generate_user_report.py deleted file mode 100755 index 5cca13fa..00000000 --- a/opensciencegrid/osgconnect-report/osgconnect-report/generate_user_report.py +++ /dev/null @@ -1,539 +0,0 @@ -#!/usr/bin/env python3 -import json -import logging -import sys -import smtplib -import argparse -import requests - -from collections import defaultdict -from datetime import datetime -from datetime import timezone -from enum import Enum -from pathlib import Path -from typing import List, Set, Union, Tuple - -from client import UserApiClient - -from tqdm import tqdm - -DATE_FMT = "%Y-%b-%d %H:%M:%S.%f %Z" - -log = logging.getLogger("reporter") - -class GroupMemberState(Enum): - """Possible group membership states""" - NONMEMBER = "nonmember" - PENDING = "pending" - ACTIVE = "active" - ADMIN = "admin" - DISABLED = "disabled" - -def get_snapshot(save=True) -> dict: - """ - Dump a snapshot of the data base into ./snapshots/YYYYMMDD_snapshot.json - - The snapshot contains only information needed to the following for a given - time period: - - new account requests - - new account requests that have already been added to a training project - - new account requests that have already been added to a non-training project - - new accounts accepted - - new accounts accepted and have been added to a training project - - new accounts accepted and have been added to a non-training project - - The snapshot is formatted as follows: - { - "date": "YYYY-Mon-DD HH:MM:SS.MS UTC", - "users": { - "": { - "osg_state": "", - "joing_date": "YYYY-Mon-DD HH:MM:SS.MS UTC", - "groups": { - "": "", - ... - } - }, ... - } - } - - :param save: whether or not to save snapshot to disk, defaults to True - :type save: bool - :return: snapshot just written - :rtype: dict - """ - TOP_DIR = Path(__file__).parent.resolve() - client = UserApiClient(TOP_DIR / "osgconnect.token") - - snapshot = defaultdict(dict) - - # get the state of all users in the root.osg group - osg_states = client.get_group_members("root.osg") - for s in osg_states: - snapshot[s["user_name"]]["osg_state"] = s["state"] - - # get the join date of all users (it is expected that all users belong to root.osg) - users = client.get_users() - for u in users: - if u["kind"].lower() == "user" and u["metadata"]["unix_name"] in snapshot: - u = u["metadata"] - snapshot[u["unix_name"]]["join_date"] = u["join_date"] - - # get the membership information from each group (len(groups) number of api requests...) - groups = client.get_group_list() - for group_name in tqdm(groups): - # only concerned with groups that are part of "root.osg" - if "root.osg" in group_name: - memberships = client.get_group_members(group_name) - for m in memberships: - if "groups" not in snapshot[m["user_name"]]: - snapshot[m["user_name"]]["groups"] = dict() - - snapshot[m["user_name"]]["groups"][group_name] = m["state"] - - log.info("collected {num} users in the root.osg group".format(num=len(snapshot))) - - # add date snapshot was recorded - snapshot = { - "date": datetime.now(timezone.utc).strftime(DATE_FMT), - "users": snapshot - } - - # setup directory for snapshot files - snapshot_dir = TOP_DIR / "snapshots" - snapshot_dir.mkdir(exist_ok=True) - - snapshot_file = "{date}_snapshot.json".format(date=datetime.now().strftime("%Y%m%d")) - snapshot_file = snapshot_dir / snapshot_file - - with snapshot_file.open("w") as f: - json.dump(snapshot, f, indent=1) - - log.info("snapshot written to {file}".format(file=snapshot_file)) - - return snapshot - -def get_latest_snapshot_on_disk() -> Union[Path, None]: - """ - Returns the a path object to the latest snapshot file in ./snapshots. - - :return: path to the latest snapshot file or None if none is found - :rtype: Union[Path, None] - """ - SNAPSHOT_DIR = Path(__file__).parent.resolve() / "snapshots" - - latest_snapshot_file = None - latest_snapshot_date = datetime.min - - if SNAPSHOT_DIR.is_dir(): - for f in SNAPSHOT_DIR.iterdir(): - if f.name.endswith("_snapshot.json"): - with f.open("r") as curr_snapshot_file: - curr_snapshot_date = json.load(curr_snapshot_file)["date"] - curr_snapshot_date = datetime.strptime(curr_snapshot_date, DATE_FMT) - - if curr_snapshot_date > latest_snapshot_date: - latest_snapshot_date = curr_snapshot_date - latest_snapshot_file = f - - return latest_snapshot_file - -def get_snapshot_on_disk(snapshot: str) -> dict: - """Returns the given snapshot as a dict. - - :param snapshot: name of the snapshot - :type snapshot: str - :return: dict representation of the snapshot - :rtype: dict - :raises FileNotFoundError: given snapshot could not be found - """ - SNAPSHOT_DIR = Path(__file__).parent.resolve() / "snapshots" - - target_snapshot = SNAPSHOT_DIR / snapshot - - with target_snapshot.open("r") as f: - return json.load(f) - - -def send_report(recipients: List[str], msg_content: str) -> None: - - # TODO: parametrize email recipients.. (or load from file to keep from versioning) - # TODO: needs exception/error handling - - # get email credentials - EMAIL_CREDENTIALS = Path(__file__).parent.resolve() / "mailgun.token" - with EMAIL_CREDENTIALS.open("r") as f: - token = f.read().strip() - - response = requests.post( - "https://api.mailgun.net/v3/api.ci-connect.net/messages", - auth=("api", token), - data={"from": "OSGConnect Report ", - "to": recipients, - "subject": "OSG Connect User Account Reporting", - "html": msg_content}) - log.info(response.text) - - -def get_training_groups() -> Set[str]: - """ - Returns the list of training groups in ./training_groups.json as a set. - - :return: training groups - :rtype: Set[str] - """ - try: - training_groups_file = Path(__file__).parent.resolve() / "training_groups.json" - with training_groups_file.open("r") as f: - training_groups = set(json.load(f)) - - log.info("found training groups: {tg} in {p}".format( - tg=training_groups, - p=training_groups_file - )) - - return training_groups - - except FileNotFoundError: - log.error("Unable to find {p}, no training groups set".format( - p=training_groups_file - )) - raise - except json.JSONDecodeError: - log.error("Unable to decode {p}, possible formatting error".format(p=training_groups_file)) - raise - - - -### New Account Request Reporting ############################################## -def get_new_account_requests(prev_snapshot: dict, curr_snapshot: dict) -> List[str]: - """ - Gets all new accounts requests that came in during - prev_snapshot["date"] < user_join_date <= curr_snapshot["date"]. - - :param prev_snapshot: snapshot previously recorded - :type prev_snapshot: dict - :param curr_snapshot: snapshot just recorded - :type curr_snapshot: dict - :return: list of users who had requested accounts since the last snapshot was taken - :rtype: list - """ - start_date = datetime.strptime(prev_snapshot["date"], DATE_FMT) - end_date = datetime.strptime(curr_snapshot["date"], DATE_FMT) - - accounts = list() - for u_name, u_info in curr_snapshot["users"].items(): - # join_date is not present for some users (for example onces that are - # part of groups other than root.osg) - if "join_date" in u_info: - join_date = datetime.strptime(u_info["join_date"], DATE_FMT) - - if start_date < join_date and join_date <= end_date: - accounts.append(u_name) - - log.info("found {n} new account requests from {start} to {end}: {acts}".format( - n=len(accounts), - start=start_date, - end=end_date, - acts=accounts - )) - - return accounts - -### New Accounts Accepted Reporting ############################################ -def get_new_accounts_accepted_and_rejected(prev_snapshot: dict, curr_snapshot: dict) -> Tuple[List[str], List[str]]: - """ - Gets all accounts that have been accepted and rejected since the last snapshot. - An "accepted account" is defined as having its state moved from "pending" to "active". - A "rejected account" is defined as having been a member of "root.osg" in the - previous snapshot, but not the current snapshot. - - :param prev_snapshot: snapshot previously recorded - :type prev_snapshot: dict - :param curr_snapshot: snapshot just recorded - :type curr_snapshot: dict - :return: lists of users whos accounts have been accepted and rejected - :rtype: Tuple[List[str], List[str]] - """ - start_date = datetime.strptime(prev_snapshot["date"], DATE_FMT) - end_date = datetime.strptime(curr_snapshot["date"], DATE_FMT) - - # accounts[0] is ACCEPTED accounts - # accounts[1] is REJECTED accounts - accounts = (list(), list()) - - # look at "root.osg" state changes from previous snapshot to current snapshot - log.debug("looking at root.osg state changes from previous to current snapshot") - for u_name, u_info in prev_snapshot["users"].items(): - log.debug("working on {}".format(u_name)) - # TODO: figure out what it means to be in group root.osg - # not all memebers are part of "root.osg", skip those that are not - try: - # only care about members in "root.osg" in previous snapshot - if "root.osg" in u_info["groups"]: - - # account is accepted iff root.osg state moved from pending -> active from prev to curr snapshot - if u_info["groups"]["root.osg"] == GroupMemberState.PENDING.value \ - and curr_snapshot["users"][u_name]["groups"]["root.osg"] == GroupMemberState.ACTIVE.value: - - # account was accepted! - accounts[0].append(u_name) - - # key error accessing curr_snapshot means account from prev not in curr, thus account was rejected - except KeyError as e: - # user from prev snapshot not in - log.warning("problem key: {}, exception: {}; adding as rejected account".format(u_name, e)) - - # account was rejected! - accounts[1].append(u_name) - - # look for accounts have been just requested and accepted between the previous snapshot and the - # current snapshot (their entries will only exist in the current snapshot and their join - # date will be after the date of the previous snapshot) - log.debug("looking at accounts that have been just requested and accepted between snapshots") - for name, info in curr_snapshot["users"].items(): - log.debug("working on {}".format(name)) - log.debug(info) - if "root.osg" in info["groups"] \ - and info["groups"]["root.osg"] == GroupMemberState.ACTIVE.value \ - and datetime.strptime(info["join_date"], DATE_FMT) > start_date: - - # account was just accepted! - accounts[0].append(name) - - log.info( - "found {n} new accounts that have been accepted from {start} to {end}: {acts}".format( - n=len(accounts[0]), - start=start_date, - end=end_date, - acts=accounts[0] - ) - ) - - log.info( - "found {n} new accounts that have been REJECTED from {start} to {end}: {acts}".format( - n=len(accounts[1]), - start=start_date, - end=end_date, - acts=accounts[1] - ) - ) - - return accounts - -def get_new_accounts_accepted_in_training_group( - new_acts_accepted: List[str], - curr_snapshot: dict, - training_projects: Set[str] - ) -> List[str]: - """ - Gets all accounts that have been accepted and added to a training project. - "Added to a training group" is defined as showing up as a member in a training - group with state=. - - :param new_acts_accepted: new accounts accepted since the last snapshot was taken - :type new_acts_accepted: List[str] - :param curr_snapshot: snapshot just recorded - :type curr_snapshot: dict - :param training_projects: predefined set of training projects to search for - :type training_projects: Set[str] - :return: list of users whos accounts have been accepted and added to a training project - :rtype: List[str] - """ - - accounts = list() - - for user in new_acts_accepted: - user_groups = curr_snapshot["users"][user]["groups"] - - for group_name, state in user_groups.items(): - if group_name in training_projects and\ - state in {GroupMemberState.ACTIVE.value, GroupMemberState.PENDING.value}: - - accounts.append(user) - break - - log.info( - "found {n} accounts that have been accepted and added to a training project: {acts}".format( - n=len(accounts), - acts=accounts - ) - ) - - return accounts - -def get_new_accounts_accepted_in_non_training_group( - new_acts_accepted: List[str], - curr_snapshot: dict, - training_projects: Set[str], - exclude: Set[str] = {"root", "root.osg", "root.osg.login-nodes", "root.osg.login-nodes.login05", "root.osg.login-nodes.login04"} - ) -> List[str]: - """ - Gets all accounts that have been accepted since the last snapshot and have - been already added to a non training group. "Added to a non training group" is - defined as showing up as a member in a group that is neither in any of the - groups in "exclude" and "training_projects" and having a group state=. - - :param new_acts_accepted: new accounts accepted since the last snapshot was taken - :type new_acts_accepted: List[str] - :param curr_snapshot: snapshot just recorded - :type curr_snapshot: dict - :param training_projects: predefiend set of training projects to exclude - :type training_projects: Set[str] - :param exclude: non-training projects to exclude, defaults to {"root", "root.osg", "root.osg.login-nodes", "root.osg.login-nodes.login05", "root.osg.login-nodes.login04"} - :type exclude: Set[str], optional - :return: list of users whos accounts have been accepted and added to a non training project - :rtype: List[str] - """ - exclude.update(training_projects) - accounts = list() - - for user in new_acts_accepted: - user_groups = curr_snapshot["users"][user]["groups"] - - for group_name, state in user_groups.items(): - if group_name not in exclude and\ - state in {GroupMemberState.ACTIVE.value, GroupMemberState.PENDING.value}: - - accounts.append(user) - break - - log.info( - "found {n} new accounts accepted that have already been added to a non training project (excluding {excluded}): {acts}".format( - n=len(accounts), - excluded=exclude, - acts=accounts - ) - ) - - return accounts - -def parse_args(args=sys.argv[1:]): - """Argument parsing""" - parser = argparse.ArgumentParser( - description=""" - Collects user account metrics, generates an html report, and - sends it to the given recipients. - """ - ) - parser.add_argument( - "--recipients", - nargs="+", - help="recipients to which the report will be sent" - ) - - parser.add_argument( - "--start", - type=str, - help="snapshot to start from, used to 'replay' from a specific snapshot" - ) - - parser.add_argument( - "--end", - type=str, - help="snapshot to end with, used to 'replay' from a specific snapshot" - ) - - parser.add_argument( - "--debug", - action="store_true", - default=False, - help="enable debug mode" - ) - - return parser.parse_args(args) - -if __name__=="__main__": - args = parse_args() - - log_level = logging.INFO - if args.debug: - log_level = logging.DEBUG - - logging.basicConfig(level=log_level) - - # TODO: cleanup smtp code; add error checking; logging - - if args.start: - previous_snapshot = get_snapshot_on_disk(args.start) - log.info("using start snapshot: {}".format(args.start)) - else: - previous_snapshot_file = get_latest_snapshot_on_disk() - - if not previous_snapshot_file: - log.info("No previous snapshot found, exiting") - sys.exit(1) - - with previous_snapshot_file.open("r") as f: - previous_snapshot = json.load(f) - - if args.end: - current_snapshot = get_snapshot_on_disk(args.end) - log.info("using end snapshot: {}".format(args.end)) - else: - get_snapshot(save=True) - with get_latest_snapshot_on_disk().open("r") as f: - current_snapshot = json.load(f) - - previous_snapshot_date = datetime.strptime(previous_snapshot["date"], DATE_FMT) - current_snapshot_date = datetime.strptime(current_snapshot["date"], DATE_FMT) - report_duration_in_days = (current_snapshot_date - previous_snapshot_date).days - - training_groups = get_training_groups() - - # new account requests - new_account_requests = get_new_account_requests( - prev_snapshot=previous_snapshot, - curr_snapshot=current_snapshot - ) - - # accounts accepted - new_accounts_accepted_and_rejected = get_new_accounts_accepted_and_rejected( - prev_snapshot=previous_snapshot, - curr_snapshot=current_snapshot - ) - - new_accounts_accepted = new_accounts_accepted_and_rejected[0] - new_accounts_rejected = new_accounts_accepted_and_rejected[1] - - new_accounts_accepted_in_training_group = get_new_accounts_accepted_in_training_group( - new_acts_accepted=new_accounts_accepted, - curr_snapshot=current_snapshot, - training_projects=training_groups - ) - - new_accounts_accepted_in_non_training_group = get_new_accounts_accepted_in_non_training_group( - new_acts_accepted=new_accounts_accepted, - curr_snapshot=current_snapshot, - training_projects=training_groups - ) - - if args.recipients: - report = """ -

Account Reporting: {start} to {end} ({dur} days)

-
    -
  • Accounts Requested: {num_nar} ({nar})
  • -
  • Accounts Accepted: {num_naa} ({naa})
  • -
      -
    • AND in Training Group: {num_naa_tr} ({naa_tr})
    • -
    • AND in Non Training Group: {num_naa_ntr} ({naa_ntr})
    • -
    -
  • Accounts Rejected: {num_rej} ({narej})
  • -
- """.format( - start=previous_snapshot["date"], - end=current_snapshot["date"], - dur=report_duration_in_days, - num_nar=len(new_account_requests), - nar=new_account_requests, - num_naa=len(new_accounts_accepted), - naa=new_accounts_accepted, - num_naa_tr=len(new_accounts_accepted_in_training_group), - naa_tr=new_accounts_accepted_in_training_group, - num_naa_ntr=len(new_accounts_accepted_in_non_training_group), - naa_ntr=new_accounts_accepted_in_non_training_group, - num_rej=len(new_accounts_rejected), - narej=new_accounts_rejected - ) - - send_report(recipients=args.recipients, msg_content=report) diff --git a/opensciencegrid/osgconnect-report/osgconnect-report/requirements.txt b/opensciencegrid/osgconnect-report/osgconnect-report/requirements.txt deleted file mode 100644 index 65b0b565..00000000 --- a/opensciencegrid/osgconnect-report/osgconnect-report/requirements.txt +++ /dev/null @@ -1,17 +0,0 @@ -attrs==20.3.0 -certifi==2024.7.4 -chardet==4.0.0 -idna==2.10 -importlib-metadata==3.10.1 -iniconfig==1.1.1 -packaging==20.9 -pluggy==0.13.1 -py==1.10.0 -pyparsing==2.4.7 -pytest==6.2.3 -requests==2.32.4 -toml==0.10.2 -tqdm==4.60.0 -typing-extensions==3.7.4.3 -urllib3==2.5.0 -zipp==3.19.1 diff --git a/opensciencegrid/osgconnect-report/osgconnect-report/runme.sh b/opensciencegrid/osgconnect-report/osgconnect-report/runme.sh deleted file mode 100755 index 27280f4a..00000000 --- a/opensciencegrid/osgconnect-report/osgconnect-report/runme.sh +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/bash - -set -e - -cd /opt/osgconnect-report - -. venv/bin/activate - -python3 ./generate_user_report.py "$@" - diff --git a/opensciencegrid/osgconnect-report/osgconnect-report/test/__init__.py b/opensciencegrid/osgconnect-report/osgconnect-report/test/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/opensciencegrid/osgconnect-report/osgconnect-report/test/test_generate_user_report.py b/opensciencegrid/osgconnect-report/osgconnect-report/test/test_generate_user_report.py deleted file mode 100644 index 43f373d9..00000000 --- a/opensciencegrid/osgconnect-report/osgconnect-report/test/test_generate_user_report.py +++ /dev/null @@ -1,333 +0,0 @@ -import pytest - -from generate_user_report import get_new_account_requests -from generate_user_report import get_new_accounts_accepted_and_rejected -from generate_user_report import get_new_accounts_accepted_in_training_group -from generate_user_report import get_new_accounts_accepted_in_non_training_group - -class TestGetNewAccountRequests: - @pytest.mark.parametrize( - "prev_snapshot, curr_snapshot", - [ - ( - { - "date": "2021-Jan-01 00:00:01.000000 UTC", - "users": { - "jim_halpert": { - "osg_state": "pending", - "join_date": "2021-Jan-01 00:00:00.000000 UTC", - "groups": { - "root.osg": "pending", - "root.osg.training2021": "pending", - "root.osg.non_training": "pending" - } - }, - "pam_beesly": { - "osg_state": "active", - "join_date": "2021-Jan-01 04:46:25.868712 UTC", - "groups": { - "root.osg": "active", - "root.osg.non_training": "active" - } - } - } - }, - { - "date": "2021-Jan-07 00:00:00.000000 UTC", - "users": { - "jim_halpert": { - "osg_state": "active", - "join_date": "2021-Jan-01 00:00:00.000000 UTC", - "groups": { - "root.osg": "active", - "root.osg.training2021": "active", - "root.osg.non_training": "active" - } - }, - "pam_beesly": { - "osg_state": "active", - "join_date": "2021-Jan-01 04:46:25.868712 UTC", - "groups": { - "root.osg": "active", - "root.osg.non_training": "active", - "root.osg.training2021": "pending" - } - } - } - } - ) - ] - ) - def test_get_new_account_requests(self, prev_snapshot, curr_snapshot): - result = get_new_account_requests(prev_snapshot, curr_snapshot) - assert result == ["pam_beesly"] - - @pytest.mark.parametrize( - "prev_snapshot, curr_snapshot, expected", - [ - ( - { - "date": "2021-Jan-01 00:00:01.000000 UTC", - "users": { - "jim_halpert": { - "osg_state": "pending", - "join_date": "2021-Jan-01 00:00:00.000000 UTC", - "groups": { - "root.osg": "pending", - "root.osg.training2021": "pending", - "root.osg.non_training": "pending" - } - }, - "pam_beesly": { - "osg_state": "active", - "join_date": "2020-Jan-01 04:46:25.868712 UTC", - "groups": { - "root.osg": "active", - "root.osg.non_training": "active" - } - } - } - }, - { - "date": "2021-Jan-07 00:00:00.000000 UTC", - "users": { - "jim_halpert": { - "osg_state": "active", - "join_date": "2021-Jan-01 00:00:00.000000 UTC", - "groups": { - "root.osg": "active", - "root.osg.training2021": "active", - "root.osg.non_training": "active" - } - }, - "pam_beesly": { - "osg_state": "active", - "join_date": "2020-Jan-01 04:46:25.868712 UTC", - "groups": { - "root.osg": "active", - "root.osg.non_training": "active", - "root.osg.training2021": "pending" - } - } - } - }, - (["jim_halpert"],[]) - ), - ( - { - "date": "2021-Jan-01 00:00:01.000000 UTC", - "users": dict() - }, - { - "date": "2021-Jan-07 00:00:00.000000 UTC", - "users": { - "jim_halpert": { - "osg_state": "active", - "join_date": "2021-Jan-02 00:00:00.000000 UTC", - "groups": { - "root.osg": "active", - "root.osg.training2021": "active", - "root.osg.non_training": "active" - } - } - } - }, - (["jim_halpert"],[]) - ), - ( - { - "date": "2021-Jan-01 00:00:01.000000 UTC", - "users": { - "jim_halpert": { - "osg_state": "pending", - "join_date": "2021-Jan-01 00:00:00.000000 UTC", - "groups": { - "root.osg": "pending", - "root.osg.training2021": "pending", - "root.osg.non_training": "pending" - } - }, - } - }, - { - "date": "2021-Jan-07 00:00:00.000000 UTC", - "users": dict() - }, - ([],["jim_halpert"]) - ), - ( - { - "date": "2021-Jan-01 00:00:01.000000 UTC", - "users": { - "jim_halpert": { - "osg_state": "pending", - "join_date": "2021-Jan-01 00:00:00.000000 UTC", - "groups": { - "root.osg": "pending", - "root.osg.training2021": "pending", - "root.osg.non_training": "pending" - } - }, - } - }, - { - "date": "2021-Jan-07 00:00:00.000000 UTC", - "users": dict() - }, - ([],["jim_halpert"]) - ), - ( - { - "date": "2021-Jan-01 00:00:01.000000 UTC", - "users": { - "jim_halpert": { - "osg_state": "pending", - "join_date": "2021-Jan-01 00:00:00.000000 UTC", - "groups": { - "root.osg": "pending", - "root.osg.training2021": "pending", - "root.osg.non_training": "pending" - } - }, - } - }, - { - "date": "2021-Jan-07 00:00:00.000000 UTC", - "users": { - "jim_halpert": { - "osg_state": "pending", - "join_date": "2021-Jan-01 00:00:00.000000 UTC", - "groups": dict() - }, - } - }, - ([],["jim_halpert"]) - ), - ] - ) - def test_get_new_accounts_accepted_and_rejected(self, prev_snapshot, curr_snapshot, expected): - result = get_new_accounts_accepted_and_rejected(prev_snapshot, curr_snapshot) - assert result == expected - @pytest.mark.parametrize( - "curr_snapshot, expected_result", - [ - ( - { - "date": "2021-Jan-07 00:00:00.000000 UTC", - "users": { - "jim_halpert": { - "osg_state": "active", - "join_date": "2021-Jan-01 00:00:00.000000 UTC", - "groups": { - "root.osg": "active", - "root.osg.training2021": "active", - "root.osg.non_training": "active" - } - } - } - }, - ["jim_halpert"] - ), - ( - { - "date": "2021-Jan-07 00:00:00.000000 UTC", - "users": { - "jim_halpert": { - "osg_state": "active", - "join_date": "2021-Jan-01 00:00:00.000000 UTC", - "groups": { - "root.osg": "active", - "root.osg.non_training": "active" - } - } - } - }, - [] - ) - ] - ) - def test_get_new_accounts_accepted_in_training_group(self, curr_snapshot, expected_result): - result = get_new_accounts_accepted_in_training_group( - new_acts_accepted=["jim_halpert"], - curr_snapshot=curr_snapshot, - training_projects={"root.osg.training2021"} - ) - - assert result == expected_result - - @pytest.mark.parametrize( - "curr_snapshot, exclude, training_groups, expected_result", - [ - ( - { - "date": "2021-Jan-07 00:00:00.000000 UTC", - "users": { - "jim_halpert": { - "osg_state": "active", - "join_date": "2021-Jan-01 00:00:00.000000 UTC", - "groups": { - "root.osg": "active", - "root.osg.training2021": "active", - "root.osg.non_training": "active" - } - } - } - }, - {"root", "root.osg"}, - {"root.osg.training2021"}, - ["jim_halpert"] - ), - ( - { - "date": "2021-Jan-07 00:00:00.000000 UTC", - "users": { - "jim_halpert": { - "osg_state": "active", - "join_date": "2021-Jan-01 00:00:00.000000 UTC", - "groups": { - "root.osg": "active", - "root.osg.training2021": "active", - } - } - } - }, - {"root", "root.osg"}, - {"root.osg.training2021"}, - [] - ), - ( - { - "date": "2021-Jan-07 00:00:00.000000 UTC", - "users": { - "jim_halpert": { - "osg_state": "active", - "join_date": "2021-Jan-01 00:00:00.000000 UTC", - "groups": { - "root.osg": "active", - "root.osg.training2021": "active", - "root.osg.non_training": "pending" - } - } - } - }, - {"root", "root.osg"}, - {"root.osg.training2021"}, - ["jim_halpert"] - ) - ] - ) - def test_get_new_accepted_in_non_training_group( - self, - curr_snapshot, - exclude, - training_groups, - expected_result - ): - result = get_new_accounts_accepted_in_non_training_group( - new_acts_accepted=["jim_halpert"], - curr_snapshot=curr_snapshot, - training_projects={"root.osg.training2021"}, - exclude=exclude - ) - - assert result == expected_result diff --git a/opensciencegrid/osgconnect-report/osgconnect-report/training_groups.json b/opensciencegrid/osgconnect-report/osgconnect-report/training_groups.json deleted file mode 100644 index 034b4f28..00000000 --- a/opensciencegrid/osgconnect-report/osgconnect-report/training_groups.json +++ /dev/null @@ -1,9 +0,0 @@ -[ - "root.osg.UserTraining-AHM-2020", - "root.osg.Tutorial-PEARC20", - "root.osg.CampusWorkshop_Feb2021", - "root.osg.Workshop-RMACC21", - "root.osg.OSGUserTrainingPilot", - "root.osg.Tutorial-PEARC21", - "root.osg.Training-ACE-NIAID" -]