class Period(Model): startDate = IsoDateTimeType() # The state date for the period. endDate = IsoDateTimeType() # The end date for the period. def validate_startDate(self, data, value): if value and data.get('endDate') and data.get('endDate') < value: raise ValidationError(u"period should begin before its end")
class Post(Model): class Options: roles = { 'create': whitelist('title', 'description', 'documents', 'relatedParty', 'relatedPost'), '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() postOf = StringType(choices=DIALOGUE_TYPE_CHOICES, default=DECISION_OBJECT_TYPE) datePublished = IsoDateTimeType(default=get_now) dateOverdue = IsoDateTimeType() relatedPost = StringType() relatedParty = StringType() def validate_relatedParty(self, data, value): parent = data['__parent__'] if value and isinstance(parent, Model) and value not in [i.id for i in parent.parties]: raise ValidationError(u"relatedParty should be one of parties.") def validate_relatedPost(self, data, value): parent = data['__parent__'] 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(u"relatedPost should be one of posts of current monitoring.") # 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(u"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(u"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(u"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(u"relatedPost can't be have relatedPost defined.")
class Liability(Model): class Options: roles = { 'create': whitelist('reportNumber', 'documents', 'legislation'), 'edit': whitelist('proceeding'), 'view': schematics_default_role, } id = MD5Type(required=True, default=lambda: uuid4().hex) reportNumber = StringType(required=True, min_length=1) datePublished = IsoDateTimeType(default=get_now) documents = ListType(ModelType(Document), default=[]) proceeding = ModelType(Proceeding) legislation = ModelType(Legislation, required=True) def get_role(self): return 'edit' @serializable(serialized_name="legislation", serialize_when_none=False) def fill_legislation(self): legislation = { 'version': '2020-11-21', 'type': 'NATIONAL_LEGISLATION', 'article': self.legislation.article, 'identifier': { 'id': '8073-X', 'legalName': 'Кодекс України про адміністративні правопорушення', 'uri': 'https://zakon.rada.gov.ua/laws/show/80731-10#Text', } } return legislation
class Conclusion(Report): violationOccurred = BooleanType(required=True) violationType = ListType(StringType(choices=VIOLATION_TYPE_CHOICES), default=[]) otherViolationType = StringType() auditFinding = StringType() stringsAttached = StringType() description = StringType(required=False) date = IsoDateTimeType(required=False) relatedParty = StringType() def validate_relatedParty(self, data, value): parent = data['__parent__'] if value and isinstance( parent, Model) and value not in [i.id for i in parent.parties]: raise ValidationError(u"relatedParty should be one of parties.") def validate_violationType(self, data, value): if data["violationOccurred"] and not value: raise ValidationError(u"This field is required.") if value and OTHER_VIOLATION not in value: # drop other type description data["otherViolationType"] = None def validate_otherViolationType(self, data, value): if OTHER_VIOLATION in data["violationType"] and not value: raise ValidationError(u"This field is required.")
class Decision(Report): date = IsoDateTimeType(required=False) relatedParty = StringType() def validate_relatedParty(self, data, value): parent = data['__parent__'] if value and isinstance(parent, Model) and value not in [i.id for i in parent.parties]: raise ValidationError(u"relatedParty should be one of parties.")
class Party(Model): id = MD5Type(required=True, default=lambda: uuid4().hex) name = StringType(required=True, min_length=1) identifier = ModelType(Identifier) additionalIdentifiers = ListType(ModelType(Identifier)) address = ModelType(Address) contactPoint = ModelType(ContactPoint) roles = ListType(StringType(choices=[]), default=[]) datePublished = IsoDateTimeType(default=get_now)
class Inspection(BaseModel): class Options: roles = { 'plain': blacklist('_attachments', 'revisions') + schematics_embedded_role, 'revision': whitelist('revisions'), 'create': blacklist('revisions', 'dateModified', 'dateCreated', 'doc_id', '_attachments', 'inspection_id') + schematics_embedded_role, 'edit': whitelist("description", "monitoring_ids"), 'view': blacklist( '_attachments', 'revisions', ) + schematics_embedded_role, 'listing': whitelist('dateModified', 'doc_id'), 'default': schematics_default_role, } monitoring_ids = ListType(MD5Type, required=True, min_size=1) description = StringType(required=True) documents = ListType(ModelType(Document), default=list()) inspection_id = StringType() dateModified = IsoDateTimeType() dateCreated = IsoDateTimeType(default=get_now) revisions = ListType(ModelType(Revision), default=list()) _attachments = DictType(DictType(BaseType), default=dict()) def __repr__(self): return '<%s:%r-%r@%r>' % (type(self).__name__, self.inspection_id, self.id, self.rev)
class Party(Model): class Options: roles = { 'create': blacklist('id') + schematics_embedded_role, 'edit': blacklist('id') + schematics_embedded_role, 'embedded': schematics_embedded_role, 'view': schematics_default_role, } id = MD5Type(required=True, default=lambda: uuid4().hex) name = StringType(required=True) identifier = ModelType(Identifier, required=True) additionalIdentifiers = ListType(ModelType(Identifier)) address = ModelType(Address, required=True) contactPoint = ModelType(ContactPoint, required=True) roles = ListType(StringType(choices=PARTY_ROLES_CHOICES), default=[]) datePublished = IsoDateTimeType(default=get_now)
class Document(Model): class Options: roles = { 'create': blacklist('id', 'datePublished', 'dateModified', 'author', 'download_url'), 'edit': blacklist('id', 'url', 'datePublished', 'dateModified', 'author', 'hash', 'download_url'), 'embedded': (blacklist('url', 'download_url') + schematics_embedded_role), 'default': blacklist("__parent__"), 'view': (blacklist('revisions') + schematics_default_role), 'revisions': whitelist('url', 'dateModified'), } id = MD5Type(required=True, default=lambda: uuid4().hex) hash = HashType() title = StringType(required=True) # A title of the document. title_en = StringType() title_ru = StringType() description = StringType() # A description of the document. description_en = StringType() description_ru = StringType() format = StringType(required=True, regex='^[-\w]+/[-\.\w\+]+$') url = StringType(required=True) # Link to the document or attachment. datePublished = IsoDateTimeType(default=get_now) dateModified = IsoDateTimeType( default=get_now) # Date that the document was last dateModified language = StringType() relatedItem = MD5Type() author = StringType() documentType = StringType(choices=[ "tenderNotice", "awardNotice", "contractNotice", "notice", "biddingDocuments", "technicalSpecifications", "evaluationCriteria", "clarifications", "shortlistedFirms", "riskProvisions", "billOfQuantity", "bidders", "conflictOfInterest", "debarments", "evaluationReports", "winningBid", "complaints", "contractSigned", "contractArrangements", "contractSchedule", "contractAnnexe", "contractGuarantees", "subContract", "eligibilityCriteria", "contractProforma", "commercialProposal", "qualificationDocuments", "eligibilityDocuments", "registerExtract", "registerFiscal", ]) @serializable(serialized_name="url") def download_url(self): url = self.url if not url or '?download=' not in url: return url doc_id = parse_qs(urlparse(url).query)['download'][-1] root = self.__parent__ parents = [] while root.__parent__ is not None: parents[0:0] = [root] root = root.__parent__ request = root.request if not request.registry.docservice_url: return url if 'status' in parents[0] and parents[0].status in type( parents[0])._options.roles: role = parents[0].status for index, obj in enumerate(parents): if obj.id != url.split('/')[(index - len(parents)) * 2 - 1]: break field = url.split('/')[(index - len(parents)) * 2] if "_" in field: field = field[0] + field.title().replace("_", "")[1:] roles = type(obj)._options.roles if roles[role if role in roles else 'default'](field, []): return url from openprocurement.audit.api.utils import generate_docservice_url if not self.hash: path = [ i for i in urlparse(url).path.split('/') if len(i) == 32 and not set(i).difference(hexdigits) ] return generate_docservice_url(request, doc_id, False, '{}/{}'.format(path[0], path[-1])) return generate_docservice_url(request, doc_id, False) def import_data(self, raw_data, **kw): """ Converts and imports the raw data into the instance of the model according to the fields in the model. :param raw_data: The data to be imported. """ data = self.convert(raw_data, **kw) del_keys = [k for k in data.keys() if data[k] == getattr(self, k)] for k in del_keys: del data[k] self._data.update(data) return self
class Revision(Model): author = StringType() date = IsoDateTimeType(default=get_now) changes = ListType(DictType(BaseType), default=list()) rev = StringType()
class Report(Model): description = StringType(required=True) documents = ListType(ModelType(Document), default=[]) dateCreated = IsoDateTimeType(default=get_now) datePublished = IsoDateTimeType()
class Proceeding(Model): dateProceedings = IsoDateTimeType(required=True) proceedingNumber = StringType(required=True)
class Monitoring(BaseModel): class Options: _perm_edit_whitelist = whitelist('status', 'reasons', 'procuringStages') roles = { 'plain': blacklist('_attachments', 'revisions') + schematics_embedded_role, 'revision': whitelist('revisions'), 'create': whitelist( "tender_id", "reasons", "procuringStages", "status", "mode", "monitoringDetails", "parties", "decision", "riskIndicators", "riskIndicatorsTotalImpact", "riskIndicatorsRegion", ), 'edit_draft': whitelist('decision', 'cancellation') + _perm_edit_whitelist, 'edit_active': whitelist('conclusion', 'cancellation') + _perm_edit_whitelist, 'edit_addressed': whitelist('eliminationResolution', 'cancellation') + _perm_edit_whitelist, 'edit_declined': whitelist('cancellation') + _perm_edit_whitelist, 'edit_completed': whitelist('documents'), 'edit_closed': whitelist('documents'), 'edit_stopped': whitelist('documents'), 'edit_cancelled': whitelist('documents'), 'view': blacklist('tender_owner_token', '_attachments', 'revisions', 'decision', 'conclusion', 'cancellation') + schematics_embedded_role, 'listing': whitelist('dateModified', 'doc_id'), 'default': schematics_default_role, } tender_id = MD5Type(required=True) monitoring_id = StringType() status = StringType(choices=MONITORING_STATUS_CHOICES, default=DRAFT_STATUS) reasons = ListType(StringType(choices=MONITORING_REASON_CHOICES), required=True) procuringStages = ListType(StringType(choices=MONITORING_PROCURING_STAGES), required=True) monitoringPeriod = ModelType(Period) documents = ListType(ModelType(Document), default=[]) riskIndicators = ListType(StringType(), default=[]) riskIndicatorsTotalImpact = FloatType() riskIndicatorsRegion = StringType() decision = ModelType(Decision) conclusion = ModelType(Conclusion) eliminationReport = ModelType(EliminationReport) eliminationResolution = ModelType(EliminationResolution) eliminationPeriod = ModelType(Period) posts = ListType(ModelType(Post), default=[]) cancellation = ModelType(Cancellation) appeal = ModelType(Appeal) liabilities = ListType(ModelType(Liability), default=list()) parties = ListType(ModelType(MonitoringParty), default=[]) dateModified = IsoDateTimeType() endDate = IsoDateTimeType() dateCreated = IsoDateTimeType(default=get_now) tender_owner = StringType() tender_owner_token = StringType() revisions = ListType(ModelType(Revision), default=[]) _attachments = DictType(DictType(BaseType), default=dict()) mode = StringType(choices=['test']) if SANDBOX_MODE: monitoringDetails = StringType() @serializable(serialized_name='decision', serialize_when_none=False, type=ModelType(Decision)) def monitoring_decision(self): role = self.__parent__.request.authenticated_role if self.decision and self.decision.datePublished or role == 'sas': return self.decision @serializable(serialized_name='conclusion', serialize_when_none=False, type=ModelType(Conclusion)) def monitoring_conclusion(self): role = self.__parent__.request.authenticated_role if self.conclusion and self.conclusion.datePublished or role == 'sas': return self.conclusion @serializable(serialized_name='cancellation', serialize_when_none=False, type=ModelType(Cancellation)) def monitoring_cancellation(self): role = self.__parent__.request.authenticated_role if self.cancellation and self.cancellation.datePublished or role == 'sas': return self.cancellation def get_role(self): role = super(Monitoring, self).get_role() status = self.__parent__.request.context.status return 'edit_{}'.format(status) if role == 'edit' else role def __acl__(self): return [ (Allow, '{}_{}'.format(self.tender_owner, self.tender_owner_token), 'create_post'), (Allow, '{}_{}'.format(self.tender_owner, self.tender_owner_token), 'create_elimination_report'), (Allow, '{}_{}'.format(self.tender_owner, self.tender_owner_token), 'create_appeal'), ] def __repr__(self): return '<%s:%r-%r@%r>' % (type(self).__name__, self.tender_id, self.id, self.rev)
class Request(BaseModel): class Options: namespace = "Request" roles = { "plain": blacklist("revisions") + schematics_embedded_role, "revision": whitelist("revisions"), "create": whitelist("description", "violationType", "documents", "parties", "tenderId", "mode"), "edit": whitelist("answer", "reason"), "view": blacklist("revisions") + schematics_embedded_role, "view_%s" % SAS_ROLE: blacklist("revisions") + schematics_embedded_role, "view_%s" % PUBLIC_ROLE: blacklist("revisions") + schematics_embedded_role, "listing": whitelist("dateModified", "doc_id"), "default": schematics_default_role, } description = StringType(required=True, min_length=1) violationType = ListType(StringType(choices=VIOLATION_TYPE_CHOICES), required=True, min_size=1) dateAnswered = IsoDateTimeType() dateModified = IsoDateTimeType() dateCreated = IsoDateTimeType(default=get_now) requestId = StringType() tenderId = MD5Type(required=True) documents = ListType(ModelType(Document, required=True), required=True, min_size=1) parties = ListType(ModelType(RequestParty, required=True), required=True, min_size=1) revisions = ListType(ModelType(Revision), default=list()) reason = StringType() answer = StringType(choices=[ "monitoringCreated", "noViolations", "plannedInspection", "lawEnforcement", "inspectionCreated", "plannedMonitoring", "noCompetency", "tenderCancelled", "violationRemoved" ]) def __repr__(self): return "<%s:%r-%r@%r>" % ( type(self).__name__, self.requestId, self.id, self.rev, ) def validate_answer(self, data, value): if data.get("reason") and not value: raise ValidationError("This field is required.") def validate_reason(self, data, value): if data.get("answer") and not value: raise ValidationError("This field is required.")