├── .gitignore ├── README.markdown ├── dump.markdown ├── macmodelshelf.db └── macmodelshelf.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | MacModelShelf 2 | ============= 3 | 4 | What? 5 | ----- 6 | 7 | A small Python module that gives you nice human readable Macintosh model 8 | names, e.g. "iMac (27-inch, Late 2009)", when given a serial number or model 9 | code. It uses shelve to keep a persistent dictionary of model codes, and looks 10 | up unknown model codes from Apple's servers. 11 | 12 | How? 13 | ---- 14 | 15 | In your code: 16 | 17 | import macmodelshelf 18 | macmodelshelf.model_code("W12345825RU") # Returns the model code "5RU" 19 | macmodelshelf.model("5RU") # Returns "iMac (27-inch, Late 2009)" 20 | 21 | On the commandline: 22 | 23 | % ./macmodelshelf.py 5RU 24 | iMac (27-inch, Late 2009) 25 | 26 | Dump? 27 | ----- 28 | 29 | % ./macmodelshelf.py dump-json 30 | macmodelshelfdump = { 31 | "000": "Power Mac G5", 32 | "00W": "Xserve (Late 2006)", 33 | "01P": "MacBook (13-inch, Late 2007)", 34 | ... 35 | } 36 | % ./macmodelshelf.py dump-markdown 37 | Code | Model 38 | :--- | :--- 39 | `000` | Power Mac G5 40 | `00W` | Xserve (Late 2006) 41 | `01P` | MacBook (13-inch, Late 2007) 42 | ... 43 | 44 | For a dump of all the models in the current cache, see [`dump.markdown`](dump.markdown). 45 | 46 | 47 | License 48 | ------- 49 | 50 | Copyright 2012-2018 Per Olofsson, University of Gothenburg. All rights reserved. 51 | 52 | Licensed under the Apache License, Version 2.0 (the "License"); 53 | you may not use this file except in compliance with the License. 54 | You may obtain a copy of the License at 55 | 56 | http://www.apache.org/licenses/LICENSE-2.0 57 | 58 | Unless required by applicable law or agreed to in writing, software 59 | distributed under the License is distributed on an "AS IS" BASIS, 60 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 61 | See the License for the specific language governing permissions and 62 | limitations under the License. 63 | -------------------------------------------------------------------------------- /macmodelshelf.db: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/MagerValp/MacModelShelf/78ee6abc4afe2148a35c441d2d0b6477aaae9574/macmodelshelf.db -------------------------------------------------------------------------------- /macmodelshelf.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | 4 | import sys 5 | import os 6 | import argparse 7 | import shelve 8 | import urllib2 9 | from xml.etree import ElementTree 10 | import re 11 | 12 | 13 | DBPATH = "macmodelshelf" 14 | 15 | 16 | def print8(*args): 17 | print " ".join(unicode(x).encode(u"utf-8") for x in args) 18 | 19 | def printerr8(*args): 20 | print >>sys.stderr, " ".join(unicode(x).encode(u"utf-8") for x in args) 21 | 22 | 23 | try: 24 | macmodelshelf = shelve.open(DBPATH) 25 | except BaseException, e: 26 | printerr8(u"Couldn't open macmodelshelf.db: %s" % unicode(e)) 27 | sys.exit(1) 28 | 29 | 30 | def model_code(serial): 31 | if "serial" in serial.lower(): # Workaround for machines with dummy serial numbers. 32 | return None 33 | if len(serial) in (12, 13) and serial.startswith("S"): # Remove S prefix from scanned codes. 34 | serial = serial[1:] 35 | if len(serial) in (11, 12): 36 | return serial[8:].decode("ascii") 37 | return None 38 | 39 | 40 | def lookup_mac_model_code_from_apple(model_code): 41 | try: 42 | earl = "http://support-sp.apple.com/sp/product?cc=%s&lang=en_US" % model_code 43 | call = urllib2.Request(earl, headers={'Accept' : '*/*'}) 44 | f = urllib2.urlopen(call) 45 | et = ElementTree.parse(f) 46 | return et.findtext("configCode").decode("utf-8") 47 | except: 48 | return None 49 | 50 | 51 | CLEANUP_RES = [ 52 | (re.compile(ur"inch ? "), u"inch, "), 53 | (re.compile(ur" "), u" "), 54 | ] 55 | def cleanup_model(model): 56 | for pattern, replacement in CLEANUP_RES: 57 | model = pattern.sub(replacement, model) 58 | return model 59 | 60 | 61 | def model(code, cleanup=True): 62 | global macmodelshelf 63 | if code == None: 64 | return None 65 | code = code.upper() 66 | try: 67 | model = macmodelshelf[code] 68 | except KeyError: 69 | printerr8(u"Looking up %s from Apple" % code) 70 | model = lookup_mac_model_code_from_apple(code) 71 | if model: 72 | macmodelshelf[code] = model 73 | if cleanup and model: 74 | return cleanup_model(model) 75 | else: 76 | return model 77 | 78 | 79 | def _dump(cleanup=True, format=u"json"): 80 | assert format in (u"python", u"json", u"markdown") 81 | def clean(model): 82 | if cleanup: 83 | return cleanup_model(model) 84 | else: 85 | return model 86 | items = macmodelshelf.keys() 87 | items.sort() 88 | items.sort(key=len) 89 | if format == u"python": 90 | print8(u"macmodelshelfdump = {") 91 | print8(u",\n".join([u' "%s": "%s"' % (code, clean(macmodelshelf[code])) for code in items])) 92 | print8(u"}") 93 | elif format == u"json": 94 | print8(u"{") 95 | print8(u",\n".join([u' "%s": "%s"' % (code, clean(macmodelshelf[code])) for code in items])) 96 | print8(u"}") 97 | elif format == u"markdown": 98 | print8(u"Code | Model") 99 | print8(u":--- | :---") 100 | print8(u"\n".join(u'`%s` | %s' % (code, clean(macmodelshelf[code])) for code in items)) 101 | 102 | 103 | def main(argv): 104 | p = argparse.ArgumentParser() 105 | p.add_argument(u"-n", u"--no-cleanup", action=u"store_false", 106 | dest=u"cleanup", help=u"Don't clean up model strings.") 107 | p.add_argument(u"code", help=u"Serial number or model code") 108 | args = p.parse_args([x.decode(u"utf-8") for x in argv[1:]]) 109 | 110 | dump_format = { 111 | u"dump": u"python", 112 | u"dump-python": u"python", 113 | u"dump-json": u"json", 114 | u"dump-markdown": u"markdown", 115 | } 116 | if args.code in dump_format.keys(): 117 | _dump(args.cleanup, dump_format[args.code]) 118 | return 0 119 | 120 | if len(args.code) in (11, 12, 13): 121 | m = model(model_code(args.code), cleanup=args.cleanup) 122 | else: 123 | m = model(args.code, cleanup=args.cleanup) 124 | if m: 125 | print m 126 | return 0 127 | else: 128 | printerr8(u"Unknown model %s" % repr(args.code)) 129 | return os.EX_UNAVAILABLE 130 | 131 | 132 | if __name__ == '__main__': 133 | sys.exit(main(sys.argv)) 134 | --------------------------------------------------------------------------------