diff --git a/api/processes_api.py b/api/processes_api.py index 8f48022b..91c256f8 100644 --- a/api/processes_api.py +++ b/api/processes_api.py @@ -26,7 +26,7 @@ class ProcessesAPI(basehandlers.APIHandler): """Return the process of the feature.""" # Load feature directly from NDB so as to never get a stale cached copy. feature_id = kwargs['feature_id'] - f = core_models.Feature.get_by_id(feature_id) + f = core_models.FeatureEntry.get_by_id(feature_id) if f is None: self.abort(404, msg=f'Feature {feature_id} not found') @@ -44,13 +44,13 @@ class ProgressAPI(basehandlers.APIHandler): def do_get(self, **kwargs): """Return the progress of the feature.""" feature_id = kwargs['feature_id'] - f = core_models.Feature.get_by_id(feature_id) - if f is None: + fe = core_models.FeatureEntry.get_by_id(feature_id) + if fe is None: self.abort(404, msg=f'Feature {feature_id} not found') - + stages = core_models.Stage.get_feature_stages(fe.key.integer_id()) progress_so_far = {} for progress_item, detector in list(processes.PROGRESS_DETECTORS.items()): - detected = detector(f) + detected = detector(fe, stages) if detected: progress_so_far[progress_item] = str(detected) return progress_so_far diff --git a/api/processes_api_test.py b/api/processes_api_test.py index 860f3948..ccde9b46 100644 --- a/api/processes_api_test.py +++ b/api/processes_api_test.py @@ -18,6 +18,7 @@ import testing_config # Must be imported before the module under test. import flask from api import processes_api +from internals import core_enums from internals import core_models from internals import processes from internals import core_enums @@ -28,15 +29,22 @@ test_app = flask.Flask(__name__) class ProcessesAPITest(testing_config.CustomTestCase): def setUp(self): - self.feature_1 = core_models.Feature( - name='feature one', summary='sum', category=1) + self.feature_1 = core_models.FeatureEntry( + name='feature one', summary='sum', category=1, feature_type=0) self.feature_1.put() self.feature_id = self.feature_1.key.integer_id() - + stage_types = [110, 120, 130, 140, 150, 151, 160] + self.stages: list[core_models.Stage] = [] + for s_type in stage_types: + stage = core_models.Stage(feature_id=self.feature_id, stage_type=s_type) + stage.put() + self.stages.append(stage) self.handler = processes_api.ProcessesAPI() self.request_path = f'/api/v0/features/{self.feature_id}/process' def tearDown(self): + for stage in self.stages: + stage.key.delete() self.feature_1.key.delete() def test_get__default_feature_type(self): @@ -82,17 +90,33 @@ class ProcessesAPITest(testing_config.CustomTestCase): class ProgressAPITest(testing_config.CustomTestCase): def setUp(self): - self.feature_1 = core_models.Feature( + self.feature_1 = core_models.FeatureEntry( name='feature one', summary='sum Z', - owner=['feature_owner@example.com'], - ready_for_trial_url='fake ready for trial url', - intent_to_experiment_url='fake intent to experiment url', + owner_emails=['feature_owner@example.com'], spec_link='fake spec link', category=1, web_dev_views=1, impl_status_chrome=5, intent_stage=core_enums.INTENT_IMPLEMENT, - shipped_milestone=1) + feature_type=0) self.feature_1.put() self.feature_id = self.feature_1.key.integer_id() + stage_types = [110, 120, 130, 140, 150, 151, 160] + self.stages: list[core_models.Stage] = [] + for s_type in stage_types: + stage = core_models.Stage(feature_id=self.feature_id, stage_type=s_type) + if s_type == 120: + stage.intent_thread_url = 'https://example.com/prototype' + elif s_type == 130: + stage.announcement_url = 'https://example.com/ready_for_trial' + elif s_type == 150: + stage.intent_thread_url = 'https://example.com/ot' + elif s_type == 151: + stage.intent_thread_url = 'https://example.com/extend' + elif s_type == 160: + stage.milestones = core_models.MilestoneSet(desktop_first=1) + stage.intent_thread_url = 'https://example.com/ship' + stage.put() + self.stages.append(stage) + self.handler = processes_api.ProgressAPI() self.request_path = f'/api/v0/features/{self.feature_id}/progress' @@ -110,8 +134,10 @@ class ProgressAPITest(testing_config.CustomTestCase): 'Draft API spec': 'fake spec link', 'Estimated target milestone': 'True', 'Final target milestone': 'True', - 'Intent to Experiment email': 'fake intent to experiment url', - 'Ready for Trial email': 'fake ready for trial url', + 'Intent to Prototype email': 'https://example.com/prototype', + 'Intent to Experiment email': 'https://example.com/ot', + 'Ready for Trial email': 'https://example.com/ready_for_trial', + 'Intent to Ship email': 'https://example.com/ship', 'Spec link': 'fake spec link', 'Web developer signals': 'True', }, actual) diff --git a/internals/processes.py b/internals/processes.py index 6e102376..953ceee8 100644 --- a/internals/processes.py +++ b/internals/processes.py @@ -17,7 +17,6 @@ import collections from internals import approval_defs from internals import core_enums -from internals import core_models Process = collections.namedtuple( @@ -523,99 +522,94 @@ def review_is_done(status): # be used as a link URL. PROGRESS_DETECTORS = { 'Initial public proposal': - lambda f: f.initial_public_proposal_url, + lambda f, _: f.initial_public_proposal_url, 'Explainer': - lambda f: f.explainer_links and f.explainer_links[0], + lambda f, _: f.explainer_links and f.explainer_links[0], 'Security review issues addressed': - lambda f: review_is_done(f.security_review_status), + lambda f, _: review_is_done(f.security_review_status), 'Privacy review issues addressed': - lambda f: review_is_done(f.privacy_review_status), + lambda f, _: review_is_done(f.privacy_review_status), 'Intent to Prototype email': - lambda f: f.intent_to_implement_url, + lambda f, stages: ( + core_enums.STAGE_TYPES_PROTOTYPE[f.feature_type] and + stages[core_enums.STAGE_TYPES_PROTOTYPE[f.feature_type]].intent_thread_url), 'Intent to Ship email': - lambda f: f.intent_to_ship_url, + lambda f, stages: (core_enums.STAGE_TYPES_SHIPPING[f.feature_type] and + stages[core_enums.STAGE_TYPES_SHIPPING[f.feature_type]].intent_thread_url), 'Ready for Trial email': - lambda f: f.ready_for_trial_url, + lambda f, stages: (core_enums.STAGE_TYPES_DEV_TRIAL[f.feature_type] and + stages[core_enums.STAGE_TYPES_DEV_TRIAL[f.feature_type]].announcement_url), 'Intent to Experiment email': - lambda f: f.intent_to_experiment_url, - - 'One LGTM on Intent to Experiment': - lambda f: f.i2e_lgtms, - - 'One LGTM on Request for Deprecation Trial': - lambda f: f.i2e_lgtms, - - 'Three LGTMs on Intent to Ship': - lambda f: f.i2s_lgtms and len(f.i2s_lgtms) >= 3, + lambda f, stages: (core_enums.STAGE_TYPES_ORIGIN_TRIAL[f.feature_type] and + stages[core_enums.STAGE_TYPES_ORIGIN_TRIAL[f.feature_type]].intent_thread_url), 'Samples': - lambda f: f.sample_links and f.sample_links[0], + lambda f, _: f.sample_links and f.sample_links[0], 'Doc links': - lambda f: f.doc_links and f.doc_links[0], + lambda f, _: f.doc_links and f.doc_links[0], 'Spec link': - lambda f: f.spec_link, + lambda f, _: f.spec_link, 'Draft API spec': - lambda f: f.spec_link, + lambda f, _: f.spec_link, 'API spec': - lambda f: f.api_spec, + lambda f, _: f.api_spec, 'Spec mentor': - lambda f: f.spec_mentors, + lambda f, _: f.spec_mentor_emails, 'TAG review requested': - lambda f: f.tag_review, + lambda f, _: f.tag_review, 'TAG review issues addressed': - lambda f: review_is_done(f.tag_review_status), + lambda f, _: review_is_done(f.tag_review_status), 'Web developer signals': - lambda f: bool(f.web_dev_views and - f.web_dev_views != core_enums.DEV_NO_SIGNALS), + lambda f, _: bool(f.web_dev_views and + f.web_dev_views != core_enums.DEV_NO_SIGNALS), 'Vendor signals': - lambda f: bool( + lambda f, _: bool( f.ff_views != core_enums.NO_PUBLIC_SIGNALS or - f.safari_views != core_enums.NO_PUBLIC_SIGNALS or - f.ie_views != core_enums.NO_PUBLIC_SIGNALS), # IE Deprecated + f.safari_views != core_enums.NO_PUBLIC_SIGNALS), 'Updated vendor signals': - lambda f: bool( + lambda f, _: bool( f.ff_views != core_enums.NO_PUBLIC_SIGNALS or - f.safari_views != core_enums.NO_PUBLIC_SIGNALS or - f.ie_views != core_enums.NO_PUBLIC_SIGNALS), # IE Deprecated + f.safari_views != core_enums.NO_PUBLIC_SIGNALS), 'Final vendor signals': - lambda f: bool( + lambda f, _: bool( f.ff_views != core_enums.NO_PUBLIC_SIGNALS or - f.safari_views != core_enums.NO_PUBLIC_SIGNALS or - f.ie_views != core_enums.NO_PUBLIC_SIGNALS), # IE Deprecated + f.safari_views != core_enums.NO_PUBLIC_SIGNALS), 'Estimated target milestone': - lambda f: bool(f.shipped_milestone), + lambda f, stages: bool(core_enums.STAGE_TYPES_SHIPPING[f.feature_type] and + stages[core_enums.STAGE_TYPES_SHIPPING[f.feature_type]].milestones.desktop_first), 'Final target milestone': - lambda f: bool(f.shipped_milestone), + lambda f, stages: bool(core_enums.STAGE_TYPES_SHIPPING[f.feature_type] and + stages[core_enums.STAGE_TYPES_SHIPPING[f.feature_type]].milestones.desktop_first), 'Code in Chromium': - lambda f: f.impl_status_chrome in ( + lambda f, _: f.impl_status_chrome in ( core_enums.IN_DEVELOPMENT, core_enums.BEHIND_A_FLAG, core_enums.ENABLED_BY_DEFAULT, core_enums.ORIGIN_TRIAL, core_enums.INTERVENTION), 'Motivation': - lambda f: bool(f.motivation), + lambda f, _: bool(f.motivation), 'Code removed': - lambda f: f.impl_status_chrome == core_enums.REMOVED, + lambda f, _: f.impl_status_chrome == core_enums.REMOVED, } diff --git a/internals/processes_test.py b/internals/processes_test.py index c2c37aaa..a44a88ea 100644 --- a/internals/processes_test.py +++ b/internals/processes_test.py @@ -141,140 +141,130 @@ class ProcessesWellFormedTest(testing_config.CustomTestCase): class ProgressDetectorsTest(testing_config.CustomTestCase): def setUp(self): - self.feature_1 = core_models.Feature( + self.feature_1 = core_models.FeatureEntry( name='feature one', summary='sum', category=1, - intent_stage=core_enums.INTENT_IMPLEMENT) + intent_stage=core_enums.INTENT_IMPLEMENT, feature_type=0) self.feature_1.put() + stage_types = [110, 120, 130, 140, 150, 151, 160] + self.stages: list[core_models.Stage] = [] + for s_type in stage_types: + stage = core_models.Stage(feature_id=self.feature_1.key.integer_id(), + stage_type=s_type) + stage.put() + self.stages.append(stage) + self.stages_dict = core_models.Stage.get_feature_stages( + self.feature_1.key.integer_id()) def tearDown(self): self.feature_1.key.delete() + for stage in self.stages: + stage.key.delete() def test_initial_public_proposal_url(self): detector = processes.PROGRESS_DETECTORS['Initial public proposal'] - self.assertFalse(detector(self.feature_1)) + self.assertFalse(detector(self.feature_1, self.stages_dict)) self.feature_1.initial_public_proposal_url = 'http://example.com' - self.assertTrue(detector(self.feature_1)) + self.assertTrue(detector(self.feature_1, self.stages_dict)) def test_explainer(self): detector = processes.PROGRESS_DETECTORS['Explainer'] - self.assertFalse(detector(self.feature_1)) + self.assertFalse(detector(self.feature_1, self.stages_dict)) self.feature_1.explainer_links = ['http://example.com'] - self.assertTrue(detector(self.feature_1)) + self.assertTrue(detector(self.feature_1, self.stages_dict)) def test_security_review_completed(self): detector = processes.PROGRESS_DETECTORS['Security review issues addressed'] - self.assertFalse(detector(self.feature_1)) + self.assertFalse(detector(self.feature_1, self.stages_dict)) self.feature_1.security_review_status = core_enums.REVIEW_ISSUES_ADDRESSED - self.assertTrue(detector(self.feature_1)) + self.assertTrue(detector(self.feature_1, self.stages_dict)) def test_privacy_review_completed(self): detector = processes.PROGRESS_DETECTORS['Privacy review issues addressed'] - self.assertFalse(detector(self.feature_1)) + self.assertFalse(detector(self.feature_1, self.stages_dict)) self.feature_1.privacy_review_status = core_enums.REVIEW_ISSUES_ADDRESSED - self.assertTrue(detector(self.feature_1)) + self.assertTrue(detector(self.feature_1, self.stages_dict)) def test_intent_to_prototype_email(self): detector = processes.PROGRESS_DETECTORS['Intent to Prototype email'] - self.assertFalse(detector(self.feature_1)) - self.feature_1.intent_to_implement_url = 'http://example.com' - self.assertTrue(detector(self.feature_1)) + self.assertFalse(detector(self.feature_1, self.stages_dict)) + self.stages_dict[120].intent_thread_url = 'http://example.com/prototype' + self.assertTrue(detector(self.feature_1, self.stages_dict)) def test_intent_to_ship_email(self): detector = processes.PROGRESS_DETECTORS['Intent to Ship email'] - self.assertFalse(detector(self.feature_1)) - self.feature_1.intent_to_ship_url = 'http://example.com' - self.assertTrue(detector(self.feature_1)) + self.assertFalse(detector(self.feature_1, self.stages_dict)) + self.stages_dict[160].intent_thread_url = 'http://example.com/ship' + self.assertTrue(detector(self.feature_1, self.stages_dict)) def test_ready_for_trial_email(self): detector = processes.PROGRESS_DETECTORS['Ready for Trial email'] - self.assertFalse(detector(self.feature_1)) - self.feature_1.ready_for_trial_url = 'http://example.com' - self.assertTrue(detector(self.feature_1)) + self.assertFalse(detector(self.feature_1, self.stages_dict)) + self.stages_dict[130].announcement_url = 'http://example.com/trial_ready' + self.assertTrue(detector(self.feature_1, self.stages_dict)) def test_intent_to_experiment_email(self): detector = processes.PROGRESS_DETECTORS['Intent to Experiment email'] - self.assertFalse(detector(self.feature_1)) - self.feature_1.intent_to_experiment_url = 'http://example.com' - self.assertTrue(detector(self.feature_1)) - - def test_one_i2e_lgtm(self): - detector = processes.PROGRESS_DETECTORS['One LGTM on Intent to Experiment'] - self.assertFalse(detector(self.feature_1)) - self.feature_1.i2e_lgtms = ['api_owner@chromium.org'] - self.assertTrue(detector(self.feature_1)) - - def test_two_i2e_lgtm(self): - detector = processes.PROGRESS_DETECTORS[ - 'One LGTM on Request for Deprecation Trial'] - self.assertFalse(detector(self.feature_1)) - self.feature_1.i2e_lgtms = ['api_owner@chromium.org'] - self.assertTrue(detector(self.feature_1)) - - def test_three_i2s_lgtm(self): - detector = processes.PROGRESS_DETECTORS['Three LGTMs on Intent to Ship'] - self.assertFalse(detector(self.feature_1)) - self.feature_1.i2s_lgtms = [ - 'one@chromium.org', - 'two@chromium.org', - 'three@chromium.org'] - self.assertTrue(detector(self.feature_1)) + self.assertFalse(detector(self.feature_1, self.stages_dict)) + self.stages_dict[150].intent_thread_url = 'http://example.com/ot' + self.assertTrue(detector(self.feature_1, self.stages_dict)) def test_samples(self): detector = processes.PROGRESS_DETECTORS['Samples'] - self.assertFalse(detector(self.feature_1)) + self.assertFalse(detector(self.feature_1, self.stages_dict)) self.feature_1.sample_links = ['http://example.com'] - self.assertTrue(detector(self.feature_1)) + self.assertTrue(detector(self.feature_1, self.stages_dict)) def test_doc_links(self): detector = processes.PROGRESS_DETECTORS['Doc links'] - self.assertFalse(detector(self.feature_1)) + self.assertFalse(detector(self.feature_1, self.stages_dict)) self.feature_1.doc_links = ['http://example.com'] - self.assertTrue(detector(self.feature_1)) + self.assertTrue(detector(self.feature_1, self.stages_dict)) def test_tag_review_requested(self): detector = processes.PROGRESS_DETECTORS['TAG review requested'] - self.assertFalse(detector(self.feature_1)) + self.assertFalse(detector(self.feature_1, self.stages_dict)) self.feature_1.tag_review = 'http://example.com' - self.assertTrue(detector(self.feature_1)) + self.assertTrue(detector(self.feature_1, self.stages_dict)) def test_tag_review_completed(self): detector = processes.PROGRESS_DETECTORS['TAG review issues addressed'] - self.assertFalse(detector(self.feature_1)) + self.assertFalse(detector(self.feature_1, self.stages_dict)) self.feature_1.tag_review_status = core_enums.REVIEW_ISSUES_ADDRESSED - self.assertTrue(detector(self.feature_1)) + self.assertTrue(detector(self.feature_1, self.stages_dict)) - def test_web_dav_signals(self): + def test_web_dev_signals(self): detector = processes.PROGRESS_DETECTORS['Web developer signals'] - self.assertFalse(detector(self.feature_1)) + self.assertFalse(detector(self.feature_1, self.stages_dict)) self.feature_1.web_dev_views = core_enums.PUBLIC_SUPPORT - self.assertTrue(detector(self.feature_1)) + self.assertTrue(detector(self.feature_1, self.stages_dict)) def test_vendor_signals(self): detector = processes.PROGRESS_DETECTORS['Vendor signals'] - self.assertFalse(detector(self.feature_1)) + self.assertFalse(detector(self.feature_1, self.stages_dict)) self.feature_1.ff_views = core_enums.PUBLIC_SUPPORT - self.assertTrue(detector(self.feature_1)) + self.assertTrue(detector(self.feature_1, self.stages_dict)) def test_estimated_target_milestone(self): detector = processes.PROGRESS_DETECTORS['Estimated target milestone'] - self.assertFalse(detector(self.feature_1)) - self.feature_1.shipped_milestone = 99 - self.assertTrue(detector(self.feature_1)) + self.stages_dict[160].milestones = core_models.MilestoneSet() + self.assertFalse(detector(self.feature_1, self.stages_dict)) + self.stages_dict[160].milestones.desktop_first = 99 + self.assertTrue(detector(self.feature_1, self.stages_dict)) def test_code_in_chromium(self): detector = processes.PROGRESS_DETECTORS['Code in Chromium'] - self.assertFalse(detector(self.feature_1)) + self.assertFalse(detector(self.feature_1, self.stages_dict)) self.feature_1.impl_status_chrome = core_enums.ENABLED_BY_DEFAULT - self.assertTrue(detector(self.feature_1)) + self.assertTrue(detector(self.feature_1, self.stages_dict)) def test_motivation(self): detector = processes.PROGRESS_DETECTORS['Motivation'] - self.assertFalse(detector(self.feature_1)) + self.assertFalse(detector(self.feature_1, self.stages_dict)) self.feature_1.motivation = 'test motivation' - self.assertTrue(detector(self.feature_1)) + self.assertTrue(detector(self.feature_1, self.stages_dict)) def test_code_removed(self): detector = processes.PROGRESS_DETECTORS['Code removed'] - self.assertFalse(detector(self.feature_1)) + self.assertFalse(detector(self.feature_1, self.stages_dict)) self.feature_1.impl_status_chrome = core_enums.REMOVED - self.assertTrue(detector(self.feature_1)) + self.assertTrue(detector(self.feature_1, self.stages_dict))