Issue tracker augmentation

If you find the issue tracking software you’re using lacks specific process or functionality, it may be possible to augment it through fairly straightforward use of its API.

#!/usr/bin/env python3

    This script scans all issues in GitLab and performs arbitrary
    modifications. It's currently set up to ensure that every issue
    is assigned to at least one user, defaulting to a random choice
    between the project's owner and maintainers.

    This is just a demonstration of how you can augment an issue
    tracker's process with a simple cronjob. Left as an exercise to
    the reader is how one would go about doing this reactively with

import gitlab
import logging
import os
import random


def get_active_project_members(gl, project):
    # The documentation (
    # is unclear whether the state attribute of a membership object
    # represents the state of the membership or the state of the user.
    # To be safe I'm checking both, but we could investigate if it
    # refers to the latter and save a check in the process.
    return [
        for member in project.members.list(as_list=False)
        if member.state == "active"
        and get_cached_user(gl, member.username).state == "active"

def get_default_assignees(gl, project):
    return [
        for member in get_active_project_members(gl, project)
        if member.access_level in ASSIGNEE_ACCESS_LEVELS

def adjust_project_issues(gl, project):
    default_assignees = None
    for issue in project.issues.list(as_list=False):
        if not any((
            user["state"] == "active"
            for user in issue.assignees
            default_assignees = default_assignees or get_default_assignees(gl, project)
            assigned_user = random.choice(default_assignees)
  "Assigning {assigned_user.username} to {issue.web_url}")
            issue.assignee_id = get_cached_user(gl, assigned_user.username).id

def get_cached_user(gl, username):
    if not hasattr(gl, "cached_users"):
        gl.cached_users = {}
    if username not in gl.cached_users:
        gl.cached_users[username] = gl.users.list(username=username)[0]
    return gl.cached_users[username]

if __name__ == "__main__":
    logging.basicConfig(level=os.environ.get("LOGLEVEL") or "INFO")
    logger = logging.getLogger()

    logger.debug(f"Connecting to {os.environ['GITLAB_URL']}")
    with gitlab.Gitlab(
    ) as gl:

        for project in gl.projects.list(as_list=False):
            logger.debug(f"Considering {}")
            adjust_project_issues(gl, project)