Merge lp:~rodsmith/hwcert-tools/get-ytd into lp:~hardware-certification/hwcert-tools/reporting-tools

Proposed by Rod Smith
Status: Superseded
Proposed branch: lp:~rodsmith/hwcert-tools/get-ytd
Merge into: lp:~hardware-certification/hwcert-tools/reporting-tools
Diff against target: 141 lines (+137/-0)
1 file modified
certification_reports/get_ytd_makes.py (+137/-0)
To merge this branch: bzr merge lp:~rodsmith/hwcert-tools/get-ytd
Reviewer Review Type Date Requested Status
Zygmunt Krynicki (community) Needs Fixing
Jeff Lane  Pending
Review via email: mp+253389@code.launchpad.net

This proposal has been superseded by a proposal from 2015-03-18.

Description of the change

New script to generate YTD reports on server certificates issued.

To post a comment you must log in.
Revision history for this message
Zygmunt Krynicki (zyga) wrote :

Hey.

Have a look at my comments below

review: Needs Fixing
Revision history for this message
Zygmunt Krynicki (zyga) wrote :
Download full text (7.4 KiB)

Also, flake8 the script. I'm sure I've missed something. Also feel
free to pip install flake8-docstrings and re-flake the script.

On Wed, Mar 18, 2015 at 5:09 PM, Zygmunt Krynicki
<email address hidden> wrote:
> Review: Needs Fixing
>
> Hey.
>
> Have a look at my comments below
>
> Diff comments:
>
>> === added file 'certification_reports/get_ytd_makes.py'
>> --- certification_reports/get_ytd_makes.py 1970-01-01 00:00:00 +0000
>> +++ certification_reports/get_ytd_makes.py 2015-03-18 15:57:45 +0000
>> @@ -0,0 +1,131 @@
>> +#!/usr/bin/python2
>
> for all python2 code please do at a bare minimum:
>
> from __future__ import print_function, absolute_import
>
>> +'''
>> +Script to summarize make and model certification for the year to date
>
> Please move this to the description= keyword argument of the argparse.ArgumentParser() below
>
>> +or for a specified year. Produces a list of makes, the number of
>> +certificates issued for each make, and a list of the models certified
>> +for that make.
>> +
>> +Options:
>> +- C3 username
>> +- C3 API key (from https://certification.canonical.com/me/)
>> +- Ubuntu release (e.g., "precise" or "trusty")
>> +- Year (e.g., "2015")
>> +- --certnum={value} -- Certificate number for a report on one machine or
>> + "all" for all certified systems. Defaults to "all". Used for debugging,
>> + since retrieving all certificates takes ~10 minutes.
>> +
>> +Copyright (c) 2015 Canonical Ltd.
>
> please move the copyright to a comment at the top of the file
>
>> +
>> +Author: Rod Smith <email address hidden>
>> +'''
>> +
>> +from api_utils import APIQuery, QueryError
>
> Please put imports in this order: stdlib, 3rd party, this app/library. Put a newline between each group
>
>> +from argparse import ArgumentParser
>> +
>> +import sys
>> +import time
>> +
>> +C3_URL = "https://certification.canonical.com"
>> +CERTIFICATE_API = C3_URL + "/api/v1/certificates"
>> +
>> +SERVER_FF = ['Expansion Chassis',
>> + 'Main Server Chassis',
>> + 'Multi-system',
>> + 'Blade',
>> + 'Rack Mount Chassis',
>> + 'Server']
>> +
>> +api = APIQuery(C3_URL)
>> +
>> +def get_server_certs(username, api_key, release, year, certnum):
>> + '''
>> + Retrieve certificate information from C3
>
> Please follow PEP for docstrings
>
> Retrieve certificate information from C3.
>
> :param username:
> Bla bla bla
> :returns:
> Dictionary with ...
>
>> + Return value: Dictionary with manufacturer names as keys and
>> + list of models as values
>> + '''
>> + request_url = CERTIFICATE_API
>> + summaries = {}
>> +
>> + if certnum == "all":
>> + request_params = {"username": username,
>> + "api_key": api_key}
>> + else:
>> + request_params = {"username": username,
>> + "api_key": api_key,
>> + "name": certnum}
>> +
>> + certs = []
>> + try:
>> + certs = api.batch_query(request_url, params=request_params)
>> + except QueryError:
>> + print "Unable to get certificates"
>
> you can raise SystemExit("Unable to gett...."), this ...

Read more...

Revision history for this message
Rod Smith (rodsmith) :
Revision history for this message
Zygmunt Krynicki (zyga) wrote :

On Wed, Mar 18, 2015 at 5:20 PM, Roderick Smith <email address hidden> wrote:
>
>
> Diff comments:
>
>> === added file 'certification_reports/get_ytd_makes.py'
>> --- certification_reports/get_ytd_makes.py 1970-01-01 00:00:00 +0000
>> +++ certification_reports/get_ytd_makes.py 2015-03-18 15:57:45 +0000
>> @@ -0,0 +1,131 @@
>> +#!/usr/bin/python2
>> +'''
>> +Script to summarize make and model certification for the year to date
>
> It's unclear to me how much of this you want moved. Also: MOVED or COPIED?

Moved, just keep a one-line summary at the top. The idea is to see
useful information when running get_ytd_makes.py --help.

Thanks
ZK

Revision history for this message
Jeff Lane  (bladernr) wrote :

The latest changes to the output look good to me. I'll ack once Zyga is satisfied.

Revision history for this message
Zygmunt Krynicki (zyga) wrote :

I'll do my best to help to resolve code style issues

On Wed, Mar 18, 2015 at 6:41 PM, Jeff Lane <email address hidden> wrote:
> The latest changes to the output look good to me. I'll ack once Zyga is satisfied.
> --
> https://code.launchpad.net/~rodsmith/hwcert-tools/get-ytd/+merge/253389
> You are reviewing the proposed merge of lp:~rodsmith/hwcert-tools/get-ytd into lp:~hardware-certification/hwcert-tools/reporting-tools.

lp:~rodsmith/hwcert-tools/get-ytd updated
170. By Rod Smith

Modifications to get_ytd_makes.py in response to Zygmunt's comments

Revision history for this message
Rod Smith (rodsmith) wrote :

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

On 03/18/2015 12:09 PM, Zygmunt Krynicki wrote:
> Review: Needs Fixing
>
> Hey.
>
> Have a look at my comments below

I've changed most of these things. A few further comments and queries,
though....

>> +from api_utils import APIQuery, QueryError
>
> Please put imports in this order: stdlib, 3rd party, this
> app/library. Put a newline between each group

I think I've got this, but I'm really guessing at what's what. Is this
documented somewhere?

>> + num_of_make += 1 + grand_total += 1 +
>> print make.encode('utf-8') + ": " + str(num_of_make)
>
> This is a code smell, don't do this.
>
> Either move to all unicode strings internally or use all bytes and
> hope for the best. Why make needs this treatment now? I assume you
> get an unicode string from the API, correct?

The problem isn't with internal string handling; it's with what
happens when running the script with redirection when
".encode('utf-8')" is dropped:

$ ./get_ytd_makes.py {blah blah blah} > foo.txt
Traceback (most recent call last):
  File "./get_ytd_makes.py", line 137, in <module>
    sys.exit(main())
  File "./get_ytd_makes.py", line 119, in main
    print (make + ": " + str(num_of_make))
UnicodeEncodeError: 'ascii' codec can't encode characters in position
17-24: ordinal not in range(128)

It works fine when sending output to the console (or at least, an X
terminal), but when redirecting, it runs into this crash when a name
includes non-ASCII characters. (The trouble point for us is NEC, which
is encoded as "NEC Corporation (日本電気株式会社)"). I've Googled this
to death and using ".encode('utf-8')" is the only solution I've found
that works. (I've tried several ways of explicitly marking strings as
Unicode, and they all fail in one way or another.) If you have another
solution, I'm quite willing to try it.

- --
Rod Smith
Server and Cloud Certification Engineer
<email address hidden>
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1

iQEcBAEBAgAGBQJVCdlhAAoJEFgyRI+V0FjmunMH/A+3z/c5e6L7WB3QVHaezDZx
jkvUeZjI3BPwKHI2jpdOiAxYQoGqK2RNMGL+3DZDVTiDYbbqOwG/13sZ6bWUXg2p
JT1Hvl8yUllOLjyys4HoiPskMjLYHS/4TUmOeJnWFMZ4yNY69Gzb08Y+emzavPDe
KZVw2Uxi36bZXBiO99U1SwmU0YcTx68oOt2hPlywOYsHtgQP1TKoGWb/iwrOUnVI
qE7W3Ho3zkc17LiQtNfPWUYvVlwFzd096LnhloSa6ujS3Yv7NUls6Va/X+vS/mcs
60dWgE0Agnc+6hCviOKNyEFcuKP/aJb/L5cd7uGjF7mUSBxEZeuyF7AVySMgROw=
=LSKD
-----END PGP SIGNATURE-----

lp:~rodsmith/hwcert-tools/get-ytd updated
171. By Rod Smith

More tweaks to get_ytd_makes.py in response to Zygmunt's comments

172. By Rod Smith

More changes in response to Zygmunt's comments

173. By Rod Smith

Changes to get_ytd_makes.py's Unicode handling in response to Zygmunt's comments

Unmerged revisions

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added file 'certification_reports/get_ytd_makes.py'
2--- certification_reports/get_ytd_makes.py 1970-01-01 00:00:00 +0000
3+++ certification_reports/get_ytd_makes.py 2015-03-18 19:49:16 +0000
4@@ -0,0 +1,137 @@
5+#!/usr/bin/python2
6+"""
7+Script to summarize make and model certification for the year to date
8+or for a specified year.
9+
10+Copyright (c) 2015 Canonical Ltd.
11+
12+Author: Rod Smith <rod.smith@canonical.com>
13+"""
14+
15+from __future__ import print_function, absolute_import
16+
17+import sys
18+import time
19+
20+from api_utils import APIQuery, QueryError
21+from argparse import ArgumentParser
22+
23+C3_URL = "https://certification.canonical.com"
24+CERTIFICATE_API = C3_URL + "/api/v1/certificates"
25+
26+SERVER_FF = ['Expansion Chassis',
27+ 'Main Server Chassis',
28+ 'Multi-system',
29+ 'Blade',
30+ 'Rack Mount Chassis',
31+ 'Server']
32+
33+api = APIQuery(C3_URL)
34+
35+
36+def get_server_certs(username, api_key, release, year, certnum):
37+ """ Retrieve certificate information from C3
38+ :param username:
39+ C3 username
40+ :param api_key:
41+ C3 API key, obtainable from https://certification.canonical.com/me/
42+ :param release:
43+ Ubuntu release codename (e.g., "precise", "trusty")
44+ :param year:
45+ Year for which the report is desired
46+ :param certnum:
47+ Certificate number (e.g., "1502-7107" or "all" for all certificates)
48+ :returns:
49+ Dictionary with manufacturer names as keys and list of models as values
50+ """
51+ request_url = CERTIFICATE_API
52+ summaries = {}
53+
54+ if certnum == "all":
55+ request_params = {"username": username,
56+ "api_key": api_key}
57+ else:
58+ request_params = {"username": username,
59+ "api_key": api_key,
60+ "name": certnum}
61+
62+ certs = []
63+ try:
64+ certs = api.batch_query(request_url, params=request_params)
65+ except QueryError:
66+ raise SystemExit("Unable to get certificates")
67+ certs = [cert for cert in certs if cert['name']]
68+
69+ for cert in certs:
70+ if cert['created_at'][0:4] == year:
71+ machine = api.single_query(C3_URL + cert['machine'],
72+ params=request_params)
73+ if (machine['platform']['form_factor'] in SERVER_FF and
74+ cert['release']['codename'] == release):
75+ make = machine['account']['name']
76+ details = {'model': machine['platform']['name'],
77+ 'certnum': cert['name'],
78+ 'cid': machine['canonical_id'].encode('utf-8')}
79+ try:
80+ summaries[make].append(details)
81+ except LookupError:
82+ summaries[make] = [details]
83+
84+ return summaries
85+
86+
87+def main():
88+ parser = ArgumentParser(
89+ description="Script to summarize make and model certificates " +
90+ "issued for the year to date or for a specified year. Produces " +
91+ "a list of makes, the number of certificates issued for each" +
92+ "make, and a list of the models certified for that make.")
93+ parser.add_argument(
94+ "username", help="Launchpad username used to access C3.")
95+ parser.add_argument(
96+ "api_key", help="API key used to access C3, from " +
97+ "https://certification.canonical.com/me/")
98+ parser.add_argument(
99+ "release", help="Ubuntu release ('trusty', 'precise', etc.)")
100+ parser.add_argument("year", help="The year to summarize")
101+ parser.add_argument(
102+ "--certnum", help="Certificate number or 'all' (used for debugging)",
103+ default="all")
104+ args = parser.parse_args()
105+
106+ summaries = get_server_certs(args.username, args.api_key, args.release,
107+ args.year, args.certnum)
108+
109+ print ("YTD server certificates for {0} in {1}".format(args.release,
110+ args.year))
111+ print ("\nReport generated: {0}\n".format(time.strftime("%c")))
112+
113+ # First pass: Generate "executive summary"
114+ print ("Summary:")
115+ print ("--------\n")
116+ grand_total = 0
117+ sorted_make = sorted(summaries)
118+ for make in sorted_make:
119+ num_of_make = 0
120+ for model in sorted(summaries[make]):
121+ num_of_make += 1
122+ grand_total += 1
123+ print (make.encode('utf-8') + ": " + str(num_of_make))
124+ print ("\nGrand total: ", grand_total)
125+
126+ # Second pass: Generate detailed report
127+ print ("\n\nDetailed information:")
128+ print ("---------------------\n")
129+ for make in sorted_make:
130+ print (make.encode('utf-8') + ":")
131+ num_of_make = 0
132+ for model in sorted(summaries[make]):
133+ num_of_make += 1
134+ print (" * {0} (CID {1}):".format(model['model'], model['cid']))
135+ print (" https://certification.canonical.com/certificates/{0}".
136+ format(model['certnum']))
137+ print (make.encode('utf-8'), " TOTAL: ", num_of_make, "\n")
138+
139+
140+if __name__ == "__main__":
141+ sys.exit(main())

Subscribers

People subscribed via source and target branches