# frozen_string_literal: true require_relative "category" module Gitlab module Dangerfiles class Teammate attr_reader :options, :username, :name, :role, :projects, :available, :hungry, :reduced_capacity, :tz_offset_hours # The options data are produced by https://gitlab.com/gitlab-org/gitlab-roulette/-/blob/master/lib/team_member.rb def initialize(options = {}) @options = options @username = options["username"] @name = options["name"] @markdown_name = options["markdown_name"] || default_markdown_name(*options.values_at("username", "name")) @role = options["role"] @projects = process_projects(options["projects"]) @available = options["available"] @hungry = options["hungry"] @reduced_capacity = options["reduced_capacity"] @tz_offset_hours = options["tz_offset_hours"] end def to_h options end def inspect "#<#{self.class} @username=#{username.inspect}>" end def ==(other) return false unless other.respond_to?(:username) other.username == username end def in_project?(name) projects&.has_key?(name) end def any_capability?(project, category) capabilities(project).any? { |capability| capability.end_with?(category.to_s) } end def reviewer?(project, category, labels) has_capability?(project, category, :reviewer, labels) end def traintainer?(project, category, labels) has_capability?(project, category, :trainee_maintainer, labels) end def maintainer?(project, category, labels) has_capability?(project, category, :maintainer, labels) end def integrations_be?(project, category, labels) return false unless category == :integrations_be has_capability?(project, category, :reviewer, labels) end def integrations_fe?(project, category, labels) return false unless category == :integrations_fe has_capability?(project, category, :reviewer, labels) end def markdown_name(author: nil) "#{@markdown_name}#{utc_offset_text(author)}" end def local_hour (Time.now.utc + tz_offset_hours * 3600).hour end def capabilities(project) projects.fetch(project, []) end protected def floored_offset_hours floored_offset = tz_offset_hours.floor(0) floored_offset == tz_offset_hours ? floored_offset : tz_offset_hours end private def default_markdown_name(username, name) "[#{name}](https://gitlab.com/#{username}) (`@#{username}`)" end def process_projects(projects) return nil unless projects projects.each_with_object({}) do |(project, capabilities), all| all[project.downcase] = Array(capabilities).map(&:downcase) end end def utc_offset_text(author = nil) return unless tz_offset_hours offset_text = if floored_offset_hours >= 0 "UTC+#{floored_offset_hours}" else "UTC#{floored_offset_hours}" end if author " (#{offset_text}, #{offset_diff_compared_to_author(author)})" else " (#{offset_text})" end end def offset_diff_compared_to_author(author) diff = floored_offset_hours - author.floored_offset_hours return "same timezone as `@#{author.username}`" if diff == 0 ahead_or_behind = diff < 0 ? "behind" : "ahead of" pluralized_hours = pluralize(diff.abs, "hour", "hours") "#{pluralized_hours} #{ahead_or_behind} `@#{author.username}`" end def has_capability?(project, category, kind, labels) Category.for(category, project: project, kind: kind, labels: labels).has_capability?(self) end def pluralize(count, singular, plural) word = count == 1 || count.to_s =~ /^1(\.0+)?$/ ? singular : plural "#{count || 0} #{word}" end end end end