Skip to content

Commit

Permalink
Merge pull request #9 from ProzorroUKR/frameworkagreement
Browse files Browse the repository at this point in the history
Framework Agreement
  • Loading branch information
dimka2014 authored Sep 28, 2018
2 parents 7435210 + 495d5cf commit 7f7f262
Show file tree
Hide file tree
Showing 15 changed files with 395 additions and 106 deletions.
22 changes: 22 additions & 0 deletions .gitlab-ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
image: alpine
before_script:
- eval `ssh-agent -s` && ssh-add
- sh bootstrap.sh
- bin/buildout -N
stages:
- test

test_sanbox_true:
stage: test
variables:
SANDBOX_MODE: 'True'
script:
- bin/py.test ./src/openprocurement/api/tests/ --cov=openprocurement.api


test_sanbox_not_true:
stage: test
variables:
SANDBOX_MODE: ''
script:
- bin/py.test ./src/openprocurement/api/tests/ --cov=openprocurement.api
6 changes: 3 additions & 3 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,14 @@ cache:
directories:
- eggs
before_install:
- pip install setuptools==7.0
- python2 bootstrap.py
- sh bootstrap.sh
install:
- bin/buildout -N
script:
- bin/nosetests
- bin/py.test ./src/openprocurement/api/tests/ --cov=openprocurement.api
after_success:
- bin/coveralls

notifications:
webhooks:
urls:
Expand Down
4 changes: 4 additions & 0 deletions bootstrap.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/bin/sh
virtualenv --clear .
./bin/pip install setuptools==7.0
./bin/pip install zc.buildout==2.2.5
3 changes: 3 additions & 0 deletions buildout.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,7 @@ dependent-scripts = true
eggs =
openprocurement.api [test]
nose
pytest
pytest-cov
# pylint

2 changes: 2 additions & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[pytest]
python_files = tests/*.py
2 changes: 2 additions & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
'couchdb-schematics',
'gevent',
'iso8601',
'isodate',
'jsonpatch',
'libnacl',
'pbkdf2',
Expand All @@ -22,6 +23,7 @@
'rfc6266',
'setuptools',
'tzlocal',
'zope.component',
]
test_requires = requires + [
'webtest',
Expand Down
20 changes: 20 additions & 0 deletions src/openprocurement/api/adapters.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
# -*- coding: utf-8 -*-
from schematics.types.base import BaseType
from schematics.types.serializable import Serializable as BaseSerializable

class ContentConfigurator(object):
""" Base OP Content Configuration adapter """

Expand All @@ -10,3 +13,20 @@ def __init__(self, context, request):

def __repr__(self):
return "<Configuration adapter for %s>" % type(self.context)


class Serializable(BaseSerializable):
serialized_name = None
serialize_when_none = True
serialized_type = BaseType()

def __init__(self, tender):
self.context = tender
serialized_type = self.context._fields.get(self.serialized_name, self.serialized_type)
super(Serializable, self).__init__(
self.__call__, type=serialized_type, serialized_name=self.serialized_name,
serialize_when_none=self.serialize_when_none
)

def __call__(self, obj, *args, **kwargs):
raise NotImplemented
39 changes: 39 additions & 0 deletions src/openprocurement/api/dev_tools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# -*- coding: utf-8 -*-
import os, sys
import csv

def get_fields_name(cls):
fields_name = set(cls._fields._keys)
fields_name.update(cls._serializables.keys())
return fields_name


def get_roles_from_object(cls):
fields_name = get_fields_name(cls)
roles_keys = [role_name for role_name in cls._options.roles]
roles = {}
for role_name in roles_keys:
rr = cls._options.roles[role_name]
roles[role_name] = set([i for i in list(fields_name) if not rr(i, '')])
return roles


def create_csv_roles(cls):
roles = get_roles_from_object(cls)
fields_name = list(get_fields_name(cls))
path_role_csv = ''
for i in os.path.abspath(sys.modules[cls.__module__].__file__).split('/')[:-1]:
path_role_csv += (i + '/')
with open('{0}{1}.csv'.format(path_role_csv, cls.__name__), 'wb') as csvfile:
fields_name.insert(0, 'rolename')
writer = csv.DictWriter(csvfile, fieldnames=fields_name)
writer.writeheader()
writer = csv.writer(csvfile)
for role_name in roles:
row = [role_name]
for field_name in fields_name[1:]:
if field_name in roles[role_name]:
row.append('1')
else:
row.append('')
writer.writerow(row)
8 changes: 8 additions & 0 deletions src/openprocurement/api/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,11 @@ class IOPContent(Interface):

class IContentConfigurator(Interface):
""" Content configurator """


class IValidator(Interface):
pass


class ISerializable(Interface):
pass
132 changes: 129 additions & 3 deletions src/openprocurement/api/models.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
# -*- coding: utf-8 -*-
import re
from datetime import datetime
from decimal import Decimal, InvalidOperation, ROUND_HALF_UP
from iso8601 import parse_date, ParseError
from isodate import ISO8601Error, parse_duration, duration_isoformat
from isodate.duration import Duration
from uuid import uuid4
from urlparse import urlparse, parse_qs
from string import hexdigits
from hashlib import algorithms, new as hash_new
from couchdb_schematics.document import SchematicsDocument
from schematics.exceptions import ConversionError, ValidationError
from schematics.models import Model as SchematicsModel
from schematics.models import ModelMeta
from schematics.transforms import whitelist, blacklist, export_loop, convert
from schematics.types import (StringType, FloatType, URLType, IntType,
BooleanType, BaseType, EmailType, MD5Type, DecimalType as BaseDecimalType)
from schematics.types.compound import (ModelType, DictType,
ListType as BaseListType)
from schematics.types.serializable import serializable

from openprocurement.api.interfaces import ISerializable, IValidator
from openprocurement.api.utils import get_now, set_parent, get_schematics_document
from openprocurement.api.constants import (
CPV_CODES, ORA_CODES, TZ, DK_CODES, CPV_BLOCK_FROM,
Expand All @@ -27,7 +31,84 @@
plain_role = (blacklist('_attachments', 'revisions', 'dateModified') + schematics_embedded_role)
listing_role = whitelist('dateModified', 'doc_id')
draft_role = whitelist('status')

from couchdb_schematics.document import DocumentMeta
from zope.component import getAdapter, queryAdapter, getAdapters

class AdaptiveDict(dict):
def __init__(self, context, interface, data, prefix=''):
self.context = context
self.interface = interface
self.prefix = prefix
self.prefix_len = len(prefix)
self.adaptive_items = {}
super(AdaptiveDict, self).__init__(data)

def __contains__(self, item):
return item in self.keys()

def __getitem__(self, key):
adapter = None
if key in self.adaptive_items:
return self.adaptive_items[key]
if self.prefix and key.startswith(self.prefix):
adapter = queryAdapter(self.context, self.interface, key[self.prefix_len:])
else:
adapter = queryAdapter(self.context, self.interface, key)
if adapter:
return adapter
val = dict.__getitem__(self, key)
return val

def __setitem__(self, key, val):
dict.__setitem__(self, key, val)

def __repr__(self):
dictrepr = dict.__repr__(self)
return '%s(%s)' % (type(self).__name__, dictrepr)

def keys(self):
return list(self)

def __iter__(self):
for item in self.iteritems():
yield item[0]

def iteritems(self):
for i in super(AdaptiveDict, self).iteritems():
yield i
for k, v in getAdapters((self.context,), self.interface):
if self.prefix:
k = self.prefix + k
self.adaptive_items[k] = v
for i in self.adaptive_items.iteritems():
yield i


class OpenprocurementCouchdbDocumentMeta(DocumentMeta):

def __new__(mcs, name, bases, attrs):
klass = DocumentMeta.__new__(mcs, name, bases, attrs)
klass._validator_functions = AdaptiveDict(
klass,
IValidator,
klass._validator_functions
)
klass._serializables = AdaptiveDict(
klass, ISerializable,
klass._serializables,
)
return klass


class OpenprocurementSchematicsDocument(SchematicsDocument):

__metaclass__ = OpenprocurementCouchdbDocumentMeta

def __init__(self, raw_data=None, deserialize_mapping=None):
super(OpenprocurementSchematicsDocument, self).__init__(raw_data=raw_data,
deserialize_mapping=deserialize_mapping)
if hasattr(self, 'Options') and hasattr(self.Options, 'namespace'):
self.doc_type = self.Options.namespace

class DecimalType(BaseDecimalType):

Expand Down Expand Up @@ -72,6 +153,38 @@ def to_primitive(self, value, context=None):
return value.isoformat()


class IsoDurationType(BaseType):
''' Iso Duration format
P is the duration designator (referred to as "period"), and is always placed at the beginning of the duration.
Y is the year designator that follows the value for the number of years.
M is the month designator that follows the value for the number of months.
W is the week designator that follows the value for the number of weeks.
D is the day designator that follows the value for the number of days.
T is the time designator that precedes the time components.
H is the hour designator that follows the value for the number of hours.
M is the minute designator that follows the value for the number of minutes.
S is the second designator that follows the value for the number of seconds.
examples: 'P5000Y72M8W10DT55H3000M5S'
'''

MESSAGES = {
'parse': u'Could not parse {0}. Should be ISO8601 Durations.',
}

def to_native(self, value, context=None):
if isinstance(value, Duration):
return value
try:
return parse_duration(value)
except TypeError:
raise ConversionError(self.messages['parse'].format(value))
except ISO8601Error as e:
raise ConversionError(e.message)

def to_primitive(self, value, context=None):
return duration_isoformat(value)


class ListType(BaseListType):

def export_loop(self, list_instance, field_converter,
Expand Down Expand Up @@ -155,8 +268,9 @@ def export_loop(self, list_instance, field_converter,
elif print_none:
return data


class Model(SchematicsModel):
__metaclass__ = OpenprocurementCouchdbDocumentMeta

class Options(object):
"""Export options for Document."""
serialize_when_none = False
Expand All @@ -167,6 +281,18 @@ class Options(object):

__parent__ = BaseType()

def __getattribute__(self, name):
serializables = super(Model, self).__getattribute__('_serializables')
if name in serializables.adaptive_items:
return serializables[name](self)
return super(Model, self).__getattribute__(name)

def __getitem__(self, name):
try:
return getattr(self, name)
except AttributeError as e:
raise KeyError(e.message)

def __eq__(self, other):
if isinstance(other, self.__class__):
for k in self._fields:
Expand Down
14 changes: 14 additions & 0 deletions src/openprocurement/api/roles.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import os
from schematics.transforms import export_loop, whitelist
import csv, os


class RolesFromCsv(dict):

def __init__(self, path, relative_to=__file__):
super(RolesFromCsv, self).__init__(())
self.base_dir = os.path.dirname(os.path.abspath(relative_to))
with open(os.path.join(self.base_dir, path)) as csvfile:
reader = csv.DictReader(csvfile)
for row in reader:
self[row['rolename']] = whitelist(*[k for k in row if k != 'rolename' and row[k]])
4 changes: 3 additions & 1 deletion src/openprocurement/api/tests/auth.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# -*- coding: utf-8 -*-
import os
import unittest
from pyramid import testing
from openprocurement.api.auth import AuthenticationPolicy
Expand All @@ -7,7 +8,8 @@

class AuthTest(TestBasicAuthAuthenticationPolicy):
def _makeOne(self, check):
return AuthenticationPolicy('src/openprocurement/api/tests/auth.ini', 'SomeRealm')
auth_file_path = "{}/auth.ini".format(os.path.dirname(os.path.abspath(__file__)))
return AuthenticationPolicy(auth_file_path, 'SomeRealm')

test_authenticated_userid_utf8 = None
test_authenticated_userid_latin1 = None
Expand Down
Loading

0 comments on commit 7f7f262

Please sign in to comment.