| OLD | NEW |
| 1 #!/usr/bin/python | 1 #!/usr/bin/python |
| 2 | 2 |
| 3 """ | 3 """ |
| 4 Copyright 2013 Google Inc. | 4 Copyright 2013 Google Inc. |
| 5 | 5 |
| 6 Use of this source code is governed by a BSD-style license that can be | 6 Use of this source code is governed by a BSD-style license that can be |
| 7 found in the LICENSE file. | 7 found in the LICENSE file. |
| 8 | 8 |
| 9 HTTP server for our HTML rebaseline viewer. | 9 HTTP server for our HTML rebaseline viewer. |
| 10 """ | 10 """ |
| (...skipping 30 matching lines...) Expand all Loading... |
| 41 # Server.results() property. See discussion at | 41 # Server.results() property. See discussion at |
| 42 # https://codereview.chromium.org/195943004/diff/1/gm/rebaseline_server/server.p
y#newcode44 | 42 # https://codereview.chromium.org/195943004/diff/1/gm/rebaseline_server/server.p
y#newcode44 |
| 43 # pylint: enable=C0301 | 43 # pylint: enable=C0301 |
| 44 import compare_configs | 44 import compare_configs |
| 45 import compare_rendered_pictures | 45 import compare_rendered_pictures |
| 46 import compare_to_expectations | 46 import compare_to_expectations |
| 47 import download_actuals | 47 import download_actuals |
| 48 import imagediffdb | 48 import imagediffdb |
| 49 import imagepairset | 49 import imagepairset |
| 50 import results as results_mod | 50 import results as results_mod |
| 51 import writable_expectations as writable_expectations_mod |
| 52 |
| 51 | 53 |
| 52 PATHSPLIT_RE = re.compile('/([^/]+)/(.+)') | 54 PATHSPLIT_RE = re.compile('/([^/]+)/(.+)') |
| 53 | 55 |
| 54 # A simple dictionary of file name extensions to MIME types. The empty string | 56 # A simple dictionary of file name extensions to MIME types. The empty string |
| 55 # entry is used as the default when no extension was given or if the extension | 57 # entry is used as the default when no extension was given or if the extension |
| 56 # has no entry in this dictionary. | 58 # has no entry in this dictionary. |
| 57 MIME_TYPE_MAP = {'': 'application/octet-stream', | 59 MIME_TYPE_MAP = {'': 'application/octet-stream', |
| 58 'html': 'text/html', | 60 'html': 'text/html', |
| 59 'css': 'text/css', | 61 'css': 'text/css', |
| 60 'png': 'image/png', | 62 'png': 'image/png', |
| 61 'js': 'application/javascript', | 63 'js': 'application/javascript', |
| 62 'json': 'application/json' | 64 'json': 'application/json' |
| 63 } | 65 } |
| 64 | 66 |
| 65 # Keys that server.py uses to create the toplevel content header. | 67 # Keys that server.py uses to create the toplevel content header. |
| 66 # NOTE: Keep these in sync with static/constants.js | 68 # NOTE: Keep these in sync with static/constants.js |
| 67 KEY__EDITS__MODIFICATIONS = 'modifications' | 69 KEY__EDITS__MODIFICATIONS = 'modifications' |
| 68 KEY__EDITS__OLD_RESULTS_HASH = 'oldResultsHash' | 70 KEY__EDITS__OLD_RESULTS_HASH = 'oldResultsHash' |
| 69 KEY__EDITS__OLD_RESULTS_TYPE = 'oldResultsType' | 71 KEY__EDITS__OLD_RESULTS_TYPE = 'oldResultsType' |
| 72 KEY__LIVE_EDITS__MODIFICATIONS = 'modifications' |
| 73 KEY__LIVE_EDITS__SET_A_DESCRIPTIONS = 'setA' |
| 74 KEY__LIVE_EDITS__SET_B_DESCRIPTIONS = 'setB' |
| 70 | 75 |
| 71 DEFAULT_ACTUALS_DIR = results_mod.DEFAULT_ACTUALS_DIR | 76 DEFAULT_ACTUALS_DIR = results_mod.DEFAULT_ACTUALS_DIR |
| 72 DEFAULT_GM_SUMMARIES_BUCKET = download_actuals.GM_SUMMARIES_BUCKET | 77 DEFAULT_GM_SUMMARIES_BUCKET = download_actuals.GM_SUMMARIES_BUCKET |
| 73 DEFAULT_JSON_FILENAME = download_actuals.DEFAULT_JSON_FILENAME | 78 DEFAULT_JSON_FILENAME = download_actuals.DEFAULT_JSON_FILENAME |
| 74 DEFAULT_PORT = 8888 | 79 DEFAULT_PORT = 8888 |
| 75 | 80 |
| 76 PARENT_DIRECTORY = os.path.dirname(os.path.realpath(__file__)) | 81 PARENT_DIRECTORY = os.path.dirname(os.path.realpath(__file__)) |
| 77 TRUNK_DIRECTORY = os.path.dirname(os.path.dirname(PARENT_DIRECTORY)) | 82 TRUNK_DIRECTORY = os.path.dirname(os.path.dirname(PARENT_DIRECTORY)) |
| 78 | 83 |
| 79 # Directory, relative to PARENT_DIRECTORY, within which the server will serve | 84 # Directory, relative to PARENT_DIRECTORY, within which the server will serve |
| (...skipping 598 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 678 def do_POST(self): | 683 def do_POST(self): |
| 679 """ Handles all POST requests, forwarding them to the appropriate | 684 """ Handles all POST requests, forwarding them to the appropriate |
| 680 do_POST_* dispatcher. """ | 685 do_POST_* dispatcher. """ |
| 681 # All requests must be of this form: | 686 # All requests must be of this form: |
| 682 # /dispatcher | 687 # /dispatcher |
| 683 # where 'dispatcher' indicates which do_POST_* dispatcher to run. | 688 # where 'dispatcher' indicates which do_POST_* dispatcher to run. |
| 684 logging.debug('do_POST: path="%s"' % self.path) | 689 logging.debug('do_POST: path="%s"' % self.path) |
| 685 normpath = posixpath.normpath(self.path) | 690 normpath = posixpath.normpath(self.path) |
| 686 dispatchers = { | 691 dispatchers = { |
| 687 '/edits': self.do_POST_edits, | 692 '/edits': self.do_POST_edits, |
| 693 '/live-edits': self.do_POST_live_edits, |
| 688 } | 694 } |
| 689 try: | 695 try: |
| 690 dispatcher = dispatchers[normpath] | 696 dispatcher = dispatchers[normpath] |
| 691 dispatcher() | 697 dispatcher() |
| 692 self.send_response(200) | |
| 693 except: | 698 except: |
| 694 self.send_error(404) | 699 self.send_error(404) |
| 695 raise | 700 raise |
| 696 | 701 |
| 697 def do_POST_edits(self): | 702 def do_POST_edits(self): |
| 698 """ Handle a POST request with modifications to GM expectations, in this | 703 """ Handle a POST request with modifications to GM expectations, in this |
| 699 format: | 704 format: |
| 700 | 705 |
| 701 { | 706 { |
| 702 KEY__EDITS__OLD_RESULTS_TYPE: 'all', # type of results that the client | 707 KEY__EDITS__OLD_RESULTS_TYPE: 'all', # type of results that the client |
| (...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 742 raise Exception('results of type "%s" changed while the client was ' | 747 raise Exception('results of type "%s" changed while the client was ' |
| 743 'making modifications. The client should reload the ' | 748 'making modifications. The client should reload the ' |
| 744 'results and submit the modifications again.' % | 749 'results and submit the modifications again.' % |
| 745 oldResultsType) | 750 oldResultsType) |
| 746 _SERVER.results.edit_expectations(data[KEY__EDITS__MODIFICATIONS]) | 751 _SERVER.results.edit_expectations(data[KEY__EDITS__MODIFICATIONS]) |
| 747 | 752 |
| 748 # Read the updated results back from disk. | 753 # Read the updated results back from disk. |
| 749 # We can do this in a separate thread; we should return our success message | 754 # We can do this in a separate thread; we should return our success message |
| 750 # to the UI as soon as possible. | 755 # to the UI as soon as possible. |
| 751 thread.start_new_thread(_SERVER.update_results, (True,)) | 756 thread.start_new_thread(_SERVER.update_results, (True,)) |
| 757 self.send_response(200) |
| 758 |
| 759 def do_POST_live_edits(self): |
| 760 """ Handle a POST request with modifications to SKP expectations, in this |
| 761 format: |
| 762 |
| 763 { |
| 764 KEY__LIVE_EDITS__SET_A_DESCRIPTIONS: { |
| 765 # setA descriptions from the original data |
| 766 }, |
| 767 KEY__LIVE_EDITS__SET_B_DESCRIPTIONS: { |
| 768 # setB descriptions from the original data |
| 769 }, |
| 770 KEY__LIVE_EDITS__MODIFICATIONS: [ |
| 771 # as needed by writable_expectations.modify() |
| 772 ], |
| 773 } |
| 774 |
| 775 Raises an Exception if there were any problems. |
| 776 """ |
| 777 content_type = self.headers[_HTTP_HEADER_CONTENT_TYPE] |
| 778 if content_type != 'application/json;charset=UTF-8': |
| 779 raise Exception('unsupported %s [%s]' % ( |
| 780 _HTTP_HEADER_CONTENT_TYPE, content_type)) |
| 781 |
| 782 content_length = int(self.headers[_HTTP_HEADER_CONTENT_LENGTH]) |
| 783 json_data = self.rfile.read(content_length) |
| 784 data = json.loads(json_data) |
| 785 logging.debug('do_POST_live_edits: received new GM expectations data [%s]' % |
| 786 data) |
| 787 with writable_expectations_mod.WritableExpectations( |
| 788 data[KEY__LIVE_EDITS__SET_A_DESCRIPTIONS]) as writable_expectations: |
| 789 writable_expectations.modify(data[KEY__LIVE_EDITS__MODIFICATIONS]) |
| 790 diffs = writable_expectations.get_diffs() |
| 791 # TODO(stephana): Move to a simpler web framework so we don't have to |
| 792 # call these functions. See http://skbug.com/2856 ('rebaseline_server: |
| 793 # Refactor server to use a simple web framework') |
| 794 self.send_response(200) |
| 795 self.send_header('Content-type', 'text/plain') |
| 796 self.end_headers() |
| 797 self.wfile.write(diffs) |
| 752 | 798 |
| 753 def redirect_to(self, url): | 799 def redirect_to(self, url): |
| 754 """ Redirect the HTTP client to a different url. | 800 """ Redirect the HTTP client to a different url. |
| 755 | 801 |
| 756 Args: | 802 Args: |
| 757 url: URL to redirect the HTTP client to | 803 url: URL to redirect the HTTP client to |
| 758 """ | 804 """ |
| 759 self.send_response(301) | 805 self.send_response(301) |
| 760 self.send_header('Location', url) | 806 self.send_header('Location', url) |
| 761 self.end_headers() | 807 self.end_headers() |
| (...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 898 reload_seconds=args.reload, config_pairs=config_pairs, | 944 reload_seconds=args.reload, config_pairs=config_pairs, |
| 899 builder_regex_list=args.builders, boto_file_path=args.boto, | 945 builder_regex_list=args.builders, boto_file_path=args.boto, |
| 900 imagediffdb_threads=args.threads) | 946 imagediffdb_threads=args.threads) |
| 901 if args.truncate: | 947 if args.truncate: |
| 902 _SERVER.truncate_results = True | 948 _SERVER.truncate_results = True |
| 903 _SERVER.run() | 949 _SERVER.run() |
| 904 | 950 |
| 905 | 951 |
| 906 if __name__ == '__main__': | 952 if __name__ == '__main__': |
| 907 main() | 953 main() |
| OLD | NEW |