# !/usr/bin/env python# -*- coding: utf-8 -*-## Filename: base.py# Project: models# Author: Brian Cherinka# Created: Monday, June 28 2021 4:08:00 pm# License: BSD 3-clause "New" or "Revised" License# Copyright (c) 2021 Brian Cherinka# Last Modified: Monday, June 28 2021 4:08:00 pm# Modified By: Brian CherinkafrompydanticimportBaseModel,ConfigDictfromtypingimportIterator,Union,Callable,Dict,Any,Type
[docs]defadd_repr(schema:Dict[str,Any],model:Type[BaseModel])->None:""" Adds custom information into the schema """# "repr" flag in Field not retained when dumping model schema# this adds the "repr" boolean Field into the schema for each# property on a model. Use until this is fixed in the core Pydantic.forkey,modinmodel.model_fields.items():ifmod.reprisFalse:schema['properties'][key].update({'repr':False})
[docs]classCoreModel(BaseModel):""" Custom BaseModel """# TODO[pydantic]: We couldn't refactor this class, please create the `model_config` manually.# Check https://docs.pydantic.dev/dev-v2/migration/#changes-to-config for more information.# class Config:# """ base model config """# @staticmethod# def schema_extra(schema: Dict[str, Any], model: Type[BaseModel]) -> None:# """ Adds custom information into the schema """# # "repr" flag in Field not retained when dumping model schema# # this adds the "repr" boolean Field into the schema for each# # property on a model. Use until this is fixed in the core Pydantic.# for key, mod in model.__fields__.items():# if mod.field_info.repr is False:# schema['properties'][key].update({'repr': False})model_config=ConfigDict(json_schema_extra=add_repr)def__repr_args__(self):""" Custom repr args By default, for model fields that are complex objects, pydantic displays the entire object in the repr. This allows specifying an object attribute to be used for display in the repr instead, using an extra "repr_attr" field, with the name of the attribute. See models Survey and Phase for example. """rargs=[]# loop over model's repr argsforiinsuper().__repr_args__():# get the field_info "extra" and look for the "repr_attr" keyext=self.model_fields[i[0]].json_schema_extraifextand'repr_attr'inext:# use the object attribute specified as the repr argifisinstance(i[1],list):attr=[getattr(e,ext['repr_attr'])foreini[1]]else:attr=getattr(i[1],ext['repr_attr'])rargs.append((i[0],attr))else:rargs.append(i)returnrargs
[docs]classBaseList(BaseModel):""" Base pydantic class for lists of models """def__iter__(self)->Iterator:returniter(self.root)def__contains__(self,value)->bool:""" Uses the model name to check list inclusion """ifisinstance(value,str):returnvalue.lower()in[i.name.lower()foriinself]returnvalueinself.rootdef__getitem__(self,item:Union[str,int])->BaseModel:""" Allows item access by index or model name """ifisinstance(item,str)anditeminself:vals=[i.name.lower()foriinself.root]returnself.root[vals.index(item.lower())]elifisinstance(item,BaseModel):returnself[self.root.index(item)]returnself.root[item]def__repr__(self)->str:""" Creates a simple repr in a verbose list format """val='\n '.join(repr(i)foriinself)returnf"[{val}]"def__str__(self)->str:""" Stringifies the model names """returnf"[{', '.join([i.nameforiinself])}]"def__len__(self)->int:""" Returns the length of the list """returnlen(self.root)
[docs]defsort(self,field:str,key:Callable=None,**kwargs)->None:""" Sort the list of models by a pydantic field name Performs an in-place sort of the Pydantic Models using Python's built-in ``sorted()`` method. Sets the newly sorted list to the ``root`` attribute, to preserve the original BaseList object instance. By default, the input sort ``key`` to the ``sorted`` function is the field attribute on the model. Parameters ---------- field : str The Pydantic field name key : Callable, optional a function to be passed into the sorted() function, by default None """ifnotkey:key=lambdax:getattr(x,field)vals=sorted(self.root.copy(),key=key,**kwargs)self.root=vals
[docs]deflist_names(self):""" Create a simplified list of name attributes """return[item.nameforiteminself.root]