From 5615a6c4dafd0feb56de07f0920e4c21cda67bf4 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Mon, 4 Jun 2018 22:59:30 +0100 Subject: [PATCH 1/4] Update api stubs ## Summary Fix lot capitalisation to only capitalise the first letter, and set further_competition/direct_award flags based on framework family. --- dmutils/__init__.py | 2 +- dmutils/api_stubs.py | 32 +++++++++++++++++++------------- tests/test_api_stubs.py | 14 +++++++------- 3 files changed, 27 insertions(+), 21 deletions(-) diff --git a/dmutils/__init__.py b/dmutils/__init__.py index 10d02c0d..f44dcc2a 100644 --- a/dmutils/__init__.py +++ b/dmutils/__init__.py @@ -4,4 +4,4 @@ import flask_featureflags # noqa -__version__ = '38.1.0' +__version__ = '38.1.1' diff --git a/dmutils/api_stubs.py b/dmutils/api_stubs.py index 59ae31d0..0a88fffc 100644 --- a/dmutils/api_stubs.py +++ b/dmutils/api_stubs.py @@ -15,7 +15,8 @@ def _derive_framework_family(slug): def lot(lot_id=1, slug="some-lot", name=None, allows_brief=False, one_service_limit=False, unit_singular='service', unit_plural='services'): if not name: - name = slug.replace("-", " ").title() + name = slug.replace("-", " ") + name = name[:1].upper() + name[1:] return { "id": lot_id, @@ -76,19 +77,24 @@ def framework(framework_id=1, intention_to_award_at=dt(2000, 1, 4), framework_live_at=dt(2000, 1, 5), framework_expires_at=dt(2000, 1, 6), - has_direct_award=True, - has_further_competition=False): - if name: - pass - elif slug.startswith('g-cloud'): - name = 'G-Cloud {}'.format(slug.split('-')[-1]) - elif slug.startswith("digital-outcomes-and-specialists"): - name = slug.replace("-", " ").title() - name = name.replace('And', 'and') - else: - name = slug.replace("-", " ").title() + has_direct_award=None, + has_further_competition=None): + framework_family = framework_family or _derive_framework_family(slug) + + if slug.startswith('g-cloud'): + name = name or 'G-Cloud {}'.format(slug.split('-')[-1]) + has_direct_award = has_direct_award if has_direct_award is not None else True + has_further_competition = has_further_competition if has_further_competition is not None else False + + elif slug.startswith('digital-outcomes-and-specialists'): + name = name or slug.replace("-", " ").title().replace('And', 'and') + has_direct_award = has_direct_award if has_direct_award is not None else False + has_further_competition = has_further_competition if has_further_competition is not None else True - framework_family = _derive_framework_family(slug) if not framework_family else framework_family + else: + name = name or slug.replace("-", " ").title() + has_direct_award = has_direct_award if has_direct_award is not None else True + has_further_competition = has_further_competition if has_further_competition is not None else True lots = lots or [] diff --git a/tests/test_api_stubs.py b/tests/test_api_stubs.py index ed71b9b6..0bbfbb8d 100644 --- a/tests/test_api_stubs.py +++ b/tests/test_api_stubs.py @@ -7,7 +7,7 @@ def test_default_values(self): assert api_stubs.lot() == { "id": 1, "slug": "some-lot", - "name": "Some Lot", + "name": "Some lot", "allowsBrief": False, "oneServiceLimit": False, "unitSingular": "service", @@ -19,7 +19,7 @@ def test_override_values(self): unit_singular='brief', unit_plural='briefs') == { "id": 2, "slug": "my-special-lot", - "name": "My Special Lot", + "name": "My special lot", "allowsBrief": True, "oneServiceLimit": True, "unitSingular": "brief", @@ -72,8 +72,8 @@ def test_with_lots(self): "frameworkRefDate": "29-06-2000", "frameworkStartDate": "05 January 2000", "frameworkURL": f"https://www.gov.uk/government/publications/g-cloud-7", - "lotDescriptions": {"cloud-hosting": "Lot 1: Cloud Hosting", - "cloud-support": "Lot 2: Cloud Support"}, + "lotDescriptions": {"cloud-hosting": "Lot 1: Cloud hosting", + "cloud-support": "Lot 2: Cloud support"}, "lotOrder": ["cloud-hosting", "cloud-support"], "pageTotal": 99, "signaturePageNumber": 98, @@ -130,8 +130,8 @@ def test_custom_slug_derives_name_and_framework(self): 'intentionToAwardAtUTC': '2000-01-04T00:00:00.000000Z', 'frameworkLiveAtUTC': '2000-01-05T00:00:00.000000Z', 'frameworkExpiresAtUTC': '2000-01-06T00:00:00.000000Z', - 'hasDirectAward': True, - 'hasFurtherCompetition': False, + 'hasDirectAward': False, + 'hasFurtherCompetition': True, } } @@ -158,7 +158,7 @@ def test_override_status_slug_name_and_clarification_questions(self): 'frameworkLiveAtUTC': '2000-01-05T00:00:00.000000Z', 'frameworkExpiresAtUTC': '2000-01-06T00:00:00.000000Z', 'hasDirectAward': True, - 'hasFurtherCompetition': False, + 'hasFurtherCompetition': True, } } From ac6da32d3592029c65fffec7f82551fbb869b838 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Tue, 5 Jun 2018 07:55:44 +0100 Subject: [PATCH 2/4] Add known lots for G-Cloud and DOS ## Summary There are lots of places in frontend apps where we want to create known lots for G-Cloud/Digital Outcomes and Specialists frameworks, so let's simplify this by creating simple pre-configured lots. We'll also update the framework stub to populate these lots correctly for our two known frameworks if no argument is supplied. Bump version to 38.2.0 --- dmutils/__init__.py | 2 +- dmutils/api_stubs.py | 41 +++++++++++++++++++++++++++++++++++++++-- tests/test_api_stubs.py | 21 +++++++++++++-------- 3 files changed, 53 insertions(+), 11 deletions(-) diff --git a/dmutils/__init__.py b/dmutils/__init__.py index f44dcc2a..cae5db76 100644 --- a/dmutils/__init__.py +++ b/dmutils/__init__.py @@ -4,4 +4,4 @@ import flask_featureflags # noqa -__version__ = '38.1.1' +__version__ = '38.2.0' diff --git a/dmutils/api_stubs.py b/dmutils/api_stubs.py index 0a88fffc..9d4bd0ba 100644 --- a/dmutils/api_stubs.py +++ b/dmutils/api_stubs.py @@ -29,6 +29,41 @@ def lot(lot_id=1, slug="some-lot", name=None, allows_brief=False, one_service_li } +def __as_a_service_lots(): + return [ + lot(lot_id=1, slug='saas', name='Software as a Service'), + lot(lot_id=2, slug='paas', name='Platform as a Service'), + lot(lot_id=3, slug='iaas', name='Infrastructure as a Service'), + lot(lot_id=4, slug='scs', name='Specialist Cloud Services') + ] + + +def __cloud_lots(): + return [ + lot(lot_id=9, slug='cloud-hosting', name='Cloud hosting'), + lot(lot_id=10, slug='cloud-software', name='Cloud software'), + lot(lot_id=11, slug='cloud-support', name='Cloud support') + ] + + +g_cloud_4_lots = __as_a_service_lots +g_cloud_5_lots = __as_a_service_lots +g_cloud_6_lots = __as_a_service_lots +g_cloud_7_lots = __as_a_service_lots +g_cloud_8_lots = __as_a_service_lots +g_cloud_9_lots = __cloud_lots +g_cloud_10_lots = __cloud_lots + + +def dos_lots(): + return [ + lot(lot_id=5, slug='digital-outcomes', name='Digital outcomes', allows_brief=True), + lot(lot_id=6, slug='digital-specialists', name='Digital specialists', allows_brief=True), + lot(lot_id=7, slug='user-research-studios', name='User research studios'), + lot(lot_id=8, slug='user-research-participants', name='User research participants', allows_brief=True) + ] + + def framework_agreement_details(slug='g-cloud-7', framework_agreement_version='RM1557x', framework_variations=None, @@ -85,18 +120,20 @@ def framework(framework_id=1, name = name or 'G-Cloud {}'.format(slug.split('-')[-1]) has_direct_award = has_direct_award if has_direct_award is not None else True has_further_competition = has_further_competition if has_further_competition is not None else False + framework_iteration = int(slug.split('-')[-1]) + lots = lots if lots else (__as_a_service_lots() if framework_iteration <= 8 else __cloud_lots()) elif slug.startswith('digital-outcomes-and-specialists'): name = name or slug.replace("-", " ").title().replace('And', 'and') has_direct_award = has_direct_award if has_direct_award is not None else False has_further_competition = has_further_competition if has_further_competition is not None else True + lots = lots if lots else dos_lots() else: name = name or slug.replace("-", " ").title() has_direct_award = has_direct_award if has_direct_award is not None else True has_further_competition = has_further_competition if has_further_competition is not None else True - - lots = lots or [] + lots = lots or [] agreement_details = framework_agreement_details(slug=slug, framework_agreement_version=framework_agreement_version, diff --git a/tests/test_api_stubs.py b/tests/test_api_stubs.py index 0bbfbb8d..618d8101 100644 --- a/tests/test_api_stubs.py +++ b/tests/test_api_stubs.py @@ -82,6 +82,10 @@ def test_with_lots(self): class TestFramework: + def setup(self): + self.g7_lots = api_stubs.g_cloud_7_lots() + self.dos_lots = api_stubs.dos_lots() + def test_default_values(self): assert api_stubs.framework() == { "frameworks": { @@ -91,9 +95,9 @@ def test_default_values(self): "framework": "g-cloud", "status": "open", "clarificationQuestionsOpen": True, - "lots": [], + "lots": self.g7_lots, "allowDeclarationReuse": True, - "frameworkAgreementDetails": api_stubs.framework_agreement_details(), + "frameworkAgreementDetails": api_stubs.framework_agreement_details(lots=self.g7_lots), "countersignerName": None, "frameworkAgreementVersion": "RM1557x", "variations": {}, @@ -109,7 +113,8 @@ def test_default_values(self): } def test_custom_slug_derives_name_and_framework(self): - framework_agreement_details = api_stubs.framework_agreement_details(slug='digital-outcomes-and-specialists') + framework_agreement_details = api_stubs.framework_agreement_details(slug='digital-outcomes-and-specialists', + lots=self.dos_lots) assert api_stubs.framework(slug='digital-outcomes-and-specialists') == { "frameworks": { "id": 1, @@ -118,7 +123,7 @@ def test_custom_slug_derives_name_and_framework(self): "framework": "digital-outcomes-and-specialists", "status": "open", "clarificationQuestionsOpen": True, - "lots": [], + "lots": self.dos_lots, "allowDeclarationReuse": True, "frameworkAgreementDetails": framework_agreement_details, "countersignerName": None, @@ -203,9 +208,9 @@ def test_custom_dates(self): "framework": "g-cloud", "status": "open", "clarificationQuestionsOpen": True, - "lots": [], + "lots": self.g7_lots, "allowDeclarationReuse": True, - "frameworkAgreementDetails": api_stubs.framework_agreement_details(), + "frameworkAgreementDetails": api_stubs.framework_agreement_details(lots=self.g7_lots), "countersignerName": None, "frameworkAgreementVersion": "RM1557x", "variations": {}, @@ -229,9 +234,9 @@ def test_further_competition_vs_direct_award(self): "framework": "g-cloud", "status": "open", "clarificationQuestionsOpen": True, - "lots": [], + "lots": self.g7_lots, "allowDeclarationReuse": True, - "frameworkAgreementDetails": api_stubs.framework_agreement_details(), + "frameworkAgreementDetails": api_stubs.framework_agreement_details(lots=self.g7_lots), "countersignerName": None, "frameworkAgreementVersion": "RM1557x", "variations": {}, From f8f45d2d142c2bd5a2a7699dafcd4605b299eb91 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Tue, 5 Jun 2018 08:22:25 +0100 Subject: [PATCH 3/4] remove `__init__` from mixin ## Summary `py.test` won't collect test classes that have an `__init__` method, which was included on a mixin added to the email test classes. This meant those tests weren't running! Let's remove the init as it is not needed for functionality. --- tests/helpers.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/tests/helpers.py b/tests/helpers.py index 9c61717e..8c64a309 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -79,12 +79,6 @@ def assert_external_service_log_entry(service='\w+', description='.+', successfu class PatchExternalServiceLogConditionMixin: - def __init__(self, *args, **kwargs): - super().__init__(*args, **kwargs) - - self.log_condition_patch = None - self.log_condition = None - def setup(self): self.log_condition_patch = mock.patch('dmutils.timing.request_context_and_any_of_slow_call_or_sampled_request') self.log_condition = self.log_condition_patch.start() From 3ff173117091e6859142f06a4e520c8d97d20242 Mon Sep 17 00:00:00 2001 From: Samuel Williams Date: Tue, 5 Jun 2018 11:43:25 +0100 Subject: [PATCH 4/4] Updates from review --- dmutils/api_stubs.py | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/dmutils/api_stubs.py b/dmutils/api_stubs.py index 9d4bd0ba..84dacd51 100644 --- a/dmutils/api_stubs.py +++ b/dmutils/api_stubs.py @@ -15,8 +15,7 @@ def _derive_framework_family(slug): def lot(lot_id=1, slug="some-lot", name=None, allows_brief=False, one_service_limit=False, unit_singular='service', unit_plural='services'): if not name: - name = slug.replace("-", " ") - name = name[:1].upper() + name[1:] + name = slug.replace("-", " ").capitalize() return { "id": lot_id, @@ -57,10 +56,13 @@ def __cloud_lots(): def dos_lots(): return [ - lot(lot_id=5, slug='digital-outcomes', name='Digital outcomes', allows_brief=True), - lot(lot_id=6, slug='digital-specialists', name='Digital specialists', allows_brief=True), - lot(lot_id=7, slug='user-research-studios', name='User research studios'), - lot(lot_id=8, slug='user-research-participants', name='User research participants', allows_brief=True) + lot(lot_id=5, slug='digital-outcomes', name='Digital outcomes', allows_brief=True, one_service_limit=True), + lot(lot_id=6, slug='digital-specialists', name='Digital specialists', allows_brief=True, + one_service_limit=True), + lot(lot_id=7, slug='user-research-studios', name='User research studios', unit_singular='lab', + unit_plural='labs'), + lot(lot_id=8, slug='user-research-participants', name='User research participants', allows_brief=True, + one_service_limit=True) ]