Source code for ccpn.core.Complex


# Licence, Reference and Credits
__copyright__ = "Copyright (C) CCPN project ( 2014 - 2021"
__credits__ = ("Ed Brooksbank, Joanna Fox, Victoria A Higman, Luca Mureddu, Eliza Płoskoń",
               "Timothy J Ragan, Brian O Smith, Gary S Thompson & Geerten W Vuister")
__licence__ = ("CCPN licence. See")
__reference__ = ("Skinner, S.P., Fogh, R.H., Boucher, W., Ragan, T.J., Mureddu, L.G., & Vuister, G.W.",
                 "CcpNmr AnalysisAssign: a flexible platform for integrated NMR analysis",
                 "J.Biomol.Nmr (2016), 66, 111-124,")
# Last code modification
__modifiedBy__ = "$modifiedBy: Ed Brooksbank $"
__dateModified__ = "$dateModified: 2021-10-11 20:43:39 +0100 (Mon, October 11, 2021) $"
__version__ = "$Revision: 3.0.4 $"
# Created
__author__ = "$Author: CCPN $"
__date__ = "$Date: 2017-04-07 10:28:41 +0000 (Fri, April 07, 2017) $"
# Start of code

import typing
from functools import partial

from ccpn.core.Project import Project
from ccpn.core.Chain import Chain
from ccpn.core._implementation.AbstractWrapperObject import AbstractWrapperObject
from ccpn.core.lib import Pid
from ccpnmodel.ccpncore.api.ccp.molecule.MolSystem import Chain as ApiChain
from ccpnmodel.ccpncore.api.ccp.molecule.MolSystem import ChainGroup as ApiChainGroup
from ccpn.util.decorators import logCommand
from ccpn.core.lib.ContextManagers import newObject, renameObject, ccpNmrV3CoreSetter

[docs]class Complex(AbstractWrapperObject): """A group of Chains, which can be used as a single object to describe a molecular complex.""" #: Class name and Short class name, for PID. shortClassName = 'MX' # Attribute it necessary as subclasses must use superclass className className = 'Complex' _parentClass = Project #: Name of plural link to instances of class _pluralLinkName = 'complexes' #: List of child classes. _childClasses = [] # Qualified name of matching API class _apiClassQualifiedName = ApiChainGroup._metaclass.qualifiedName() # internal namespace _COMMENT = 'comment' # CCPN properties @property def _apiChainGroup(self) -> ApiChainGroup: """ CCPN Project ChainGroup""" return self._wrappedData @property def _key(self) -> str: """Residue local ID""" return @property def name(self) -> str: """Name of Complex, part of identifier""" return @name.setter def name(self, value: str): """set name of Complex.""" self.rename(value) @property def serial(self) -> str: """Serial number of Complex, used for sorting""" return self._wrappedData.serial @property def _parent(self) -> Project: """Parent (containing) object.""" return self._project @property def chains(self) -> typing.Tuple[Chain, ...]: """Chains that make up Complex.""" data2Obj = self._project._data2Obj return tuple(sorted(data2Obj[x] for x in self._wrappedData.chains)) @chains.setter @logCommand(get='self', isProperty=True) @ccpNmrV3CoreSetter() def chains(self, value): getDataObj = self._project._data2Obj.get value = [getDataObj(x) if isinstance(x, str) else x for x in value] self._wrappedData.chains = [x._wrappedData for x in value] @property def comment(self) -> str: """Free-form text comment""" comment = self._getInternalParameter(self._COMMENT) return comment @comment.setter @logCommand(get='self', isProperty=True) @ccpNmrV3CoreSetter() def comment(self, value: str): """set optional comment of Complex.""" if not isinstance(value, (str, type(None))): raise ValueError("comment must be a string/None.") self._setInternalParameter(self._COMMENT, value) #========================================================================================= # Implementation functions #========================================================================================= @classmethod def _getAllWrappedData(cls, parent: Project) -> list: """get wrappedData for all Complexes linked to NmrProject""" molSystem = parent._wrappedData.molSystem if molSystem is None: return [] else: return molSystem.sortedChainGroups()
[docs] @renameObject() @logCommand(get='self') def rename(self, value: str): """Rename Complex, changing its name and Pid. """ name = self._uniqueName(project=self.project, name=value) # rename functions from here oldName = self._oldPid = self._wrappedData.__dict__['name'] = name return (oldName,)
@classmethod def _restoreObject(cls, project, apiObj): """Restore the object and update ccpnInternalData """ COMPLEX = 'complex' COMPLEXCOMMENT = 'complexComment' result = super()._restoreObject(project, apiObj) for namespace, param, newVar in [(COMPLEX, COMPLEXCOMMENT, cls._COMMENT), ]: if result.hasParameter(namespace, param): # move the internal parameter to the correct namespace value = result.getParameter(namespace, param) result.deleteParameter(namespace, param) result._setInternalParameter(newVar, value) return result
#========================================================================================= # CCPN functions #========================================================================================= #=========================================================================================== # new'Object' and other methods # Call appropriate routines in their respective locations #=========================================================================================== #========================================================================================= # Connections to parents: #========================================================================================= @newObject(Complex) def _newComplex(self: Project, name: str, chains=(), comment: str = None) -> Complex: """Create new Complex. See the Complex class for details. :param name: :param chains: :param comment: optional comment. :return: a new Complex instance. """ name = Complex._uniqueName(project=self, name=name) if chains: getByPid = self._project.getByPid chains = [getByPid(x) if isinstance(x, str) else x for x in chains] result = self._data2Obj.get(self._wrappedData.molSystem.newChainGroup(name=name)) if result is None: raise RuntimeError('Unable to generate new Complex item') if comment: result.comment = comment if chains: result.chains = chains return result #EJB 20181205 moved to Project # Project.newComplex = _newComplex # del _newComplex # reverse link Chain.complexes def getter(self: Chain) -> typing.Tuple[Complex, ...]: data2Obj = self._project._data2Obj return tuple(sorted(data2Obj[x] for x in self._wrappedData.chainGroups)) def setter(self: Chain, value): self._wrappedData.chainGroups = [x._wrappedData for x in value] # Chain.complexes = property(getter, setter, None, "Complexes that contain Chain") del getter del setter # Extra Notifiers to notify changes in Chain-Complex link className = ApiChainGroup._metaclass.qualifiedName() Project._apiNotifiers.extend( (('_modifiedLink', {'classNames': ('Chain', 'Complex')}, className, 'addChain'), ('_modifiedLink', {'classNames': ('Chain', 'Complex')}, className, 'removeChain'), ('_modifiedLink', {'classNames': ('Chain', 'Complex')}, className, 'setChains'), ) ) className = ApiChain._metaclass.qualifiedName() Project._apiNotifiers.extend( (('_modifiedLink', {'classNames': ('Chain', 'Complex')}, className, 'addChainGroup'), ('_modifiedLink', {'classNames': ('Chain', 'Complex')}, className, 'removeChainGroup'), ('_modifiedLink', {'classNames': ('Chain', 'Complex')}, className, 'setChainGroups'), ) )