Skip to content

Commit

Permalink
Merge pull request #8 from gorserg/add_stage2
Browse files Browse the repository at this point in the history
add stage2 model and views
  • Loading branch information
kroman0 committed Jul 1, 2016
2 parents b988a12 + 53a6489 commit cd3ea06
Show file tree
Hide file tree
Showing 4 changed files with 215 additions and 9 deletions.
5 changes: 4 additions & 1 deletion openprocurement/tender/competitivedialogue/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,12 @@ def includeme(config):
:param config: Pyramid server configuration
:return:
"""
from openprocurement.tender.competitivedialogue.models import CompetitiveDialogUA, CompetitiveDialogEU
from openprocurement.tender.competitivedialogue.models import (CompetitiveDialogUA, CompetitiveDialogEU,
TenderStage2EU, TenderStage2UA)
LOGGER.info('init competitivedialogue plugin')
# add two types of Competitive Dialogue
config.add_tender_procurementMethodType(CompetitiveDialogUA)
config.add_tender_procurementMethodType(CompetitiveDialogEU)
config.add_tender_procurementMethodType(TenderStage2EU)
config.add_tender_procurementMethodType(TenderStage2UA)
config.scan("openprocurement.tender.competitivedialogue.views")
146 changes: 143 additions & 3 deletions openprocurement/tender/competitivedialogue/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,14 @@
from schematics.types import StringType
from schematics.exceptions import ValidationError
from zope.interface import implementer
from pyramid.security import Allow
from schematics.types.compound import ModelType
from openprocurement.api.models import ITender
from openprocurement.api.models import ITender, Identifier, Model
from openprocurement.tender.openua.models import SifterListType, Item as BaseItem
from openprocurement.tender.openeu.models import (Tender as TenderEU, Administrator_bid_role, view_bid_role,
pre_qualifications_role, Bid as BidEU, ConfidentialDocument)
pre_qualifications_role, Bid as BidEU, ConfidentialDocument,
edit_role_eu, auction_patch_role, auction_view_role,
auction_post_role)
from openprocurement.api.models import (
plain_role, create_role, edit_role, view_role, listing_role,
enquiries_role, validate_cpv_group, validate_items_uniq,
Expand All @@ -19,8 +22,12 @@
# constants for procurementMethodtype
CD_UA_TYPE = "competitiveDialogueUA"
CD_EU_TYPE = "competitiveDialogueEU"
STAGE_2_EU_TYPE = "competitiveDialogueEU.stage2"
STAGE_2_UA_TYPE = "competitiveDialogueUA.stage2"

edit_role_ua = edit_role + blacklist('enquiryPeriod', 'status')
edit_stage2_pending = whitelist('status')
edit_stage2_waiting = whitelist('status', 'stage2TenderID')

roles = {
'plain': plain_role,
Expand All @@ -31,7 +38,7 @@
'active.pre-qualification.stand-still': pre_qualifications_role,
'active.stage2.pending': enquiries_role,
'active.stage2.waiting': pre_qualifications_role,
'edit_active.stage2.pending': pre_qualifications_role,
'edit_active.stage2.pending': whitelist('status'),
'draft': enquiries_role,
'active.tendering': enquiries_role,
'complete': view_role,
Expand All @@ -42,8 +49,10 @@
'Administrator': Administrator_role,
'default': schematics_default_role,
'contracting': whitelist('doc_id', 'owner'),
'competitive_dialogue': edit_stage2_waiting,
}


class Document(ConfidentialDocument):
""" Document model with new feature as Description of the decision to purchase """

Expand Down Expand Up @@ -105,21 +114,152 @@ class Tender(TenderEU):
bids = SifterListType(ModelType(Bid), default=list(),
filter_by='status', filter_in_values=['invalid', 'deleted'])
TenderID = StringType(required=False)
stage2TenderID = StringType(required=False)

class Options:
roles = roles.copy()

def get_role(self):
root = self.__parent__
request = root.request
if request.authenticated_role == 'Administrator':
role = 'Administrator'
elif request.authenticated_role == 'chronograph':
role = 'chronograph'
elif request.authenticated_role == 'competitive_dialogue':
role = 'competitive_dialogue'
else:
role = 'edit_{}'.format(request.context.status)
return role

def __acl__(self):
acl = [
(Allow, '{}_{}'.format(i.owner, i.owner_token), 'create_qualification_complaint')
for i in self.bids
if i.status in ['active', 'unsuccessful']
]
acl.extend(
[(Allow, '{}_{}'.format(i.owner, i.owner_token), 'create_award_complaint')
for i in self.bids
if i.status == 'active'
])
acl.extend([
(Allow, '{}_{}'.format(self.owner, self.owner_token), 'edit_tender'),
(Allow, '{}_{}'.format(self.owner, self.owner_token), 'upload_tender_documents'),
(Allow, '{}_{}'.format(self.owner, self.owner_token), 'edit_complaint'),
(Allow, 'g:competitive_dialogue', 'extract_credentials'),
(Allow, 'g:competitive_dialogue', 'edit_tender'),
])
return acl


CompetitiveDialogEU = Tender


class LotId(Model):
id = StringType()


class Firms(Model):
identifier = ModelType(Identifier, required=True)
name = StringType(required=True)
lots = ListType(ModelType(LotId), default=list())


@implementer(ITender)
class Tender(CompetitiveDialogEU):
procurementMethodType = StringType(default=CD_UA_TYPE)
title_en = StringType()
items = ListType(ModelType(BaseItem), required=True, min_size=1,
validators=[validate_cpv_group, validate_items_uniq])
procuringEntity = ModelType(BaseProcuringEntity, required=True)
stage2TenderID = StringType(required=False)


CompetitiveDialogUA = Tender


# stage 2 models

hide_dialogue_token = blacklist('dialogue_token')
close_edit_technical_fields = blacklist('dialogue_token', 'shortlistedFirms', 'dialogueID')


stage_2_roles = {
'plain': plain_role,
'create': (blacklist('owner_token', '_attachments', 'revisions', 'dateModified', 'doc_id', 'tenderID', 'bids', 'documents', 'awards', 'questions', 'complaints', 'auctionUrl', 'status', 'auctionPeriod', 'awardPeriod', 'awardCriteria', 'submissionMethod', 'cancellations') + schematics_embedded_role),
'edit': edit_role_eu + close_edit_technical_fields,
'edit_draft': edit_role_eu + close_edit_technical_fields,
'edit_active.tendering': edit_role_eu + close_edit_technical_fields,
'edit_active.pre-qualification': whitelist('status'),
'edit_active.pre-qualification.stand-still': whitelist(),
'edit_active.auction': whitelist(),
'edit_active.qualification': whitelist(),
'edit_active.awarded': whitelist(),
'edit_complete': whitelist(),
'edit_unsuccessful': whitelist(),
'edit_cancelled': whitelist(),
'view': view_role + hide_dialogue_token,
'listing': listing_role,
'auction_view': auction_view_role,
'auction_post': auction_post_role,
'auction_patch': auction_patch_role,
'draft': enquiries_role + hide_dialogue_token,
'active.tendering': enquiries_role + hide_dialogue_token,
'active.pre-qualification': pre_qualifications_role + hide_dialogue_token,
'active.pre-qualification.stand-still': pre_qualifications_role + hide_dialogue_token,
'active.auction': pre_qualifications_role + hide_dialogue_token,
'active.qualification': view_role + hide_dialogue_token,
'active.awarded': view_role + hide_dialogue_token,
'complete': view_role + hide_dialogue_token,
'unsuccessful': view_role + hide_dialogue_token,
'cancelled': view_role + hide_dialogue_token,
'chronograph': chronograph_role,
'chronograph_view': chronograph_view_role,
'Administrator': Administrator_role,
'default': schematics_default_role,
'contracting': whitelist('doc_id', 'owner'),
}


@implementer(ITender)
class Tender(TenderEU):
procurementMethodType = StringType(default=STAGE_2_EU_TYPE)
dialogue_token = StringType(required=True)
dialogueID = StringType()
shortlistedFirms = ListType(ModelType(Firms), required=True)

class Options:
roles = stage_2_roles.copy()

def __acl__(self):
acl = [
(Allow, '{}_{}'.format(self.owner, self.dialogue_token), 'generate_credentials')
]
acl.extend([
(Allow, '{}_{}'.format(i.owner, i.owner_token), 'create_qualification_complaint')
for i in self.bids
if i.status in ['active', 'unsuccessful']
])
acl.extend([
(Allow, '{}_{}'.format(i.owner, i.owner_token), 'create_award_complaint')
for i in self.bids
if i.status == 'active'
])
acl.extend([
(Allow, '{}_{}'.format(self.owner, self.owner_token), 'edit_tender'),
(Allow, '{}_{}'.format(self.owner, self.owner_token), 'upload_tender_documents'),
(Allow, '{}_{}'.format(self.owner, self.owner_token), 'edit_complaint')
])
return acl


TenderStage2EU = Tender


@implementer(ITender)
class Tender(TenderStage2EU):
procurementMethodType = StringType(default=STAGE_2_UA_TYPE)
title_en = StringType()

TenderStage2UA = Tender
7 changes: 5 additions & 2 deletions openprocurement/tender/competitivedialogue/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from openprocurement.tender.openua.utils import calculate_business_date
from openprocurement.tender.openua.models import TENDERING_EXTRA_PERIOD
from openprocurement.api.models import get_now
from openprocurement.api.utils import save_tender, apply_patch, opresource, json_view, context_unpack
from openprocurement.api.utils import save_tender, apply_patch, opresource, json_view, context_unpack, generate_id
from openprocurement.tender.openeu.utils import check_status, all_bids_are_reviewed
from openprocurement.tender.openeu.models import PREQUALIFICATION_COMPLAINT_STAND_STILL as COMPLAINT_STAND_STILL
from openprocurement.tender.openua.utils import BLOCK_COMPLAINT_STATUS, check_complaint_status, add_next_award
Expand Down Expand Up @@ -134,6 +134,9 @@ def check_status(request):
for i in q.complaints
]):
LOGGER.info('Switched tender {} to {}'.format(tender['id'], 'active.stage2.pending'),
extra=context_unpack(request, {'MESSAGE_ID': 'switched_tender_active.auction'}))
extra=context_unpack(request, {'MESSAGE_ID': 'switched_tender_active_stage2_pending'}))
tender.status = 'active.stage2.pending'
return

def set_ownership(item):
item.owner_token = generate_id()
66 changes: 63 additions & 3 deletions openprocurement/tender/competitivedialogue/views/tender.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@
from openprocurement.api.views.tender import TenderResource
from openprocurement.tender.openeu.views.tender import TenderEUResource
from openprocurement.tender.openua.validation import validate_patch_tender_ua_data
from openprocurement.tender.competitivedialogue.utils import patch_eu
from openprocurement.api.utils import opresource, json_view
from openprocurement.tender.competitivedialogue.models import CD_EU_TYPE, CD_UA_TYPE
from openprocurement.tender.competitivedialogue.utils import patch_eu, set_ownership
from openprocurement.api.utils import opresource, json_view, save_tender, context_unpack, APIResource
from openprocurement.tender.competitivedialogue.models import CD_EU_TYPE, CD_UA_TYPE, STAGE_2_EU_TYPE, STAGE_2_UA_TYPE


@opresource(name='Competitive Dialogue for EU procedure',
Expand All @@ -29,3 +29,63 @@ class CompetitiveDialogueUAResource(TenderResource):
@json_view(content_type="application/json", validators=(validate_patch_tender_ua_data,), permission='edit_tender')
def patch(self):
return patch_eu(self)


@opresource(name='Tender Stage 2 for UA procedure',
path='/tenders/{tender_id}',
procurementMethodType=STAGE_2_UA_TYPE,
description="")
class TenderStage2UAResource(TenderResource):
""" Resource handler for tender stage 2 UA"""

@json_view(content_type="application/json", validators=(validate_patch_tender_ua_data,), permission='edit_tender')
def patch(self):
return patch_eu(self)


@opresource(name='Tender Stage 2 for EU procedure',
path='/tenders/{tender_id}',
procurementMethodType=STAGE_2_EU_TYPE,
description="")
class TenderStage2UEResource(TenderResource):
""" Resource handler for tender stage 2 EU"""

@json_view(content_type="application/json", validators=(validate_patch_tender_ua_data,), permission='edit_tender')
def patch(self):
return patch_eu(self)


@opresource(name='Tender stage2 EU credentials',
path='/tenders/{tender_id}/credentials',
procurementMethodType=STAGE_2_EU_TYPE,
description="Tender stage2 UE credentials")
class TenderStage2EUCredentialsResource(APIResource):

@json_view(permission='generate_credentials')
def patch(self):
tender = self.request.validated['tender']
if tender.status != "draft":
self.request.errors.add('body', 'data',
'Can\'t generate credentials in current ({}) contract status'.format(
tender.status))
self.request.errors.status = 403
return

set_ownership(tender)
if save_tender(self.request):
self.LOGGER.info('Generate Tender stage2 credentials {}'.format(tender.id),
extra=context_unpack(self.request, {'MESSAGE_ID': 'tender_patch'}))
return {
'data': tender.serialize("view"),
'access': {
'token': tender.owner_token
}
}


@opresource(name='Tender stage2 UA credentials',
path='/tenders/{tender_id}/credentials',
procurementMethodType=STAGE_2_UA_TYPE,
description="Tender stage2 UA credentials")
class TenderStage2UACredentialsResource(TenderStage2EUCredentialsResource):
pass

0 comments on commit cd3ea06

Please sign in to comment.