#! /usr/bin/env python # # RTEMS Tools Project (http://www.rtems.org/) # Copyright 2022 Chris Johns (chris@contemporary.software) # All rights reserved. # # This file is part of the RTEMS Tools package in 'rtems-tools'. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: # # 1. Redistributions of source code must retain the above copyright notice, # this list of conditions and the following disclaimer. # # 2. Redistributions in binary form must reproduce the above copyright notice, # this list of conditions and the following disclaimer in the documentation # and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" # AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE # ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # import argparse import sys import trac import tickets import reports def get_notes(notes_file): return [l[:-1] for l in open(notes_file, 'r').readlines()] if notes_file is not None else None def milestone_to_major_minor(release): rtems_major, rtems_minor = release.split('.', 1) try: major = int(rtems_major) rm = '' for c in rtems_minor: if c.isdigit(): rm += c else: break try: minor = int(rm) except: raise RuntimeError('invalid release: ' + milestone) except: raise RuntimeError('invalid release: ' + milestone) return major, minor def milestone_from_major_minor(major, minor): return '%d.%d' % (major, minor) def milestones(release, reverse=False): major, minor = milestone_to_major_minor(release) ms = [milestone_from_major_minor(major, m) for m in range(1, minor + 1)] if reverse: ms.reverse() return ms def collect_tickets(release, cache, force): ''' Collect the tickets for the release and all previous release milestones A release is major.minor[-.*] from minor back to 1. ''' ticks = {} for milestone in milestones(release): print( f"Fetching and processing tickets for release {release} milestone {milestone}." ) tcache = trac.cache(milestone, cache, force) ticks[milestone] = tickets.tickets(release=release, milestone=milestone) ticks[milestone].load(cache=tcache) return ticks def generate(ticks, release, notes_file): rtems_major, rtems_minor = milestone_to_major_minor(release) notes = {} for milestone in milestones(release): notes[milestone] = get_notes(notes_file % (milestone)) gen = reports.generator(release) gen.generator.gen_heading('Table of Content', reports.heading_base) for milestone in milestones(release, reverse=True): print( f"Formatting tickets for release {release} milestone {milestone}." ) t = ticks[milestone] gen.set_milestone(milestone) gen.gen_toc(notes[milestone]) for milestone in milestones(release, reverse=True): t = ticks[milestone] gen.generator.gen_page_break() gen.generator.gen_line_break() gen.set_milestone(milestone) gen.gen_start(notes[milestone]) gen.gen_overall_progress(t.tickets['overall_progress']) gen.gen_tickets_summary(t.tickets['tickets']) gen.gen_tickets_stats_by_category(t.tickets['by_category']) gen.gen_individual_tickets_info(t.tickets['tickets']) return gen.generator.content if __name__ == '__main__': args = argparse.ArgumentParser() args.add_argument('-r', '--release', required=True, dest='release', help='The release to report', type=str, default=None) args.add_argument('-f', '--force', dest='force', help='Force downloading of tickets', action='store_true') args.add_argument('-c', '--cache', dest='cache', help='Cache file base name of ticket data, one per milestone', type=str, default=None) args.add_argument('-o', '--output', required=True, dest='output', help='Output file', type=str, default=None) args.add_argument('-N', '--notes', dest='notes', help='Top-level, manually-written release notes', default=None) opts = args.parse_args() if opts.cache is not None: cache = opts.cache else: cache = '.rng-cache' ticks = collect_tickets(release=opts.release, cache=cache, force=opts.force) contents = generate(ticks, opts.release, opts.notes) print('Writing ' + opts.output) with open(opts.output, 'w') as f: f.write(contents)