class Test2: id = Integer(primary_key=True) name = String() test_id = Integer(foreign_key=Model.Test.use('id'))
class Person: name = String(primary_key=True) addresses = Many2Many( m2m_remote_columns=['test_id'], model=Model.Address)
class Person: name = String(primary_key=True) addresses = Many2Many(model=Model.Address, join_model=Model.PersonAddress, many2many="persons")
class Person: name = String(primary_key=True) addresses = Many2Many(model=Model.Address, remote_columns="id2")
class Test: id = Integer(primary_key=True) id2 = String(primary_key=True)
class User: """User declaration need for Auth""" login = String(primary_key=True, nullable=False) @classmethod def get_roles(cls, login): """Return the roles of an user :param login: str, login attribute of the user :rtype: list of str (name of the roles) """ # cache the method roles = [login] user = cls.query().filter(cls.login == login).one_or_none() if user: for role in user.roles: roles.extend(role.roles_name) return list(set(roles)) @classmethod def get_acl(cls, login, resource, params=None): """Retun the ACL for a ressource and a user Auth, does not implement any rule to compute ACL, This method allow all user to use the resource ask by controllers. For other configuration, this method must be overwrite :param login: str, login attribute of the user :param resource: str, name of a resource :param params: all options need to compute ACL """ # cache the method return [(Allow, login, ALL_PERMISSIONS)] @classmethod def format_login_params(cls, request): """Return the login and password from query By default the query come from json_body and are named **login** and **password** If the entries come from another place, this method must be overwrite :param request: the request from the controllers """ return request.json_body @classmethod def check_login(cls, login=None, password=None, **kwargs): """Check login / password This method raise an exception, because any credential is stored in this bloks .. warning:: This method must be overwriting by anycredential blok :param login: str, the login attribute of the user :param password: str :param kwargs: any options need to validate credential """ raise HTTPUnauthorized() @classmethod def get_login_location_to(cls, login, request): """Return the default path after the login""" return '/' @classmethod def get_logout_location_to(cls, request): """Return the default path after the logout""" return '/'
class Room: id = Integer(primary_key=True) name = String(label="Room name", nullable=False, index=True) capacity = Integer(label="Capacity", nullable=False)
class Arrival(Operation): """Operation to describe physical arrival of goods in some location. Arrivals store data about the expected or arrived Goods: properties, code… These are copied over to the corresponding Goods records in all cases and stay inert after the fact. In case the Arrival state is ``planned``, these are obviously only unchecked values, but in case it is ``done``, the actual meaning can depend on the application: - maybe the application won't use the ``planned`` state at all, and will only create Arrival after checking them, - maybe the application will inspect the Arrival properties, compare them to reality, update them on the created Goods and cancel downstream operations if needed, before calling :meth:`execute`. TODO maybe provide higher level facilities for validation scenarios. """ TYPE = 'wms_arrival' id = Integer(label="Identifier", primary_key=True, autoincrement=False, foreign_key=Operation.use('id').options(ondelete='cascade')) """Primary key.""" goods_type = Many2One(model='Model.Wms.Goods.Type') """Expected :class:`Goods Type <anyblok_wms_base.bloks.wms_core.goods.Type>`. """ goods_properties = Jsonb(label="Properties of arrived Goods") """Expected :class:`Properties <anyblok_wms_base.bloks.wms_core.goods.Properties>`. They are copied over to the newly created :class:`Goods <anyblok_wms_base.bloks.wms_core.goods.Goods>` as soon as the Arrival is planned, and aren't updated by :meth:`execute`. Matching them with reality is the concern of separate validation processes, and this field can serve for later assessments after the fact. """ goods_code = String(label="Code to set on arrived Goods") """Expected :attr:`Goods code <anyblok_wms_base.bloks.wms_core.goods.Goods.code>`. Can be ``None`` in case the arrival process issues the code only at the time of actual arrival. """ location = Many2One(model='Model.Wms.Location') """Will be the location of the initial Avatar.""" inputs_number = 0 """This Operation is a purely creative one.""" def specific_repr(self): return ("goods_type={self.goods_type!r}, " "location={self.location!r}").format(self=self) def after_insert(self): Goods = self.registry.Wms.Goods self_props = self.goods_properties if self_props is None: props = None else: props = Goods.Properties.create(**self_props) goods = Goods.insert(type=self.goods_type, properties=props, code=self.goods_code) Goods.Avatar.insert( goods=goods, location=self.location, reason=self, state='present' if self.state == 'done' else 'future', dt_from=self.dt_execution, ) def execute_planned(self): Avatar = self.registry.Wms.Goods.Avatar Avatar.query().filter(Avatar.reason == self).one().update( state='present', dt_from=self.dt_execution)
class Task: """Main Task, define the main table""" TASK_TYPE = None id = Integer(primary_key=True) label = String(nullable=False) create_at = DateTime(default=datetime.now, nullable=False) update_at = DateTime(default=datetime.now, nullable=False, auto_update=True) task_type = Selection(selections="get_task_type", nullable=False) order = Integer(nullable=False, default=100) main_task = Many2One(model='Model.Dramatiq.Task', one2many="sub_tasks") @classmethod def get_task_type(cls): """List the task type possible""" return { 'call_method': 'Call classmethod', 'stepbystep': 'Step by Step', 'parallel': 'Parallel steps', } @classmethod def define_mapper_args(cls): """Polymorphism configuration""" mapper_args = super(Task, cls).define_mapper_args() if cls.__registry_name__ == Model.Dramatiq.Task.__registry_name__: mapper_args.update({ 'polymorphic_identity': cls.TASK_TYPE, 'polymorphic_on': cls.task_type, }) else: mapper_args.update({ 'polymorphic_identity': cls.TASK_TYPE, }) return mapper_args def do_the_job(self, main_job=None, run_at=None, with_args=None, with_kwargs=None): """Create a job for this tash and add send it to dramatiq :param main_job: parent job if exist :param run_at: datetime to execute the job :param with_args: tuple of the argument to pass at the job :param with_kwargs: dict of the argument to pass at the job """ values = dict(run_at=run_at, data=dict(with_args=with_args or tuple(), with_kwargs=with_kwargs or dict()), task=self, main_job=main_job) job = self.registry.Dramatiq.Job.insert(**values) # FIXME dramatiq don t accept uuid, waiting the next version self.registry.Dramatiq.Job.run(job_uuid=str(job.uuid), run_at=run_at) def run(self, job): """Execute the task for one job :param job: job executed """ raise Exception("No task definition for job %r" % job) def run_next(self, job): """next action to execute when a sub job finish this task for one job :param job: job executed """ raise Exception("No next action define for this task for job %r" % job)
class Test(Mixin.ReadOnly): id = Integer(primary_key=True) name = String()
class Authorization: """A model to store autorization rules (permissions for users against an Anyblok model or a Pyramid resource)""" id = Integer(primary_key=True) order = Integer(default=100, nullable=False) resource = String() model = String( foreign_key=Declarations.Model.System.Model.use('name').options( ondelete="cascade")) primary_keys = Json(default={}) filter = Json(default={}) # next step role = Many2One(model=User.Role, foreign_key_options={'ondelete': 'cascade'}) login = String(foreign_key=User.use('login').options(ondelete="cascade")) user = Many2One(model=User) perms = Json(default={}) perm_create = JsonRelated(json_column='perms', keys=['create']) perm_read = JsonRelated(json_column='perms', keys=['read']) perm_update = JsonRelated(json_column='perms', keys=['update']) perm_delete = JsonRelated(json_column='perms', keys=['delete']) @classmethod def get_acl_filter_model(cls): """Return the Model to use to check the permission""" return { 'User': cls.registry.User, 'Role': cls.registry.User.Role, } @classmethod def get_acl(cls, login, resource, params=None): """Return the Pyramid ACL in function of the resource and user :param login: str, login of the user :param resource: str, name of the resource """ # cache the method User = cls.registry.User Role = cls.registry.User.Role query = cls.query() query = query.filter( or_(cls.resource == resource, cls.model == resource)) query = query.order_by(cls.order) Q1 = query.filter(cls.login == login) Q2 = query.join(cls.role).filter(Role.name.in_(User.get_roles(login))) res = [] for query in (Q1, Q2): for self in query.all(): allow_perms = [] deny_perms = [] perms = list((self.perms or {}).keys()) perms.sort() for perm in perms: p = self.perms[perm] query = User.query() query = query.filter(User.login == login) query = query.join(User.roles) if self.filter: query = query.condition_filter( self.filter, cls.get_acl_filter_model()) if 'condition' in p: query = query.condition_filter( p['condition'], cls.get_acl_filter_model()) ismatched = True if query.count() else False if p.get('matched' if ismatched else 'unmatched') is True: allow_perms.append(perm) elif (p.get('matched' if ismatched else 'unmatched') is False): deny_perms.append(perm) if len(allow_perms): res.append((Allow, login, allow_perms)) if len(deny_perms): res.append((Deny, login, deny_perms)) res.append((Deny, login, ALL_PERMISSIONS)) return res @classmethod def before_insert_orm_event(cls, mapper, connection, target): target.check_validity() @classmethod def before_update_orm_event(cls, mapper, connection, target): target.check_validity() def check_validity(self): """When creating or updating a User.Authorization, check that all rules objects exists or return an AuthorizationValidationException :exception: AuthorizationValidationException """ if not (self.role or self.login or self.user or self.role_name): raise AuthorizationValidationException( "No role and login to apply in the authorization (%s)" % self) if not (self.resource or self.model): raise AuthorizationValidationException( "No resource and model to apply in the authorization (%s)" % self) if not self.model and self.primary_keys: raise AuthorizationValidationException( "Primary keys without model to apply in the authorization " "(%s)" % self)
class Test(Mixin.ForbidUpdate): id = Integer(primary_key=True) name = String()
class Test2: id = Integer(primary_key=True) name = String() test = Many2Many(model=Model.Test, many2many='test2')
class Test2: id = Integer(primary_key=True) name = String() test = One2One(model=Model.Test, backref='test2')
class Test: id = Integer(primary_key=True) name = String()
class Room: id = Integer(primary_key=True) name = String()
class JourneyWish(Mixin.UuidColumn, Mixin.TrackModel, Mixin.WorkFlow): """This model has Model.JourneyWish as a namespace. It is intented for storing journey wishes that represents travels intentions. For instance, an intention may be caracterized by start date, with an a time frame delimited by earlier and latest departure times, departure and arrival stations, passengers, maximum price, etc... Implemented fields are the following : * Departure station : Many2One relationship with Model.Station * Arrival station : Many2One relationship with Model.Station * Start date : DateTime representing earlier time after which train may leave the departure station * End date : DateTime representing latest time after which train may leave departure station. * Passengers : Many2Many relationship to Model.Passenger * Transportation mean : String, containing type of transport required by user (train, coach, etc...) * Active : boolean that stores if wish has to be processed * Activation date : Date, represent the moment when the wish could start being processed""" user = Many2One( label="User", model=Model.User, nullable=False, one2many="wishes" ) departure = Many2One( label="Departure Station", model=Model.Station, nullable=True, one2many="departures", ) arrival = Many2One( label="Arrival Station", model=Model.Station, nullable=True, one2many="arrivals", ) from_date = DateTime(label="Earlier Departure Date", nullable=True) end_date = DateTime(label="Latest Departure Date", nullable=True) activation_date = Date(label="Activation Date", nullable=True) passengers = Many2Many( model=Model.Passenger, join_table="join_passengers_by_wishes", remote_columns="uuid", local_columns="uuid", m2m_remote_columns="p_uuid", m2m_local_columns="w_uuid", many2many="wishes", ) transportation_mean = String( label="Transportation Mean", default="any", nullable=True ) active = Boolean(label="Active Wish", nullable=True) @classmethod def get_workflow_definition(cls): """This method is aimed at defining workflow used for model Model.JourneyWish""" return { "draft": { "default": True, "allowed_to": ["running", "pending"], "apply_change": "deactivate", }, "running": { "allowed_to": ["cancelled", "expired"], "apply_change": "activate", }, "pending": { "allowed_to": ["cancelled", "expired", "running"], "apply_change": "deactivate", }, "expired": {"apply_change": "deactivate"}, "cancelled": {"apply_change": "deactivate"}, } def activate(self, from_state): if from_state == "draft" and not self.activation_date: self.activation_date = date.today() self.active = True def deactivate(self, from_state): self.active = False def check_state(self): """This method is aimed at being used in order to automatically set workflow state, depending on record attributes.""" if self.state == "pending": # If wish is pending, check is activation_date is set if self.activation_date and self.activation_date <= date.today(): self.state_to("running") elif self.state == "running": # If wish is already running, check that from_date is not in the # past try: now = datetime.now().astimezone() except ValueError: # Python3.5 and below do not support astimezone on 'naive' # dates provided by datetime.now() now = datetime.now(timezone.utc).astimezone() if self.from_date and self.from_date < now: self.state_to("expired")
class Engineer(Model.Employee): id = Integer(primary_key=True, foreign_key=Model.Employee.use('id')) engineer_name = String()
class Order(Mixin.UuidColumn, Mixin.TrackModel, Mixin.WorkFlow): """Sale.Order model """ SCHEMA = OrderBaseSchema @classmethod def get_schema_definition(cls, **kwargs): return cls.SCHEMA(**kwargs) @classmethod def get_workflow_definition(cls): return { 'draft': { 'default': True, 'allowed_to': ['quotation', 'cancelled'] }, 'quotation': { 'allowed_to': ['order', 'cancelled'], 'validators': SchemaValidator( cls.get_schema_definition(exclude=['price_list'])) }, 'order': { 'validators': SchemaValidator( cls.get_schema_definition(exclude=['price_list'])) }, 'cancelled': {}, } code = String(label="Code", nullable=False) channel = String(label="Sale Channel", nullable=False) price_list = Many2One(label="Price list", model=Declarations.Model.Sale.PriceList) delivery_method = String(label="Delivery Method") amount_untaxed = Decimal(label="Amount Untaxed", default=D(0)) amount_tax = Decimal(label="Tax amount", default=D(0)) amount_total = Decimal(label="Total", default=D(0)) def __str__(self): return "{self.uuid} {self.channel} {self.code} {self.state}".format( self=self) def __repr__(self): return "<Sale(id={self.uuid}, code={self.code}," \ " amount_untaxed={self.amount_untaxed},"\ " amount_tax={self.amount_tax},"\ " amount_total={self.amount_total},"\ " channel={self.channel} state={self.state})>".format( self=self) @classmethod def create(cls, price_list=None, **kwargs): data = kwargs.copy() if cls.get_schema_definition: sch = cls.get_schema_definition(registry=cls.registry, exclude=['lines']) if price_list: data["price_list"] = price_list.to_primary_keys() data = sch.load(data) data['price_list'] = price_list return cls.insert(**data) def compute(self): """Compute order total amount""" amount_untaxed = D(0) amount_tax = D(0) amount_total = D(0) for line in self.lines: amount_untaxed += line.amount_untaxed amount_tax += line.amount_tax amount_total += line.amount_total self.amount_untaxed = amount_untaxed self.amount_tax = amount_tax self.amount_total = amount_total
class Track: id = Integer(primary_key=True) name = String(label="Name", nullable=False)
class Person: name = String(primary_key=True) invoiced_addresses = Many2Many(model=Model.Address) delivery_addresses = Many2Many(model=Model.Address)
class Guest: id = Integer(primary_key=True) name = String(nullable=False) def __repr__(self): return "<Guest(name={self.name!r})>".format(self=self)
class Person2(Mixin.MixinM2M): name = String(primary_key=True)
class TodoList: id = Integer(primary_key=True) name = String(label="Name", unique=True, nullable=False)
class Test2: id = Integer(primary_key=True) id2 = String(primary_key=True) test = Many2Many(model=Model.Test, many2many="test2")
class Question: id = Integer(primary_key=True) name = String(label="Name", nullable=False) survey_id = Integer(foreign_key="Model.Survey=>id") position = Integer(label="Position", nullable=True)
class Person: name = String(primary_key=True) addresses = Many2Many(model=Model.Address)
class Mapping: key = String(primary_key=True) model = String(primary_key=True, foreign_key=Model.System.Model.use('name')) primary_key = Json(nullable=False) blokname = String(label="Blok name", foreign_key=Model.System.Blok.use('name')) @hybrid_method def filter_by_model_and_key(self, model, key): """ SQLAlechemy hybrid method to filter by model and key :param model: model of the mapping :param key: external key of the mapping """ return (self.model == model) & (self.key == key) @hybrid_method def filter_by_model_and_keys(self, model, *keys): """ SQLAlechemy hybrid method to filter by model and key :param model: model of the mapping :param key: external key of the mapping """ return (self.model == model) & self.key.in_(keys) def remove_element(self, byquery=False): val = self.registry.get(self.model).from_primary_keys( **self.primary_key) logger.info("Remove entity for %r.%r: %r" % ( self.model, self.key, val)) val.delete(byquery=byquery, remove_mapping=False) @classmethod def multi_delete(cls, model, *keys, **kwargs): """ Delete all the keys for this model :param model: model of the mapping :param \*keys: list of the key :rtype: Boolean True if the mappings are removed """ mapping_only = kwargs.get('mapping_only', True) byquery = kwargs.get('byquery', False) query = cls.query() query = query.filter(cls.filter_by_model_and_keys(model, *keys)) count = query.count() if count: if not mapping_only: query.all().remove_element(byquery=byquery) query.delete(synchronize_session='fetch', remove_mapping=False) cls.registry.expire_all() return count return 0 @classmethod def delete(cls, model, key, mapping_only=True, byquery=False): """ Delete the key for this model :param model: model of the mapping :param key: string of the key :rtype: Boolean True if the mapping is removed """ query = cls.query() query = query.filter(cls.filter_by_model_and_key(model, key)) count = query.count() if count: if not mapping_only: query.one().remove_element(byquery=byquery) query.delete(remove_mapping=False) return count return 0 @classmethod def get_mapping_primary_keys(cls, model, key): """ return primary key for a model and an external key :param model: model of the mapping :param key: string of the key :rtype: dict primary key: value or None """ query = cls.query() query = query.filter(cls.filter_by_model_and_key(model, key)) if query.count(): pks = query.first().primary_key cls.check_primary_keys(model, *pks.keys()) return pks return None @classmethod def check_primary_keys(cls, model, *pks): """ check if the all the primary keys match with primary keys of the model :param model: model to check :param pks: list of the primary keys to check :exception: IOMappingCheckException """ for pk in cls.get_model(model).get_primary_keys(): if pk not in pks: raise IOMappingCheckException( "No primary key %r found in %r for model %r" % ( pk, pks, model)) @classmethod def set_primary_keys(cls, model, key, pks, raiseifexist=True, blokname=None): """ Add or update a mmping with a model and a external key :param model: model to check :param key: string of the key :param pks: dict of the primary key to save :param raiseifexist: boolean (True by default), if True and the entry exist then an exception is raised :param blokname: name of the blok where come from the mapping :exception: IOMappingSetException """ if cls.get_mapping_primary_keys(model, key): if raiseifexist: raise IOMappingSetException( "One value found for model %r and key %r" % (model, key)) cls.delete(model, key) if not pks: raise IOMappingSetException( "No value to save %r for model %r and key %r" % ( pks, model, key)) cls.check_primary_keys(model, *pks.keys()) vals = dict(model=model, key=key, primary_key=pks) if blokname is not None: vals['blokname'] = blokname return cls.insert(**vals) @classmethod def set(cls, key, instance, raiseifexist=True, blokname=None): """ Add or update a mmping with a model and a external key :param model: model to check :param key: string of the key :param instance: instance of the model to save :param raiseifexist: boolean (True by default), if True and the entry exist then an exception is raised :param blokname: name of the blok where come from the mapping :exception: IOMappingSetException """ pks = instance.to_primary_keys() return cls.set_primary_keys(instance.__registry_name__, key, pks, blokname=blokname, raiseifexist=raiseifexist) @classmethod def get(cls, model, key): """ return instance of the model with this external key :param model: model of the mapping :param key: string of the key :rtype: instance of the model """ pks = cls.get_mapping_primary_keys(model, key) if pks is None: return None return cls.get_model(model).from_primary_keys(**pks) @classmethod def get_from_model_and_primary_keys(cls, model, pks): query = cls.query().filter(cls.model == model) for mapping in query.all(): if mapping.primary_key == pks: return mapping return None @classmethod def get_from_entry(cls, entry): return cls.get_from_model_and_primary_keys( entry.__registry_name__, entry.to_primary_keys()) @classmethod def __get_models(cls, models): """Return models name if models is not: return all the existing model if models is a list of instance model, convert them :params models: list of model """ if models is None: models = cls.registry.System.Model.query().all().name elif not isinstance(models, (list, tuple)): models = [models] return [m.__registry_name__ if hasattr(m, '__registry_name__') else m for m in models] @classmethod def clean(cls, bloknames=None, models=None): """Clean all mapping with removed object linked:: Mapping.clean(bloknames=['My blok']) .. warning:: For filter only the no blokname:: Mapping.clean(bloknames=[None]) :params bloknames: filter by blok, keep the order to remove the mapping :params models: filter by model, keep the order to remove the mapping """ if bloknames is None: bloknames = cls.registry.System.Blok.query().all().name + [None] elif not isinstance(bloknames, (list, tuple)): bloknames = [bloknames] models = cls.__get_models(models) removed = 0 for blokname in bloknames: for model in models: query = cls.query().filter_by(blokname=blokname, model=model) for key in query.all().key: if cls.get(model, key) is None: cls.delete(model, key) removed += 1 return removed @classmethod def delete_for_blokname(cls, blokname, models=None, byquery=False): """Clean all mapping with removed object linked:: Mapping.clean('My blok') .. warning:: For filter only the no blokname:: Mapping.clean(None) :params blokname: filter by blok :params models: filter by model, keep the order to remove the mapping """ models = cls.__get_models(models) removed = 0 for model in models: query = cls.query().filter_by(blokname=blokname, model=model) for key in query.all().key: if cls.get(model, key): cls.delete(model, key, mapping_only=False, byquery=byquery) cls.registry.flush() removed += 1 return removed
class Address: id = Integer(primary_key=True) street = String() zip = String() city = String()
class Test2: id = Integer(primary_key=True) name = String() test = Many2One(model=Model.Test, one2many="test2")