class Encryption(Model): sha256 = StringType(deserialize_from='sha256') kmsKey_service_account = StringType( deserialize_from='kmsKeyServiceAccount') raw_key = StringType(deserialize_from='rawKey') kms_key_name = StringType(deserialize_from='kmsKeyName')
class ContractModification(Model): class Options: roles = RolesFromCsv("ContractModification.csv", relative_to=__file__) itemId = StringType() contractId = StringType(required=True)
class DayOfWeek(Model): day = StringType(choices=('MONDAY', 'TUESDAY', 'WEDNESDAY', 'THURSDAY', 'FRIDAY', 'SATURDAY', 'SUNDAY')) start_time = StringType(deserialize_from='startTime') duration = StringType()
class RetentionPolicy(Model): max_retention_days_display = StringType(serialize_when_none=False) max_retention_days = IntType(deserialize_from='maxRetentionDays', serialize_when_none=False) on_source_disk_delete = StringType(deserialize_from='onSourceDiskDelete', serialize_when_none=False)
class ScaleCapacity(Model): default = StringType(serialize_when_none=False) maximum = StringType(serialize_when_none=False) minimum = StringType(serialize_when_none=False)
class Projects(Model): postcode = StringType(max_length=6, required=True) desc = StringType()
class Tender(BaseTender): """Data regarding tender process - publicly inviting prospective contractors to submit bids for evaluation and selecting a winner or winners. """ class Options: namespace = "Tender" _core_roles = BaseTender.Options.roles # without _serializable_fields they won't be calculated (even though serialized_name is in the role) _serializable_fields = whitelist("tender_guarantee", "tender_value", "tender_minimalStep") _edit_fields = _serializable_fields + whitelist( "enquiryPeriod", "next_check", "numberOfBidders", "hasEnquiries", "features", "items", "tenderPeriod", "procuringEntity", "guarantee", "value", "minimalStep", ) _edit_role = _core_roles["edit"] + _edit_fields + whitelist( "contracts", "numberOfBids") _view_tendering_role = (_core_roles["view"] + _edit_fields + whitelist( "auctionUrl", "awards", "awardPeriod", "questions", "lots", "cancellations", "complaints", "contracts", "auctionPeriod", )) _view_role = _view_tendering_role + whitelist("bids", "numberOfBids") _all_forbidden = whitelist() roles = { "create": _core_roles["create"] + _edit_role + whitelist("lots"), "edit": _edit_role, "edit_draft": _core_roles["edit_draft"], "edit_active.enquiries": _edit_role, "edit_active.tendering": _all_forbidden, "edit_active.auction": _all_forbidden, "edit_active.qualification": _all_forbidden, "edit_active.awarded": _all_forbidden, "edit_complete": _all_forbidden, "edit_unsuccessful": _all_forbidden, "edit_cancelled": _all_forbidden, "draft": _view_tendering_role, "active.enquiries": _view_tendering_role, "active.tendering": _view_tendering_role, "active.auction": _view_tendering_role, "view": _view_role, "active.qualification": _view_role, "active.awarded": _view_role, "complete": _view_role, "unsuccessful": _view_role, "cancelled": _view_role, "auction_view": _core_roles["auction_view"], "auction_post": _core_roles["auction_post"], "auction_patch": _core_roles["auction_patch"], "chronograph": _core_roles["chronograph"], "chronograph_view": _core_roles["chronograph_view"], "Administrator": _core_roles["Administrator"], "plain": _core_roles["plain"], "listing": _core_roles["listing"], "contracting": _core_roles["contracting"], "default": _core_roles["default"], } items = ListType( ModelType(Item, required=True), required=True, min_size=1, validators=[validate_items_uniq, validate_classification_id], ) # The goods and services to be purchased, broken into line items wherever possible. Items should not be duplicated, but a quantity of 2 specified instead. value = ModelType( Value, required=True) # The total estimated value of the procurement. enquiryPeriod = ModelType( PeriodEndRequired, required=True ) # The period during which enquiries may be made and will be answered. tenderPeriod = ModelType( PeriodEndRequired, required=True ) # The period when the tender is open for submissions. The end date is the closing date for tender submissions. hasEnquiries = BooleanType( ) # A Yes/No field as to whether enquiries were part of tender process. awardPeriod = ModelType( Period ) # The date or period on which an award is anticipated to be made. numberOfBidders = IntType( ) # The number of unique tenderers who participated in the tender bids = ListType(ModelType(Bid, required=True), default=list( )) # A list of all the companies who entered submissions for the tender. procuringEntity = ModelType( ProcuringEntity, required=True ) # The entity managing the procurement, which may be different from the buyer who is paying / using the items being procured. awards = ListType(ModelType(Award, required=True), default=list()) contracts = ListType(ModelType(Contract, required=True), default=list()) auctionPeriod = ModelType(TenderAuctionPeriod, default={}) minimalStep = ModelType(Value, required=True) questions = ListType(ModelType(Question, required=True), default=list()) complaints = ListType(ComplaintModelType(Complaint, required=True), default=list()) auctionUrl = URLType() cancellations = ListType(ModelType(Cancellation, required=True), default=list()) features = ListType(ModelType(Feature, required=True), validators=[validate_features_uniq]) lots = ListType(ModelType(Lot, required=True), default=list(), validators=[validate_lots_uniq]) guarantee = ModelType(Guarantee) procurementMethodType = StringType(default="belowThreshold") procuring_entity_kinds = [ "general", "special", "defense", "central", "other" ] block_complaint_status = ["answered", "pending"] def __local_roles__(self): roles = dict([("{}_{}".format(self.owner, self.owner_token), "tender_owner")]) for i in self.bids: roles["{}_{}".format(i.owner, i.owner_token)] = "bid_owner" return roles def __acl__(self): acl = super(Tender, self).__acl__() acl.extend([(Allow, "g:brokers", "create_cancellation_complaint")]) return acl @serializable(serialize_when_none=False) def next_check(self): now = get_now() checks = [] if self.status == "active.enquiries" and self.tenderPeriod.startDate: checks.append(self.tenderPeriod.startDate.astimezone(TZ)) elif self.status == "active.enquiries" and self.enquiryPeriod.endDate: checks.append(self.enquiryPeriod.endDate.astimezone(TZ)) elif self.status == "active.tendering" and self.tenderPeriod.endDate: checks.append(self.tenderPeriod.endDate.astimezone(TZ)) elif (not self.lots and self.status == "active.auction" and self.auctionPeriod and self.auctionPeriod.startDate and not self.auctionPeriod.endDate): if now < self.auctionPeriod.startDate: checks.append(self.auctionPeriod.startDate.astimezone(TZ)) elif now < calc_auction_end_time( self.numberOfBids, self.auctionPeriod.startDate).astimezone(TZ): checks.append( calc_auction_end_time( self.numberOfBids, self.auctionPeriod.startDate).astimezone(TZ)) elif self.lots and self.status == "active.auction": for lot in self.lots: if (lot.status != "active" or not lot.auctionPeriod or not lot.auctionPeriod.startDate or lot.auctionPeriod.endDate): continue if now < lot.auctionPeriod.startDate: checks.append(lot.auctionPeriod.startDate.astimezone(TZ)) elif now < calc_auction_end_time( lot.numberOfBids, lot.auctionPeriod.startDate).astimezone(TZ): checks.append( calc_auction_end_time( lot.numberOfBids, lot.auctionPeriod.startDate).astimezone(TZ)) elif (not self.lots and self.status == "active.awarded" and not any( [i.status in self.block_complaint_status for i in self.complaints]) and not any([ i.status in self.block_complaint_status for a in self.awards for i in a.complaints ])): standStillEnds = [ a.complaintPeriod.endDate.astimezone(TZ) for a in self.awards if a.complaintPeriod.endDate ] last_award_status = self.awards[-1].status if self.awards else "" if standStillEnds and last_award_status == "unsuccessful": checks.append(max(standStillEnds)) elif (self.lots and self.status in ["active.qualification", "active.awarded"] and not any([ i.status in self.block_complaint_status and i.relatedLot is None for i in self.complaints ])): for lot in self.lots: if lot["status"] != "active": continue lot_awards = [i for i in self.awards if i.lotID == lot.id] pending_complaints = any([ i["status"] in self.block_complaint_status and i.relatedLot == lot.id for i in self.complaints ]) pending_awards_complaints = any([ i.status in self.block_complaint_status for a in lot_awards for i in a.complaints ]) standStillEnds = [ a.complaintPeriod.endDate.astimezone(TZ) for a in lot_awards if a.complaintPeriod.endDate ] last_award_status = lot_awards[-1].status if lot_awards else "" if (not pending_complaints and not pending_awards_complaints and standStillEnds and last_award_status == "unsuccessful"): checks.append(max(standStillEnds)) if self.status.startswith("active"): from openprocurement.tender.core.utils import calculate_tender_business_date for complaint in self.complaints: if complaint.status == "answered" and complaint.dateAnswered: checks.append( calculate_tender_business_date( complaint.dateAnswered, COMPLAINT_STAND_STILL_TIME, self)) elif complaint.status == "pending": checks.append(self.dateModified) for award in self.awards: if award.status == "active" and not any( [i.awardID == award.id for i in self.contracts]): checks.append(award.date) for complaint in award.complaints: if complaint.status == "answered" and complaint.dateAnswered: checks.append( calculate_tender_business_date( complaint.dateAnswered, COMPLAINT_STAND_STILL_TIME, self)) elif complaint.status == "pending": checks.append(self.dateModified) return min(checks).isoformat() if checks else None @serializable def numberOfBids(self): """A property that is serialized by schematics exports.""" return len(self.bids) @serializable(serialized_name="value", type=ModelType(Value)) def tender_value(self): return (Value( dict( amount=sum([i.value.amount for i in self.lots]), currency=self.value.currency, valueAddedTaxIncluded=self.value.valueAddedTaxIncluded, )) if self.lots else self.value) @serializable(serialized_name="guarantee", serialize_when_none=False, type=ModelType(Guarantee)) def tender_guarantee(self): if self.lots: lots_amount = [ i.guarantee.amount for i in self.lots if i.guarantee ] if not lots_amount: return self.guarantee guarantee = {"amount": sum(lots_amount)} lots_currency = [ i.guarantee.currency for i in self.lots if i.guarantee ] guarantee["currency"] = lots_currency[0] if lots_currency else None if self.guarantee: guarantee["currency"] = self.guarantee.currency return Guarantee(guarantee) else: return self.guarantee @serializable(serialized_name="minimalStep", type=ModelType(Value)) def tender_minimalStep(self): return (Value( dict( amount=min([i.minimalStep.amount for i in self.lots]), currency=self.minimalStep.currency, valueAddedTaxIncluded=self.minimalStep.valueAddedTaxIncluded, )) if self.lots else self.minimalStep) def validate_items(self, data, items): cpv_336_group = items[ 0].classification.id[:3] == "336" if items else False if (not cpv_336_group and (data.get("revisions")[0].date if data.get("revisions") else get_now()) > CPV_ITEMS_CLASS_FROM and items and len(set([i.classification.id[:4] for i in items])) != 1): raise ValidationError(u"CPV class of items should be identical") else: validate_cpv_group(items) def validate_features(self, data, features): if (features and data["lots"] and any([ round( vnmax([ i for i in features if i.featureOf == "tenderer" or i.featureOf == "lot" and i.relatedItem == lot["id"] or i.featureOf == "item" and i.relatedItem in [ j.id for j in data["items"] if j.relatedLot == lot["id"] ] ]), 15, ) > 0.3 for lot in data["lots"] ])): raise ValidationError( u"Sum of max value of all features for lot should be less then or equal to 30%" ) elif features and not data["lots"] and round(vnmax(features), 15) > 0.3: raise ValidationError( u"Sum of max value of all features should be less then or equal to 30%" ) def validate_auctionUrl(self, data, url): if url and data["lots"]: raise ValidationError(u"url should be posted for each lot") def validate_minimalStep(self, data, value): validate_minimalstep(data, value) def validate_enquiryPeriod(self, data, period): active_validation = get_first_revision_date( data, default=get_now()) > RELEASE_2020_04_19 if (active_validation and period and period.startDate and period.endDate and period.endDate < calculate_tender_business_date( period.startDate, timedelta(days=3), data, True)): raise ValidationError( u"the enquiryPeriod cannot end earlier than 3 business days after the start" ) def validate_tenderPeriod(self, data, period): if (period and period.startDate and data.get("enquiryPeriod") and data.get("enquiryPeriod").endDate and period.startDate < data.get("enquiryPeriod").endDate): raise ValidationError(u"period should begin after enquiryPeriod") active_validation = get_first_revision_date( data, default=get_now()) > RELEASE_2020_04_19 if (active_validation and period and period.startDate and period.endDate and period.endDate < calculate_tender_business_date( period.startDate, timedelta(days=2), data, True)): raise ValidationError( u"the tenderPeriod cannot end earlier than 2 business days after the start" ) def validate_awardPeriod(self, data, period): if (period and period.startDate and data.get("auctionPeriod") and data.get("auctionPeriod").endDate and period.startDate < data.get("auctionPeriod").endDate): raise ValidationError(u"period should begin after auctionPeriod") if (period and period.startDate and data.get("tenderPeriod") and data.get("tenderPeriod").endDate and period.startDate < data.get("tenderPeriod").endDate): raise ValidationError(u"period should begin after tenderPeriod") def validate_lots(self, data, value): if len(set([lot.guarantee.currency for lot in value if lot.guarantee])) > 1: raise ValidationError( u"lot guarantee currency should be identical to tender guarantee currency" )
def test_string_type_accepts_none(self): field = StringType() field.validate(None)
class ScaleAction(Model): direction = StringType(choices=('Decrease', 'Increase', 'None'), serialize_when_none=False) type = StringType(choices=('ChangeCount', 'ExactCount', 'PercentChangeCount'), serialize_when_none=False) value = StringType(serialize_when_none=False) cooldown = TimedeltaType(serialize_when_none=False)
class AutoscaleProfile(Model): capacity = ModelType(ScaleCapacity, serialize_when_none=False) fixed_date = ModelType(TimeWindow, serialize_when_none=False) name = StringType(serialize_when_none=False) recurrence = ModelType(Recurrence, serialize_when_none=False) rules = ListType(ModelType(ScaleRule), serialize_when_none=False)
class Recurrence(Model): frequency = StringType(choices= ('Day', 'Hour', 'Minute', 'Month', 'None', 'Second', 'Week', 'Year'), serialize_when_none=False) schedule = ModelType(RecurrentSchedule)
class RecurrentSchedule(Model): days = ListType(StringType, serialize_when_none=False) hours = ListType(IntType, serialize_when_none=False) minutes = ListType(IntType, serialize_when_none=False) time_zone = StringType(serialize_when_none=False)
class TimeWindow(Model): end = StringType(serialize_when_none=False) start = StringType(serialize_when_none=False) time_zone = StringType(serialize_when_none=False)
def test_string_type_required(): field = StringType(required=True) with pytest.raises(ValidationError): field.validate(None)
class RequestGithubToken(Model): authorizationCode = StringType(required=True)
def test_string_required_doesnt_accept_empty_string(self): field = StringType(required=True) with self.assertRaises(ValidationError): field.validate('')
class RequestGithubAuthorization(Model): username = StringType(required=True) password = StringType(required=True) otp_code = StringType()
def test_string_min_length_doesnt_accept_empty_string(self): field = StringType(min_length=1) with self.assertRaises(ValidationError): field.validate('')
class AutoScalingResource(CloudServiceResource): cloud_service_group = StringType(default='EC2')
class RequestData(Model): text = StringType(required=True)
class AutoScalingGroupResource(AutoScalingResource): cloud_service_type = StringType(default='AutoScalingGroup') data = ModelType(AutoScalingGroup) _metadata = ModelType(CloudServiceMeta, default=asg_meta, serialized_name='metadata')
class DailySchedule(Model): days_in_cycle = IntType(deserialize_from='daysInCycle') start_time = StringType(deserialize_from='startTime') duration = StringType()
class LaunchConfigurationResource(AutoScalingResource): cloud_service_type = StringType(default='LaunchConfiguration') data = ModelType(LaunchConfiguration) _metadata = ModelType(CloudServiceMeta, default=lc_meta, serialized_name='metadata')
class Labels(Model): key = StringType() value = StringType()
class LaunchTemplateResource(AutoScalingResource): cloud_service_type = StringType(default='LaunchTemplate') data = ModelType(LaunchTemplateDetail) _metadata = ModelType(CloudServiceMeta, default=lt_meta, serialized_name='metadata')
class Disk(Model): source_disk = StringType() source_disk_display = StringType() source_disk_id = StringType() disk_size = FloatType() storage_bytes = IntType()
class Bid(BaseBid): class Options: roles = { "Administrator": Administrator_bid_role, "embedded": view_bid_role, "view": view_bid_role, "create": whitelist( "value", "tenderers", "parameters", "lotValues", "status", "selfQualified", "selfEligible", "subcontractingDetails", "documents", ), "edit": whitelist("value", "tenderers", "parameters", "lotValues", "status", "subcontractingDetails"), "auction_view": whitelist("value", "lotValues", "id", "date", "parameters", "participationUrl", "status"), "auction_post": whitelist("value", "lotValues", "id", "date"), "auction_patch": whitelist("id", "lotValues", "participationUrl"), "active.enquiries": whitelist(), "active.tendering": whitelist(), "active.auction": whitelist(), "active.qualification": view_bid_role, "active.awarded": view_bid_role, "complete": view_bid_role, "unsuccessful": view_bid_role, "cancelled": view_bid_role, "invalid": whitelist("id", "status"), "deleted": whitelist("id", "status"), } lotValues = ListType(ModelType(LotValue, required=True), default=list()) subcontractingDetails = StringType() status = StringType(choices=["draft", "active", "invalid", "deleted"], default="active") selfQualified = BooleanType(required=True, choices=[True]) selfEligible = BooleanType(required=True, choices=[True]) parameters = ListType(ModelType(Parameter, required=True), default=list(), validators=[validate_parameters_uniq]) documents = ListType(ConfidentialDocumentModelType(ConfidentialDocument, required=True), default=list()) def serialize(self, role=None): if role and self.status in ["invalid", "deleted"]: role = self.status return super(Bid, self).serialize(role) @bids_validation_wrapper def validate_value(self, data, value): BaseBid._validator_functions["value"](self, data, value) @bids_validation_wrapper def validate_lotValues(self, data, lotValues): BaseBid._validator_functions["lotValues"](self, data, lotValues) @bids_validation_wrapper def validate_participationUrl(self, data, participationUrl): BaseBid._validator_functions["participationUrl"](self, data, participationUrl) @bids_validation_wrapper def validate_parameters(self, data, parameters): BaseBid._validator_functions["parameters"](self, data, parameters)
class HourlySchedule(Model): hours_in_cycle = IntType(deserialize_from='hoursInCycle') start_time = StringType(deserialize_from='startTime') duration = StringType()
class ComplaintPost(Model): class Options: namespace = "post" roles = { "create": whitelist("title", "description", "documents", "relatedParty", "relatedPost", "recipient"), "edit": whitelist(), "view": schematics_default_role, "default": schematics_default_role, "embedded": schematics_embedded_role, } id = MD5Type(required=True, default=lambda: uuid4().hex) title = StringType(required=True) description = StringType(required=True) documents = ListType(ModelType(Document), default=list()) author = StringType( choices=["complaint_owner", "tender_owner", "aboveThresholdReviewers"]) recipient = StringType( required=True, choices=["complaint_owner", "tender_owner", "aboveThresholdReviewers"]) datePublished = IsoDateTimeType(default=get_now) relatedPost = StringType() reviewer_roles = ["aboveThresholdReviewers"] recipient_roles = ["complaint_owner", "tender_owner"] def validate_relatedPost(self, data, value): parent = data["__parent__"] if data["author"] in self.recipient_roles and not value: raise ValidationError(BaseType.MESSAGES["required"]) if value and isinstance(parent, Model): # check that another post with "id" # that equals "relatedPost" of current post exists if value not in [i.id for i in parent.posts]: raise ValidationError("relatedPost should be one of posts.") # check that another posts with `relatedPost` # that equals `relatedPost` of current post does not exist if len([i for i in parent.posts if i.relatedPost == value]) > 1: raise ValidationError("relatedPost must be unique.") related_posts = [i for i in parent.posts if i.id == value] # check that there are no multiple related posts, # that should never happen coz `id` is unique if len(related_posts) > 1: raise ValidationError( "relatedPost can't be a link to more than one post.") # check that related post have another author if len(related_posts ) == 1 and data["author"] == related_posts[0]["author"]: raise ValidationError( "relatedPost can't have the same author.") # check that related post is not an answer to another post if len(related_posts) == 1 and related_posts[0]["relatedPost"]: raise ValidationError( "relatedPost can't have relatedPost defined.") # check that answer author matches related post recipient if data["author"] and data["author"] != related_posts[0][ "recipient"]: raise ValidationError("relatedPost invalid recipient.") def validate_recipient(self, data, value): # validate for aboveThresholdReviewers role if data["author"] in self.reviewer_roles and value not in self.recipient_roles: raise ValidationError("Value must be one of {}.".format( self.recipient_roles)) # validate for complaint_owner and tender_owner roles elif data[ "author"] in self.recipient_roles and value not in self.reviewer_roles: raise ValidationError("Value must be one of {}.".format( self.reviewer_roles))
def test_string_type_doesnt_accept_none(self): field = StringType() with self.assertRaises(ValidationError): field.validate(None)
class Complaint(BaseComplaint): class Options: _base_roles = BaseComplaint.Options.roles roles = { "create": _base_roles["create"], # TODO inherit the rest of the roles "draft": whitelist("author", "title", "description", "status"), "bot": whitelist("rejectReason", "status"), "cancellation": whitelist("cancellationReason", "status"), "satisfy": whitelist("satisfied", "status"), "escalate": whitelist("status"), "resolve": whitelist("status", "tendererAction"), "answer": whitelist("resolution", "resolutionType", "status", "tendererAction"), "action": whitelist("tendererAction"), "review": whitelist("decision", "status", "rejectReason", "rejectReasonDescription", "reviewDate", "reviewPlace"), "embedded": (blacklist("owner_token", "owner", "transfer_token", "bid_id") + schematics_embedded_role), "view": (blacklist("owner_token", "owner", "transfer_token", "bid_id") + schematics_default_role), } status = StringType( choices=[ "draft", "claim", "answered", "pending", "accepted", "invalid", "resolved", "declined", "cancelled", "satisfied", "stopping", "stopped", "mistaken", ], default="draft", ) acceptance = BooleanType() dateAccepted = IsoDateTimeType() rejectReasonDescription = StringType() reviewDate = IsoDateTimeType() reviewPlace = StringType() bid_id = StringType() posts = ListType(ModelType(ComplaintPost), default=list()) def __acl__(self): return [ (Allow, "g:bots", "edit_complaint"), (Allow, "g:aboveThresholdReviewers", "edit_complaint"), (Allow, "{}_{}".format(self.owner, self.owner_token), "edit_complaint"), (Allow, "{}_{}".format(self.owner, self.owner_token), "upload_complaint_documents"), ] def get_role(self): root = self.get_root() request = root.request data = request.json_body["data"] auth_role = request.authenticated_role status = data.get("status", self.status) if auth_role == "Administrator": role = auth_role elif auth_role == "complaint_owner" and self.status != "mistaken" and status == "cancelled": role = "cancellation" elif auth_role == "complaint_owner" and self.status in [ "pending", "accepted" ] and status == "stopping": role = "cancellation" elif auth_role == "complaint_owner" and self.status == "draft": role = "draft" elif auth_role == "complaint_owner" and self.status == "claim": role = "escalate" elif auth_role == "bots" and self.status == "draft": role = "bot" elif auth_role == "tender_owner" and self.status == "claim": role = "answer" elif auth_role == "tender_owner" and self.status in [ "pending", "accepted" ]: role = "action" elif auth_role == "tender_owner" and self.status == "satisfied": role = "resolve" elif auth_role == "complaint_owner" and self.status == "answered": role = "satisfy" elif auth_role == "aboveThresholdReviewers" and self.status in [ "pending", "accepted", "stopping" ]: role = "review" else: role = "invalid" return role def validate_cancellationReason(self, data, cancellationReason): if not cancellationReason and data.get("status") in [ "cancelled", "stopping" ]: raise ValidationError(u"This field is required.") def validate_rejectReason(self, data, rejectReason): tender_date = get_first_revision_date(get_tender(data["__parent__"]), default=get_now()) if tender_date < RELEASE_2020_04_19: return if not rejectReason and data.get("status") in [ "invalid", "stopped" ] and data.get("type") == "complaint": raise ValidationError(u"This field is required.") def validate_reviewDate(self, data, reviewDate): tender_date = get_first_revision_date(get_tender(data["__parent__"]), default=get_now()) if tender_date < RELEASE_2020_04_19: return if not reviewDate and data.get("status") == "accepted": raise ValidationError(u"This field is required.") def validate_reviewPlace(self, data, reviewPlace): tender_date = get_first_revision_date(get_tender(data["__parent__"]), default=get_now()) if tender_date < RELEASE_2020_04_19: return if not reviewPlace and data.get("status") == "accepted": raise ValidationError(u"This field is required.")
def test_string_min_length_doesnt_accept_empty_string(): field = StringType(min_length=1) with pytest.raises(ValidationError): field.validate('')
class CancellationComplaint(Complaint): class Options: _base_roles = Complaint.Options.roles namespace = "Complaint" roles = { "create": whitelist("author", "title", "description", "relatedLot"), "draft": _base_roles["draft"], "bot": _base_roles["bot"], "cancellation": _base_roles["cancellation"], "satisfy": _base_roles["satisfy"], "resolve": _base_roles["resolve"], "action": _base_roles["action"], # "pending": whitelist("decision", "status", "rejectReason", "rejectReasonDescription"), "review": _base_roles["review"], "embedded": _base_roles["embedded"], "view": _base_roles["view"], } def get_role(self): root = self.get_root() request = root.request data = request.json_body["data"] auth_role = request.authenticated_role status = data.get("status", self.status) if auth_role == "Administrator": role = auth_role elif auth_role == "complaint_owner" and self.status != "mistaken" and status == "cancelled": role = "cancellation" elif auth_role == "complaint_owner" and self.status in [ "pending", "accepted" ] and status == "stopping": role = "cancellation" elif auth_role == "complaint_owner" and self.status == "draft": role = "draft" elif auth_role == "bots" and self.status == "draft": role = "bot" elif auth_role == "tender_owner" and self.status == "pending": role = "action" elif auth_role == "tender_owner" and self.status == "satisfied": role = "resolve" elif auth_role == "aboveThresholdReviewers" and self.status in [ "pending", "accepted", "stopping" ]: role = "review" else: role = "invalid" return role def __acl__(self): return [ (Allow, "g:bots", "edit_complaint"), (Allow, "g:aboveThresholdReviewers", "edit_complaint"), (Allow, "{}_{}".format(self.owner, self.owner_token), "edit_complaint"), (Allow, "{}_{}".format(self.owner, self.owner_token), "upload_complaint_documents"), ] status = StringType( choices=[ "draft", "pending", "accepted", "invalid", "resolved", "declined", "satisfied", "stopped", "mistaken", ], default="draft", ) type = StringType( choices=["claim", "complaint"], default="complaint", )
def test_string_type_required(self): field = StringType(required=True) with self.assertRaises(ValidationError): field.validate(None)
class Tender(BaseTender): """ Data regarding tender process - publicly inviting prospective contractors to submit bids for evaluation and selecting a winner or winners. """ class Options: namespace = "Tender" _parent_roles = BaseTender.Options.roles _edit_role = _parent_roles["edit"] _above_fields = whitelist("complaintPeriod") _tendering_role = _parent_roles["active.tendering"] + _above_fields _view_role = _parent_roles["view"] + _above_fields _all_forbidden = whitelist() roles = { "create": _parent_roles["create"], "edit_draft": _parent_roles["edit_draft"], "edit": _edit_role, "edit_active.tendering": _edit_role, "edit_active.auction": _all_forbidden, "edit_active.qualification": _all_forbidden, "edit_active.awarded": _all_forbidden, "edit_complete": _all_forbidden, "edit_unsuccessful": _all_forbidden, "edit_cancelled": _all_forbidden, "draft": _tendering_role, "active.tendering": _tendering_role, "active.auction": _tendering_role, "view": _view_role, "active.qualification": _view_role, "active.awarded": _view_role, "complete": _view_role, "unsuccessful": _view_role, "cancelled": _view_role, "chronograph": _parent_roles["chronograph"], "chronograph_view": _parent_roles["chronograph_view"], "Administrator": _parent_roles["Administrator"], "default": _parent_roles["default"], "plain": _parent_roles["plain"], "listing": _parent_roles["listing"], "auction_view": _parent_roles["auction_view"], "auction_post": _parent_roles["auction_post"], "auction_patch": _parent_roles["auction_patch"], "contracting": _parent_roles["contracting"], } __name__ = "" enquiryPeriod = ModelType(EnquiryPeriod, required=False) tenderPeriod = ModelType(PeriodStartEndRequired, required=True) auctionPeriod = ModelType(TenderAuctionPeriod, default={}) bids = SifterListType( ModelType(Bid, required=True), default=list(), filter_by="status", filter_in_values=["invalid", "deleted"] ) # A list of all the companies who entered submissions for the tender. awards = ListType(ModelType(Award, required=True), default=list()) contracts = ListType(ModelType(Contract, required=True), default=list()) complaints = ListType(ComplaintModelType(Complaint, required=True), default=list()) procurementMethodType = StringType(default="aboveThresholdUA") lots = ListType(ModelType(Lot, required=True), default=list(), validators=[validate_lots_uniq]) status = StringType( choices=[ "draft", "active.tendering", "active.auction", "active.qualification", "active.awarded", "complete", "cancelled", "unsuccessful", ], default="active.tendering", ) items = ListType( ModelType(Item, required=True), required=True, min_size=1, validators=[ validate_cpv_group, validate_items_uniq, validate_classification_id ], ) # The goods and services to be purchased, broken into line items wherever possible. Items should not be duplicated, but a quantity of 2 specified instead. cancellations = ListType(ModelType(Cancellation, required=True), default=list()) create_accreditations = (ACCR_3, ACCR_5) central_accreditations = (ACCR_5, ) edit_accreditations = (ACCR_4, ) procuring_entity_kinds = [ "authority", "central", "defense", "general", "social", "special" ] block_tender_complaint_status = [ "claim", "pending", "accepted", "satisfied", "stopping" ] block_complaint_status = ["pending", "accepted", "satisfied", "stopping"] def __acl__(self): acl = [(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_complaint"), (Allow, "{}_{}".format(self.owner, self.owner_token), "edit_contract"), (Allow, "{}_{}".format(self.owner, self.owner_token), "upload_contract_documents"), ]) self._acl_cancellation_complaint(acl) return acl def validate_enquiryPeriod(self, data, period): # for deactivate validation to enquiryPeriod from parent tender return def validate_tenderPeriod(self, data, period): # data['_rev'] is None when tender was created just now if not data["_rev"] and calculate_tender_business_date( get_now(), -timedelta(minutes=10)) >= period.startDate: raise ValidationError( u"tenderPeriod.startDate should be in greater than current date" ) if period and calculate_tender_business_date( period.startDate, TENDER_PERIOD, data) > period.endDate: raise ValidationError( u"tenderPeriod should be greater than 15 days") @serializable(serialized_name="enquiryPeriod", type=ModelType(EnquiryPeriod)) def tender_enquiryPeriod(self): endDate = calculate_tender_business_date(self.tenderPeriod.endDate, -ENQUIRY_PERIOD_TIME, self) clarificationsUntil = calculate_clarifications_business_date( endDate, ENQUIRY_STAND_STILL_TIME, self, True) return EnquiryPeriod( dict( startDate=self.tenderPeriod.startDate, endDate=endDate, invalidationDate=self.enquiryPeriod and self.enquiryPeriod.invalidationDate, clarificationsUntil=clarificationsUntil, )) @serializable(type=ModelType(Period)) def complaintPeriod(self): endDate = calculate_complaint_business_date(self.tenderPeriod.endDate, -COMPLAINT_SUBMIT_TIME, self) return Period( dict(startDate=self.tenderPeriod.startDate, endDate=endDate)) @serializable def numberOfBids(self): """A property that is serialized by schematics exports.""" return len([bid for bid in self.bids if bid.status == "active"]) @serializable(serialize_when_none=False) def next_check(self): now = get_now() checks = [] if (self.status == "active.tendering" and self.tenderPeriod.endDate and not has_unanswered_complaints(self) and not has_unanswered_questions(self)): checks.append(self.tenderPeriod.endDate.astimezone(TZ)) elif (not self.lots and self.status == "active.auction" and self.auctionPeriod and self.auctionPeriod.startDate and not self.auctionPeriod.endDate): if now < self.auctionPeriod.startDate: checks.append(self.auctionPeriod.startDate.astimezone(TZ)) elif now < calc_auction_end_time( self.numberOfBids, self.auctionPeriod.startDate).astimezone(TZ): checks.append( calc_auction_end_time( self.numberOfBids, self.auctionPeriod.startDate).astimezone(TZ)) elif self.lots and self.status == "active.auction": for lot in self.lots: if (lot.status != "active" or not lot.auctionPeriod or not lot.auctionPeriod.startDate or lot.auctionPeriod.endDate): continue if now < lot.auctionPeriod.startDate: checks.append(lot.auctionPeriod.startDate.astimezone(TZ)) elif now < calc_auction_end_time( lot.numberOfBids, lot.auctionPeriod.startDate).astimezone(TZ): checks.append( calc_auction_end_time( lot.numberOfBids, lot.auctionPeriod.startDate).astimezone(TZ)) elif (not self.lots and self.status == "active.awarded" and not any( [i.status in self.block_complaint_status for i in self.complaints]) and not any([ i.status in self.block_complaint_status for a in self.awards for i in a.complaints ])): standStillEnds = [ a.complaintPeriod.endDate.astimezone(TZ) for a in self.awards if a.complaintPeriod and a.complaintPeriod.endDate ] last_award_status = self.awards[-1].status if self.awards else "" if standStillEnds and last_award_status == "unsuccessful": checks.append(max(standStillEnds)) elif (self.lots and self.status in ["active.qualification", "active.awarded"] and not any([ i.status in self.block_complaint_status and i.relatedLot is None for i in self.complaints ])): for lot in self.lots: if lot["status"] != "active": continue lot_awards = [i for i in self.awards if i.lotID == lot.id] pending_complaints = any([ i["status"] in self.block_complaint_status and i.relatedLot == lot.id for i in self.complaints ]) pending_awards_complaints = any([ i.status in self.block_complaint_status for a in lot_awards for i in a.complaints ]) standStillEnds = [ a.complaintPeriod.endDate.astimezone(TZ) for a in lot_awards if a.complaintPeriod and a.complaintPeriod.endDate ] last_award_status = lot_awards[-1].status if lot_awards else "" if (not pending_complaints and not pending_awards_complaints and standStillEnds and last_award_status == "unsuccessful"): checks.append(max(standStillEnds)) if self.status.startswith("active"): for award in self.awards: if award.status == "active" and not any( [i.awardID == award.id for i in self.contracts]): checks.append(award.date) extend_next_check_by_complaint_period_ends(self, checks) return min(checks).isoformat() if checks else None def invalidate_bids_data(self): if (self.auctionPeriod and self.auctionPeriod.startDate and self.auctionPeriod.shouldStartAfter and self.auctionPeriod.startDate > calculate_tender_business_date( parse_date(self.auctionPeriod.shouldStartAfter), AUCTION_PERIOD_TIME, self, True)): self.auctionPeriod.startDate = None for lot in self.lots: if (lot.auctionPeriod and lot.auctionPeriod.startDate and lot.auctionPeriod.shouldStartAfter and lot.auctionPeriod.startDate > calculate_tender_business_date( parse_date(lot.auctionPeriod.shouldStartAfter), AUCTION_PERIOD_TIME, self, True)): lot.auctionPeriod.startDate = None self.enquiryPeriod.invalidationDate = get_now() for bid in self.bids: if bid.status not in ["deleted", "draft"]: bid.status = "invalid"
def test_string_required_accepts_empty_string(self): field = StringType(required=True) field.validate('')
class VNet(Model): vnet_id = StringType() vnet_name = StringType() cidr = StringType()
def validate_string(): st = StringType(max_length=10) print(st.to_native('this is longer than 10')) st.validate('this is longer than 10')
class VirtualMachineAgentInstanceView(Model): statuses = ListType(ModelType(InstanceViewStatus), serialize_when_none=False) vm_agent_version = StringType(serialize_when_none=False) display_status = StringType(serialize_when_none=False) # #