Example #1
0
    def __init__(self):
        self.conf = Config()
        usage = \
"""usage: %prog --match-tags t1,t2,... [--exclude-tags t3,t4,...]
                     [-t RFA,O,...] [-f] [-v]
       %prog --batch-queries-file --dest-dir <dirname>
       %prog --list-valid-tags
       %prog --untagged-pkgs-only"""
        parser = OptionParser(usage)
        parser.add_option("-b", "--batch-queries-file", dest="batch_qfile",
                          help="""in batch mode, read queries from this file
                          (one query per line)""", default=None)
        parser.add_option("-d", "--dest-dir", dest="batch_dir",
                          help="""in batch mode, create a result file for
                          every query in this directory""", default=None)
        parser.add_option("-m", "--match-tags", dest="match_tags",
                          help="""match packages having all these tags
                          (comma-separated list; not to be used with -l or -u)""")
        parser.add_option("-x", "--exclude-tags", dest="excl_tags",
                          help="""filter out packages having any of these tags
                          (comma-separated list)""")
        parser.add_option("-t", "--bug-types", dest="bug_types", default="any",
                          help="""query only against bugs of the types in this
                          comma-separated list (valid types: any, O, RFA, RFH,
                          ITP, being_adopted; default: any)""")
        parser.add_option("-l", "--list-valid-tags", action="store_true",
                          dest="list_tags", help="""use this if you don't know
                          what to query for (not to be used with -m or -u)""")
        parser.add_option("-u", "--untagged-pkgs-only", action="store_true",
                          dest="show_untagged",
                          help="""list only bugs for packages that haven't
                          been tagged yet (not to be used with -m or -l)""")
        parser.add_option("-f", "--force-update", action="store_true",
                          dest="force_update", default=False,
                          help="""update bug and popcon data regardless of age
                                  (by default,  bug data is updated when it's
                                  older than 7 days, and popcon data when it's
                                  older than 30 days)""")
        parser.add_option("--cache-dir", dest="cache_dir",
                          default=os.path.expanduser("~/.devscripts_cache"),
                          help="""cache directory for bug and popcon data
                          (default: ~/.devscripts_cache""")
        parser.add_option("--debtags-file", dest="debtags_file",
                          default="/var/lib/debtags/package-tags",
                          help="""use an alternative debtags file
                          (default: /var/lib/debtags/package-tags)""")
        parser.add_option("--vocabulary-file", dest="tags_file",
                          default="/var/lib/debtags/vocabulary",
                          help="""user-supplied tags for filtering are checked
                          against this tag file (default:
                          /var/lib/debtags/vocabulary)""")
        parser.add_option("-v", "--verbose", action="store_true",
                          dest="verbose", default=False)
        parser.add_option("-V", "--version", action="store_true",
                          dest="show_version", default=False,
                          help="show version information")
        (options, args) = parser.parse_args()
        if len(args) > 0:
            parser.error("Unknown argument %s")
        if options.show_version:
            print "wnpp-by-tags %s" % __version__
            exit(0)
        options.match_tags or options.show_untagged or options.list_tags or \
                options.batch_qfile or \
            parser.error("Please specify at least either -m, -b, -u or -l")
        options.match_tags and options.show_untagged and \
            parser.error("Please specify either -m or -u")
        if options.batch_qfile or options.batch_dir:
            options.batch_qfile and options.batch_dir or \
                parser.error("In batch mode, you have to specify both -b and -d")
            if options.batch_qfile == "-":
                queries_fd = sys.stdin
            else:
                assert os.path.exists(options.batch_qfile)
                queries_fd = open(options.batch_qfile)
            self.batch_queries = queries_fd.read().strip().split("\n")
            ensure_dir_exists(options.batch_dir)
            self.batch_dir = options.batch_dir
        else:
            self.batch_queries = None
            self.batch_dir = None

        self.match_tags = set()
        self.excl_tags = set()
        if options.match_tags:
            # parse tags to match and tags to exclude
            self.match_tags = set(options.match_tags.split(","))
            if options.excl_tags:
                self.excl_tags = set(options.excl_tags.split(","))

        if options.bug_types == "any":
            # query against default bug types
            self.full_name_bug_types = self.conf.bug_types_to_query
            self.bug_types = [BugType.abbreviation_of(bt) \
                              for bt in self.full_name_bug_types]
        else:
            # convert any bug type acronyms to uppercase
            self.bug_types = [b.upper() if len(b) <= 3 else b \
                              for b in options.bug_types.split(",")]
            # check that the user-supplied bug types are valid
            self.full_name_bug_types = [BugType.full_name_of(bt) for bt in self.bug_types]
            invalid_bug_types = set(self.full_name_bug_types).difference(self.conf.known_bug_types)
            if invalid_bug_types:
                giveup("invalid  bug type(s): %s" % ", ".join(list(invalid_bug_types)))
            self.bug_types = [BugType.abbreviation_of(bt) for bt in self.bug_types]
        self.bug_types = set(self.bug_types)

        self.force_update = options.force_update
        self.verbose = options.verbose
        self.list_tags = options.list_tags
        self.show_untagged = options.show_untagged
        self.debtags_file = os.path.abspath(options.debtags_file)
        self.cache_dir = os.path.abspath(options.cache_dir)
        self.tags_file = options.tags_file
Example #2
0
def main():
    # parse user-supplied arguments
    args = Arguments()
    # sanity checks
    if not os.path.exists(args.debtags_file):
        giveup("The debtags file %s doesn't exist" % args.debtags_file)
    if not os.path.exists(args.tags_file):
        giveup("The tag vocabulary file %s doesn't exist" % args.tags_file)

    if args.match_tags or args.list_tags or args.batch_queries:
        vocabulary = TagVocabulary(args.tags_file)
        if args.list_tags:
            print vocabulary
            exit(0)
        invalid_tags = vocabulary.invalid_tags(args.match_tags.union(args.excl_tags))
        if invalid_tags:
            giveup("The following tags are not listed in %s:\n%s" % \
                    (args.tags_file, "\n".join(list(invalid_tags))))

    # misc initialisations
    bugs_dir = "%s/wnpp" % args.cache_dir
    popcon_dir = "%s/popcon" % args.cache_dir
    ensure_dir_exists(bugs_dir)
    ensure_dir_exists(popcon_dir)
    update_bug_data(args.force_update, bugs_dir, args.full_name_bug_types,
            args.conf.bugs_update_period_in_days, args.verbose)
    update_popcon_data(args.force_update, popcon_dir,
            args.conf.popcon_update_period_in_days, args.verbose)
    debtags = Debtags(open(args.debtags_file))
    popcon_file = "%s/%s" % (popcon_dir, POPCON_FNAME)
    assert os.path.isfile(popcon_file)
    popcon = Popcon(open(popcon_file, "r"))
    Package.init_sources(debtags.tags_of_pkg, popcon.inst_of_pkg)

    # build dict of package objects, indexed by package name, using the HTML
    # BTS pages
    pkgs_by_name = {}
    for bug_page in glob("%s/*.html" % bugs_dir):
        bug_type = BugType.abbreviation_of(os.path.basename(bug_page)[:-5])
        if bug_type in args.bug_types:
            pkgs_by_name = extract_bugs(open(bug_page, "r"), pkgs_by_name, bug_type)

    if args.show_untagged:
        # select only packages without tags
        pkg_objs = [p for p in pkgs_by_name.itervalues() if not p.tags]
        print format_matches(pkg_objs, args)
        exit(0)

    if args.verbose:
        nbugs = sum([len(p.bugs) for p in pkgs_by_name.itervalues()])
        print "loaded %d bugs in %s packages" % (nbugs, len(pkgs_by_name))

    # filter packages using user-supplied tags
    tag_db = StringIO("\n".join([str(p) for p in pkgs_by_name.itervalues()]))
    if not args.batch_queries:
        pkg_objs = gen_matches(tag_db, pkgs_by_name, args)
        print format_matches(pkg_objs, args)
        exit(0)

    # else we're in batch mode
    for facet in args.batch_queries:
        if args.verbose:
            print "processing \"%s\" tags" % facet
        facet_dir = "%s/%s" % (args.batch_dir, facet)
        ensure_dir_exists(facet_dir)
        for tag in vocabulary.tags_of_facet(facet):
            args.match_tags = set([tag])
            pkg_objs = gen_matches(tag_db, pkgs_by_name, args)
            try:
                tag_value = tag.split("::")[1]
            except ValueError:
                warn("skipping invalid tag \"%s\"" % tag)
                continue
            filename = "%s/%s" % (facet_dir, tag_value)
            matches = format_matches(pkg_objs, args)
            create_file(filename, matches)
            tag_db.seek(0)