#!/usr/bin/env python3 # this module requires the following additional Python modules # # P4Python (www.perforce.com) import P4 import re, sys from datetime import date import argparse from pprint import pprint MONTHS=60 # 5 years, overriden by option class GrowthMonthDepot: def __init__(self, months, output): self.months = months self.p4 = P4.P4() self.p4.prog = "Change analyzer" self.p4.connect() self.depots = [ x['name'].lower() for x in self.p4.run_depots() if not x['type'] in ['spec', 'archive', 'unload'] ] self.depots.append('...') # catch all for changes spanning more than one depot self.changes = {} self.sizes = {} self.get_changes_and_sizes() self.out = sys.stdout if output: self.out = open(output, "w") def month_range(self, months): now = date.today() end_year = now.year end_month = now.month + 1 for m in range(months): start_year = end_year start_month = end_month - 1 if start_month == 0: start_month = 12 start_year -= 1 date_range = "@{sy}/{sm}/1,@{ey}/{em}/1".format(sy = start_year, sm = start_month, ey = end_year, em = end_month) year_month = "{year}/{month:02}".format(year=start_year, month=start_month) yield (date_range, year_month) end_year = start_year end_month = start_month def sort_changes_by_depot(self, changes): # initalize result = {} for depot in self.depots: result[depot] = 0 pattern = re.compile("//([^/]+).*") for change in changes: m = pattern.match(change['path']) if m: depot = pattern.match(change['path']).group(1) depot = depot.lower() if not depot in result: depot = '...' else: continue result[depot] = result[depot] + 1 return result def get_sizes(self, date_range, changes_per_depot): result = {} for depot in changes_per_depot: if depot not in ['...'] and changes_per_depot[depot] > 0: path = "//{depot}/...{range}".format(depot=depot, range=date_range) result[depot] = int(self.p4.run_sizes("-sa", path)[0]['fileSize']) else: result[depot] = 0 return result def get_changes_and_sizes(self): for (date_range, year_month) in self.month_range(self.months): ch = self.p4.run_changes("-ssubmitted", date_range) print("{m} : {total}".format(m=year_month, total=len(ch))) self.changes[year_month] = self.sort_changes_by_depot(ch) self.sizes[year_month] = self.get_sizes(date_range, self.changes[year_month]) def print_headers(self): print("month", end=',', file=self.out) for depot in sorted(self.depots): print(depot,end=',', file=self.out) print(file=self.out) def print_changes(self): for ym in sorted(self.changes, reverse=True): print(ym,end=',', file=self.out) changes_for_depots = self.changes[ym] d = sorted(changes_for_depots.keys()) for ch in d: print(changes_for_depots[ch],end=',', file=self.out) print(file=self.out) def print_sizes(self): for ym in sorted(self.sizes, reverse=True): print(ym,end=',', file=self.out) sizes_for_depots = self.sizes[ym] d = sorted(sizes_for_depots.keys()) for ch in d: print(sizes_for_depots[ch],end=',', file=self.out) print(file=self.out) if __name__ == "__main__": parser = argparse.ArgumentParser(description="Change analyzer") parser.add_argument("-m","--months", default=MONTHS, type=int) parser.add_argument("-o","--output", default=None) args = parser.parse_args() months = args.months output = args.output analyzer = GrowthMonthDepot(months, output) analyzer.print_headers() analyzer.print_changes() analyzer.print_headers() analyzer.print_sizes()
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#3 | 8359 | Sven Erik Knop | added missing import | ||
#2 | 8287 | Sven Erik Knop | Updated copyright and no warranty notice. | ||
#1 | 8283 | Sven Erik Knop |
Set of useful scripts and triggers in P4Python. Most require Python 3.X to run, but can be made to use Python 2.7 if necessary. The scripts will also need P4Python 2012.2 + to work. changes_month_depot analysis the usage in changes and bytes of a Perforce Server. directory_sizes list the sizes of files within a directory of Perforce hash_database attempts to find identical files submitted several times (not lazy copies) open_view_clients lists all clients with open views Run the first 3 scripts against a (db only sufficient) replica, they can be very heavy on a server. The last file is a simple client form-in trigger to prevent open views. |