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 self.send_response(200) | |
792 self.send_header('Content-type', 'text/plain') | |
793 self.end_headers() | |
794 self.wfile.write(diffs) | |
stephana
2014/08/20 14:27:28
This is another nit. Moving forward we'll have to
epoger
2014/08/20 14:33:57
Done.
| |
752 | 795 |
753 def redirect_to(self, url): | 796 def redirect_to(self, url): |
754 """ Redirect the HTTP client to a different url. | 797 """ Redirect the HTTP client to a different url. |
755 | 798 |
756 Args: | 799 Args: |
757 url: URL to redirect the HTTP client to | 800 url: URL to redirect the HTTP client to |
758 """ | 801 """ |
759 self.send_response(301) | 802 self.send_response(301) |
760 self.send_header('Location', url) | 803 self.send_header('Location', url) |
761 self.end_headers() | 804 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, | 941 reload_seconds=args.reload, config_pairs=config_pairs, |
899 builder_regex_list=args.builders, boto_file_path=args.boto, | 942 builder_regex_list=args.builders, boto_file_path=args.boto, |
900 imagediffdb_threads=args.threads) | 943 imagediffdb_threads=args.threads) |
901 if args.truncate: | 944 if args.truncate: |
902 _SERVER.truncate_results = True | 945 _SERVER.truncate_results = True |
903 _SERVER.run() | 946 _SERVER.run() |
904 | 947 |
905 | 948 |
906 if __name__ == '__main__': | 949 if __name__ == '__main__': |
907 main() | 950 main() |
OLD | NEW |