From 5b22728a2e3bb0f94048f31fbc1fc092808abd7f Mon Sep 17 00:00:00 2001 From: julienmalard Date: Fri, 8 Dec 2023 14:45:34 +0100 Subject: [PATCH] =?UTF-8?q?Reorganizaci=C3=B3n=20general?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- poetry.lock | 93 ++++++++- .../test_orgs/test_resolver_etapas.py" | 0 pyproject.toml | 1 + "tikon/ecs/\303\241rb_coso.py" | 9 +- "tikon/m\303\263ds/cultivo/cult.py" | 2 +- "tikon/m\303\263ds/cultivo/extrn.py" | 2 +- "tikon/m\303\263ds/rae/manejo.py" | 3 +- "tikon/m\303\263ds/rae/orgs/etapa.py" | 188 +++++++++++++++++ "tikon/m\303\263ds/rae/orgs/insectos/ins.py" | 42 +++- .../m\303\263ds/rae/orgs/insectos/paras.py" | 27 ++- "tikon/m\303\263ds/rae/orgs/organismo.py" | 91 +-------- "tikon/m\303\263ds/rae/orgs/plantas/base.py" | 3 +- "tikon/m\303\263ds/rae/red/obs.py" | 2 +- "tikon/m\303\263ds/rae/red/red.py" | 193 ++++++++++++------ "tikon/m\303\263ds/rae/red/res/res.py" | 2 +- 15 files changed, 493 insertions(+), 165 deletions(-) create mode 100644 "pruebas/test_m\303\263ds/test_rae/test_orgs/test_resolver_etapas.py" create mode 100644 "tikon/m\303\263ds/rae/orgs/etapa.py" diff --git a/poetry.lock b/poetry.lock index c456a58e..117e5755 100644 --- a/poetry.lock +++ b/poetry.lock @@ -42,6 +42,27 @@ python-versions = "*" [package.dependencies] pyparsing = ">=2.0.3" +[[package]] +name = "black" +version = "22.8.0" +description = "The uncompromising code formatter." +category = "dev" +optional = false +python-versions = ">=3.6.2" + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_full_version < \"3.11.0a7\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + [[package]] name = "certifi" version = "2022.6.15" @@ -80,6 +101,17 @@ python-versions = ">=3.6.0" [package.extras] unicode_backport = ["unicodedata2"] +[[package]] +name = "click" +version = "8.1.3" +description = "Composable command line interface toolkit" +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.dependencies] +colorama = {version = "*", markers = "platform_system == \"Windows\""} + [[package]] name = "colorama" version = "0.4.5" @@ -318,7 +350,7 @@ reports = ["lxml"] type = "git" url = "https://github.com/python/mypy.git" reference = "HEAD" -resolved_reference = "0f17aff06ac1c05c442ba989e23655a2c6adbfbf" +resolved_reference = "7c14feedd2a6889d9eab8b0ac8dc8aab630bbed3" [[package]] name = "mypy-extensions" @@ -418,6 +450,14 @@ multiprocess = ">=0.70.13" pox = ">=0.3.1" ppft = ">=1.7.6.5" +[[package]] +name = "pathspec" +version = "0.10.1" +description = "Utility library for gitignore style pattern matching of file paths." +category = "dev" +optional = false +python-versions = ">=3.7" + [[package]] name = "pcse" version = "5.5.3" @@ -446,6 +486,18 @@ python-versions = ">=3.7" docs = ["furo", "olefile", "sphinx (>=2.4)", "sphinx-copybutton", "sphinx-issues (>=3.0.1)", "sphinx-removed-in", "sphinxext-opengraph"] tests = ["check-manifest", "coverage", "defusedxml", "markdown2", "olefile", "packaging", "pyroma", "pytest", "pytest-cov", "pytest-timeout"] +[[package]] +name = "platformdirs" +version = "2.5.2" +description = "A small Python module for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +category = "dev" +optional = false +python-versions = ">=3.7" + +[package.extras] +docs = ["furo (>=2021.7.5b38)", "proselint (>=0.10.2)", "sphinx-autodoc-typehints (>=1.12)", "sphinx (>=4)"] +test = ["appdirs (==1.4.4)", "pytest-cov (>=2.7)", "pytest-mock (>=3.6)", "pytest (>=6)"] + [[package]] name = "pluggy" version = "1.0.0" @@ -980,7 +1032,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" [metadata] lock-version = "1.1" python-versions = ">=3.10,<3.12" -content-hash = "b36e5c8c6e0db295a4bfb548c0c9be557cea1a82c484d9e5382de2d06e24865f" +content-hash = "83b8b5f06bb59cae68a8d31b9ee9fa9e267ebc45026f8401b592309df89f228d" [metadata.files] async-generator = [ @@ -999,6 +1051,31 @@ bibtexparser = [ {file = "bibtexparser-1.3.0-py3-none-any.whl", hash = "sha256:d2886789566f2ecbecb5f44309d36ed96162530f29f0b0c07df11630adf17632"}, {file = "bibtexparser-1.3.0.tar.gz", hash = "sha256:971a0840700f5282d61b8c119cd4e208b8e546a91f9043ead4e440734fbf9f5e"}, ] +black = [ + {file = "black-22.8.0-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:ce957f1d6b78a8a231b18e0dd2d94a33d2ba738cd88a7fe64f53f659eea49fdd"}, + {file = "black-22.8.0-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:5107ea36b2b61917956d018bd25129baf9ad1125e39324a9b18248d362156a27"}, + {file = "black-22.8.0-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:e8166b7bfe5dcb56d325385bd1d1e0f635f24aae14b3ae437102dedc0c186747"}, + {file = "black-22.8.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:dd82842bb272297503cbec1a2600b6bfb338dae017186f8f215c8958f8acf869"}, + {file = "black-22.8.0-cp310-cp310-win_amd64.whl", hash = "sha256:d839150f61d09e7217f52917259831fe2b689f5c8e5e32611736351b89bb2a90"}, + {file = "black-22.8.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:a05da0430bd5ced89176db098567973be52ce175a55677436a271102d7eaa3fe"}, + {file = "black-22.8.0-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4a098a69a02596e1f2a58a2a1c8d5a05d5a74461af552b371e82f9fa4ada8342"}, + {file = "black-22.8.0-cp36-cp36m-win_amd64.whl", hash = "sha256:5594efbdc35426e35a7defa1ea1a1cb97c7dbd34c0e49af7fb593a36bd45edab"}, + {file = "black-22.8.0-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a983526af1bea1e4cf6768e649990f28ee4f4137266921c2c3cee8116ae42ec3"}, + {file = "black-22.8.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3b2c25f8dea5e8444bdc6788a2f543e1fb01494e144480bc17f806178378005e"}, + {file = "black-22.8.0-cp37-cp37m-win_amd64.whl", hash = "sha256:78dd85caaab7c3153054756b9fe8c611efa63d9e7aecfa33e533060cb14b6d16"}, + {file = "black-22.8.0-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:cea1b2542d4e2c02c332e83150e41e3ca80dc0fb8de20df3c5e98e242156222c"}, + {file = "black-22.8.0-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:5b879eb439094751185d1cfdca43023bc6786bd3c60372462b6f051efa6281a5"}, + {file = "black-22.8.0-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:0a12e4e1353819af41df998b02c6742643cfef58282915f781d0e4dd7a200411"}, + {file = "black-22.8.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c3a73f66b6d5ba7288cd5d6dad9b4c9b43f4e8a4b789a94bf5abfb878c663eb3"}, + {file = "black-22.8.0-cp38-cp38-win_amd64.whl", hash = "sha256:e981e20ec152dfb3e77418fb616077937378b322d7b26aa1ff87717fb18b4875"}, + {file = "black-22.8.0-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8ce13ffed7e66dda0da3e0b2eb1bdfc83f5812f66e09aca2b0978593ed636b6c"}, + {file = "black-22.8.0-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:32a4b17f644fc288c6ee2bafdf5e3b045f4eff84693ac069d87b1a347d861497"}, + {file = "black-22.8.0-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:0ad827325a3a634bae88ae7747db1a395d5ee02cf05d9aa7a9bd77dfb10e940c"}, + {file = "black-22.8.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:53198e28a1fb865e9fe97f88220da2e44df6da82b18833b588b1883b16bb5d41"}, + {file = "black-22.8.0-cp39-cp39-win_amd64.whl", hash = "sha256:bc4d4123830a2d190e9cc42a2e43570f82ace35c3aeb26a512a2102bce5af7ec"}, + {file = "black-22.8.0-py3-none-any.whl", hash = "sha256:d2c21d439b2baf7aa80d6dd4e3659259be64c6f49dfd0f32091063db0e006db4"}, + {file = "black-22.8.0.tar.gz", hash = "sha256:792f7eb540ba9a17e8656538701d3eb1afcb134e3b45b71f20b25c77a8db7e6e"}, +] certifi = [ {file = "certifi-2022.6.15-py3-none-any.whl", hash = "sha256:fe86415d55e84719d75f8b69414f6438ac3547d2078ab91b67e779ef69378412"}, {file = "certifi-2022.6.15.tar.gz", hash = "sha256:84c85a9078b11105f04f3036a9482ae10e4621616db313fe045dd24743a0820d"}, @@ -1077,6 +1154,10 @@ charset-normalizer = [ {file = "charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"}, {file = "charset_normalizer-2.1.1-py3-none-any.whl", hash = "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"}, ] +click = [ + {file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"}, + {file = "click-8.1.3.tar.gz", hash = "sha256:7682dc8afb30297001674575ea00d1814d808d6a36af415a82bd481d37ba7b8e"}, +] colorama = [ {file = "colorama-0.4.5-py2.py3-none-any.whl", hash = "sha256:854bf444933e37f5824ae7bfc1e98d5bce2ebe4160d46b5edf346a89358e99da"}, {file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"}, @@ -1473,6 +1554,10 @@ pathos = [ {file = "pathos-0.2.9-py3-none-any.whl", hash = "sha256:1c44373d8692897d5d15a8aa3b3a442ddc0814c5e848f4ff0ded5491f34b1dac"}, {file = "pathos-0.2.9.tar.gz", hash = "sha256:a8dbddcd3d9af32ada7c6dc088d845588c513a29a0ba19ab9f64c5cd83692934"}, ] +pathspec = [ + {file = "pathspec-0.10.1-py3-none-any.whl", hash = "sha256:46846318467efc4556ccfd27816e004270a9eeeeb4d062ce5e6fc7a87c573f93"}, + {file = "pathspec-0.10.1.tar.gz", hash = "sha256:7ace6161b621d31e7902eb6b5ae148d12cfd23f4a249b9ffb6b9fee12084323d"}, +] pcse = [ {file = "PCSE-5.5.3.tar.gz", hash = "sha256:145facfaba412b85678d7e076eae7c1f17f1ff0bf3a6f7209ceb52b89d304b12"}, ] @@ -1536,6 +1621,10 @@ pillow = [ {file = "Pillow-9.2.0-pp38-pypy38_pp73-win_amd64.whl", hash = "sha256:0030fdbd926fb85844b8b92e2f9449ba89607231d3dd597a21ae72dc7fe26927"}, {file = "Pillow-9.2.0.tar.gz", hash = "sha256:75e636fd3e0fb872693f23ccb8a5ff2cd578801251f3a4f6854c6a5d437d3c04"}, ] +platformdirs = [ + {file = "platformdirs-2.5.2-py3-none-any.whl", hash = "sha256:027d8e83a2d7de06bbac4e5ef7e023c02b863d7ea5d079477e722bb41ab25788"}, + {file = "platformdirs-2.5.2.tar.gz", hash = "sha256:58c8abb07dcb441e6ee4b11d8df0ac856038f944ab98b7be6b27b2a3c7feef19"}, +] pluggy = [ {file = "pluggy-1.0.0-py2.py3-none-any.whl", hash = "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3"}, {file = "pluggy-1.0.0.tar.gz", hash = "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159"}, diff --git "a/pruebas/test_m\303\263ds/test_rae/test_orgs/test_resolver_etapas.py" "b/pruebas/test_m\303\263ds/test_rae/test_orgs/test_resolver_etapas.py" new file mode 100644 index 00000000..e69de29b diff --git a/pyproject.toml b/pyproject.toml index 719aa969..d7c5f71e 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -34,6 +34,7 @@ frozendict = "^2.3.4" mypy = {git = "https://github.com/python/mypy.git"} data-science-types = "^0.2.23" pyright = "^1.1.270" +black = "^22.8.0" [tool.mypy] plugins = "numpy.typing.mypy_plugin" diff --git "a/tikon/ecs/\303\241rb_coso.py" "b/tikon/ecs/\303\241rb_coso.py" index cdfa0a22..4c7cf72c 100644 --- "a/tikon/ecs/\303\241rb_coso.py" +++ "b/tikon/ecs/\303\241rb_coso.py" @@ -2,10 +2,11 @@ from typing import Dict from .dists import DistAnalítica, ManejadorDists +from ..central import Coso class PlantillaRamaEcCoso(dict): - def __init__(símismo, pariente, ramas, coso): + def __init__(símismo, pariente, ramas, coso: Coso): símismo.coso = coso símismo.nombre = pariente.nombre símismo.pariente = pariente @@ -21,7 +22,7 @@ def de_dic(símismo, dic): if rm in dic: símismo[rm].de_dic(dic[rm]) - def borrar_calib(símismo, nombre): + def borrar_calib(símismo, nombre: str): for rm in símismo: símismo[rm].borrar_calib(nombre) @@ -68,7 +69,7 @@ def desactivar_ec(símismo, categ, subcateg=None): class CategEcCoso(PlantillaRamaEcCoso): - def activar_ec(símismo, subcateg, ec): + def activar_ec(símismo, subcateg: str, ec: str): símismo[subcateg].activar_ec(ec) def desactivar_ec(símismo, subcateg=None): @@ -91,7 +92,7 @@ def __init__(símismo, pariente, ramas, coso): def activa(símismo, modelo, mód, exper, coso): return símismo.ec_activa().activa(modelo, mód, exper, coso) - def activar_ec(símismo, ec): + def activar_ec(símismo, ec: str): try: obj_ec = símismo[ec] except KeyError: diff --git "a/tikon/m\303\263ds/cultivo/cult.py" "b/tikon/m\303\263ds/cultivo/cult.py" index a8c885e8..aaf6b74e 100644 --- "a/tikon/m\303\263ds/cultivo/cult.py" +++ "b/tikon/m\303\263ds/cultivo/cult.py" @@ -21,7 +21,7 @@ def __init__(símismo, mód, simul_exper, ecs, vars_interés): raise ErrorRequísitos('Falta módulo RedAE requerido por Cultivo.') símismo.mód_red = simul_exper.modelo[RedAE.nombre] - símismo.orgs = [org for org in símismo.mód_red.orgs if isinstance(org, CultivoExterno)] + símismo.orgs = [org for org in símismo.mód_red.organismos if isinstance(org, CultivoExterno)] símismo.etapas = [etp for org in símismo.orgs for etp in org] super().__init__(mód=mód, simul_exper=simul_exper, ecs=ecs, vars_interés=vars_interés) diff --git "a/tikon/m\303\263ds/cultivo/extrn.py" "b/tikon/m\303\263ds/cultivo/extrn.py" index 40128b00..ce687344 100644 --- "a/tikon/m\303\263ds/cultivo/extrn.py" +++ "b/tikon/m\303\263ds/cultivo/extrn.py" @@ -59,7 +59,7 @@ def obt_org(símismo, cultivo, variedad=None): orgs_potenciales = símismo.parcelas.conv_cultivos[cultivo] else: _cls_apropriada = _cls_cultivos[cultivo.lower()] - orgs_potenciales = [org for org in símismo.sim.orgs if isinstance(org, _cls_apropriada)] + orgs_potenciales = [org for org in símismo.sim.organismos if isinstance(org, _cls_apropriada)] if variedad: return next( (clt for clt in orgs_potenciales if clt.variedad and clt.variedad.lower() == variedad.lower()), diff --git "a/tikon/m\303\263ds/rae/manejo.py" "b/tikon/m\303\263ds/rae/manejo.py" index a275cd7a..361c537b 100644 --- "a/tikon/m\303\263ds/rae/manejo.py" +++ "b/tikon/m\303\263ds/rae/manejo.py" @@ -2,7 +2,8 @@ from tikon.móds.manejo.acciones import Acción from tikon.móds.manejo.conds import CondVariable from tikon.móds.rae.utils import RES_POBS, EJE_ETAPA -from .orgs.organismo import Etapa, SumaEtapas, Organismo +from .orgs.organismo import Organismo +from .orgs.etapa import Etapa, SumaEtapas from .red import RedAE diff --git "a/tikon/m\303\263ds/rae/orgs/etapa.py" "b/tikon/m\303\263ds/rae/orgs/etapa.py" new file mode 100644 index 00000000..e2d3f4ad --- /dev/null +++ "b/tikon/m\303\263ds/rae/orgs/etapa.py" @@ -0,0 +1,188 @@ +from typing import Iterable + +from tikon.central import Coso +from tikon.central.coso import SumaCosos +from tikon.móds.rae.orgs.ecs import EcsOrgs +from tikon.móds.rae.orgs.ecs.utils import ECS_EDAD +from tikon.móds.rae.orgs.organismo import categs_parás, Organismo + + +class Etapa(Coso): + def __init__(símismo, nombre, org): + super().__init__(nombre, EcsOrgs) + + if ":" in símismo.nombre: + raise ValueError('Un nombre de etapa no puede contener el carácter ":".') + + símismo.org = org + + @property + def índices_inter(símismo): + return [str(símismo.org), str(símismo)] + + def con_cohortes(símismo, exper): + return símismo.categ_activa(ECS_EDAD, modelo=None, mód=None, exper=exper) + + def siguiente(símismo): + índice = símismo.org.índice(símismo) + if índice < (len(símismo.org) - 1): + return símismo.org[índice + 1] + + def __add__(símismo, otro): + return SumaEtapas([símismo, otro]) + + def __str__(símismo): + return str(símismo.org) + " : " + símismo.nombre + + def __eq__(símismo, otro): + return ( + isinstance(otro, símismo.__class__) + and símismo.nombre == otro.nombre + and símismo.org == otro.org + ) + + def __hash__(símismo): + return hash(str(símismo)) + + +class EtapaFantasma(Etapa): + def __init__(símismo, org, etp, org_hués, etp_hués, sig): + nombre = f"{etp.nombre} en {etp_hués.org}, {etp_hués.nombre}" + super().__init__(nombre, org) + + símismo.etp_espejo = etp + símismo.org_hués = org_hués + símismo.etp_hués = etp_hués + símismo.sig = sig + + símismo._vincular_ecs() + + def _vincular_ecs(símismo): + + if isinstance(símismo.sig, EtapaFantasma): + categs_de_prs = [] + else: + categs_de_prs = categs_parás + categs_de_hués = [ + str(ctg) for ctg in símismo.ecs if str(ctg) not in categs_de_prs + ] + + for ctg in categs_de_hués: + símismo.ecs[ctg] = símismo.etp_hués.ecs[ctg] + + for ctg in categs_de_prs: + símismo.ecs[ctg] = símismo.etp_espejo.ecs[ctg] + + def siguiente(símismo): + return símismo.sig + + +class EspecificaciónEtapas(object): + def resolver(símismo, etapas: list[Etapa]) -> Iterable[Etapa]: + for e in etapas: + if símismo.concuerda(e): + yield e + + def concuerda(símismo, etapa: Etapa) -> bool: + raise NotImplementedError + + @staticmethod + def _iter_etapas_huéspedes(etapa: EtapaFantasma) -> Iterable[Etapa]: + etapa_huésped = etapa.etp_hués + if isinstance(etapa_huésped, EtapaFantasma): + return EspecificaciónEtapas._iter_etapas_huéspedes(etapa_huésped) + else: + yield etapa_huésped + + +class EspecificaciónEtapasOrganismo(EspecificaciónEtapas): + def __init__(símismo, organismo: Organismo, incluir_parasitadas: bool): + símismo.organismo = organismo + símismo.incluir_parasitadas = incluir_parasitadas + + def concuerda(símismo, etapa: Etapa) -> bool: + if isinstance(etapa, EtapaFantasma): + if not símismo.incluir_parasitadas: + return False + for h in símismo._iter_etapas_huéspedes(etapa): + if h.org == símismo.organismo: + return True + return False + else: + return etapa.org == símismo.organismo + + +class EspecificaciónEtapaPorNombre(EspecificaciónEtapasOrganismo): + def __init__( + símismo, organismo: Organismo, nombre_etapa: str, incluir_parasitadas: bool + ): + super().__init__(organismo=organismo, incluir_parasitadas=incluir_parasitadas) + símismo.nombre_etapa = nombre_etapa + + def concuerda(símismo, etapa: Etapa) -> bool: + def _obt_huésped_base(f: EtapaFantasma) -> [Organismo, Etapa]: + etapa_huésped = f.etp_hués + if isinstance(etapa_huésped, EtapaFantasma): + return _obt_huésped_base(etapa_huésped) + else: + return etapa_huésped.org, etapa_huésped + + if isinstance(etapa, EtapaFantasma): + if not símismo.incluir_parasitadas: + return False + for h in símismo._iter_etapas_huéspedes(etapa): + if h.org == símismo.organismo and símismo.nombre_concuerda(h.nombre): + return True + return False + else: + return etapa.org == símismo.organismo and símismo.nombre_concuerda(etapa.nombre) + + def nombre_concuerda(símismo, nombre: str) -> bool: + return nombre == símismo.nombre_etapa + + +class EspecificaciónEtapaPorPrincipioNombre(EspecificaciónEtapaPorNombre): + def nombre_concuerda(símismo, nombre: str) -> bool: + return símismo.nombre_etapa.startswith(nombre) + + +class EspecificaciónEtapasEtapa(EspecificaciónEtapas): + def __init__(símismo, etapa: Etapa, incluir_parasitadas: bool): + símismo.etapa = etapa + símismo.incluir_parasitadas = incluir_parasitadas + + def concuerda(símismo, etapa: Etapa) -> bool: + if isinstance(etapa, EtapaFantasma): + if not símismo.incluir_parasitadas: + return False + return any( + e == símismo.etapa for e in símismo._iter_etapas_huéspedes(etapa) + ) + return etapa == símismo.etapa + + +ResolvableAEtapas = Organismo | Etapa | EspecificaciónEtapas + + +def generar_especificación_etapas( + criterio: ResolvableAEtapas, incluir_parasitadas: bool +) -> EspecificaciónEtapas: + if isinstance(criterio, EspecificaciónEtapas): + return criterio + elif isinstance(criterio, Organismo): + return EspecificaciónEtapasOrganismo( + organismo=criterio, incluir_parasitadas=incluir_parasitadas + ) + elif isinstance(criterio, Etapa): + return EspecificaciónEtapasEtapa( + etapa=criterio, incluir_parasitadas=incluir_parasitadas + ) + raise TypeError(criterio) + + +class SumaEtapas(SumaCosos): + def __add__(símismo, otro): + if isinstance(otro, Etapa): + return SumaEtapas([otro, *símismo.cosos]) + else: + return SumaEtapas(*list(otro), *símismo.cosos) diff --git "a/tikon/m\303\263ds/rae/orgs/insectos/ins.py" "b/tikon/m\303\263ds/rae/orgs/insectos/ins.py" index 273e73d1..beca79f2 100644 --- "a/tikon/m\303\263ds/rae/orgs/insectos/ins.py" +++ "b/tikon/m\303\263ds/rae/orgs/insectos/ins.py" @@ -1,6 +1,10 @@ -from ..organismo import Organismo, Etapa +from ..etapa import Etapa, EspecificaciónEtapaPorNombre, EspecificaciónEtapas, EspecificaciónEtapaPorPrincipioNombre +from ..organismo import Organismo +HUEVO = "huevo" JUVENIL = 'juvenil' +PUPA = "pupa" +ADULTO = "adulto" class Insecto(Organismo): @@ -17,7 +21,7 @@ def __init__(símismo, nombre, huevo=False, njuvenil=0, pupa=False, adulto=True, # Añadir las etapas etapas = [] if huevo: - etapas.append('huevo') + etapas.append(HUEVO) if njuvenil < 0: raise ValueError('El número de juveniles no puede ser inferior a 0.') @@ -34,14 +38,44 @@ def __init__(símismo, nombre, huevo=False, njuvenil=0, pupa=False, adulto=True, etapas.append(nombre_juv) if pupa: - etapas.append('pupa') + etapas.append(PUPA) if adulto: - etapas.append('adulto') + etapas.append(ADULTO) super().__init__(nombre=nombre, etapas=etapas) símismo.activar_ecs(tipo_ecs) + def huevo(símismo, incluir_parasitadas=True) -> EspecificaciónEtapas: + return EspecificaciónEtapaPorNombre( + organismo=símismo, + nombre_etapa=HUEVO, + incluir_parasitadas=incluir_parasitadas + ) + + def juveniles(símismo, incluir_parasitadas=True) -> EspecificaciónEtapas: + + # Incluiremos todas las etapas empezando por JUVENIL + return EspecificaciónEtapaPorPrincipioNombre( + organismo=símismo, + nombre_etapa=JUVENIL, + incluir_parasitadas=incluir_parasitadas + ) + + def pupa(símismo, incluir_parasitadas=True) -> EspecificaciónEtapas: + return EspecificaciónEtapaPorNombre( + organismo=símismo, + nombre_etapa=PUPA, + incluir_parasitadas=incluir_parasitadas + ) + + def adulto(símismo, incluir_parasitadas=True) -> EspecificaciónEtapas: + return EspecificaciónEtapaPorNombre( + organismo=símismo, + nombre_etapa=ADULTO, + incluir_parasitadas=incluir_parasitadas + ) + def resolver_etapas(símismo, etapas): if isinstance(etapas, (str, Etapa)): etapas = [etapas] diff --git "a/tikon/m\303\263ds/rae/orgs/insectos/paras.py" "b/tikon/m\303\263ds/rae/orgs/insectos/paras.py" index d756a386..62be1050 100644 --- "a/tikon/m\303\263ds/rae/orgs/insectos/paras.py" +++ "b/tikon/m\303\263ds/rae/orgs/insectos/paras.py" @@ -1,7 +1,7 @@ from tikon.móds.rae.orgs.ecs.utils import ECS_CREC, ECS_DEPR, ECS_EDAD, ECS_MRTE, ECS_MOV, ECS_REPR, ECS_TRANS from .ins import Insecto, JUVENIL -from ..organismo import Etapa +from ..etapa import Etapa, EspecificaciónEtapas, EtapaFantasma, generar_especificación_etapas, ResolvableAEtapas class EtapaJuvenilParasitoide(Etapa): @@ -89,12 +89,17 @@ def parasita(símismo, huésped, etps_entra=None, etp_emerg=None, etp_símismo=' siguiente = (etps_entra if isinstance(etps_entra, Etapa) else etps_entra[-1]).siguiente() etp_emerg = siguiente or huésped.etapas[-1] - # if etp_emerg.index(): - # raise ValueError('') super().parasita( huésped=huésped, etp_símismo=etp_símismo, etps_entra=etps_entra, etp_emerg=etp_emerg, etp_recip=etp_recip ) + def juveniles_en(símismo, huésped: ResolvableAEtapas, incluir_parasitadas=True) -> EspecificaciónEtapas: + return EspecificaciónEtapasParasitadas( + parasitoide=símismo, + huésped=huésped, + incluir_parasitadas=incluir_parasitadas + ) + def _gen_etapa(símismo, etp): if etp == JUVENIL: return EtapaJuvenilParasitoide(etp, símismo) @@ -150,3 +155,19 @@ def captura(símismo, presa, etps_presa=None): """ etps_presa = etps_presa or presa['juvenil'] símismo.secome(presa=presa, etps_presa=etps_presa, etps_símismo='adulto') + + +class EspecificaciónEtapasParasitadas(EspecificaciónEtapas): + def __init__(símismo, + parasitoide: Parasitoide, + huésped: ResolvableAEtapas, + incluir_parasitadas: bool + ): + símismo.parasitoide = parasitoide + símismo.huésped = generar_especificación_etapas(huésped, incluir_parasitadas) + + def concuerda(símismo, etapa: Etapa) -> bool: + if isinstance(etapa, EtapaFantasma): + if etapa.org is símismo.parasitoide and símismo.huésped.concuerda(etapa.etp_hués): + return True + return False diff --git "a/tikon/m\303\263ds/rae/orgs/organismo.py" "b/tikon/m\303\263ds/rae/orgs/organismo.py" index f48aec90..66cb7ef9 100644 --- "a/tikon/m\303\263ds/rae/orgs/organismo.py" +++ "b/tikon/m\303\263ds/rae/orgs/organismo.py" @@ -1,9 +1,10 @@ from itertools import product from typing import Union, Iterable -from tikon.central.coso import Coso, SumaCosos +from tikon.central.coso import Coso from .ecs import EcsOrgs from .ecs.utils import ECS_EDAD, ECS_MRTE, ECS_TRANS, ECS_ESTOC +from .etapa import Etapa, EtapaFantasma from ..utils import contexto @@ -141,84 +142,9 @@ def __contains__(símismo, itema): return any(itema is e for e in símismo.etapas) -class Etapa(Coso): - def __init__(símismo, nombre, org): - super().__init__(nombre, EcsOrgs) - - if ':' in símismo.nombre: - raise ValueError('Un nombre de etapa no puede contener el carácter ":".') - - símismo.org = org - - @property - def índices_inter(símismo): - return [str(símismo.org), str(símismo)] - - def presas(símismo): - return símismo.org.presas(símismo) - - def huéspedes(símismo): - return símismo.org.huéspedes(símismo) - - def con_cohortes(símismo, exper): - return símismo.categ_activa(ECS_EDAD, modelo=None, mód=símismo, exper=exper) - - def siguiente(símismo): - índice = símismo.org.índice(símismo) - if índice < (len(símismo.org) - 1): - return símismo.org[índice + 1] - - def __add__(símismo, otro): - return SumaEtapas([símismo, otro]) - - def __str__(símismo): - return str(símismo.org) + ' : ' + símismo.nombre - - def __eq__(símismo, otro): - return isinstance(otro, símismo.__class__) and símismo.nombre == otro.nombre and símismo.org == otro.org - - def __hash__(símismo): - return hash(str(símismo)) - - categs_parás = [ECS_TRANS, ECS_EDAD, ECS_MRTE, ECS_ESTOC] -class EspecificaciónEtapa(object): - def resolver(símismo, etapas) -> list[Etapa]: - raise NotImplementedError - - -class EtapaFantasma(Etapa): - def __init__(símismo, org, etp, org_hués, etp_hués, sig): - nombre = f'{etp.nombre} en {etp_hués.org}, {etp_hués.nombre}' - super().__init__(nombre, org) - - símismo.etp_espejo = etp - símismo.org_hués = org_hués - símismo.etp_hués = etp_hués - símismo.sig = sig - - símismo._vincular_ecs() - - def _vincular_ecs(símismo): - - if isinstance(símismo.sig, EtapaFantasma): - categs_de_prs = [] - else: - categs_de_prs = categs_parás - categs_de_hués = [str(ctg) for ctg in símismo.ecs if str(ctg) not in categs_de_prs] - - for ctg in categs_de_hués: - símismo.ecs[ctg] = símismo.etp_hués.ecs[ctg] - - for ctg in categs_de_prs: - símismo.ecs[ctg] = símismo.etp_espejo.ecs[ctg] - - def siguiente(símismo): - return símismo.sig - - class RelaciónOrgs(object): def __init__(símismo, orgs): símismo.orgs = orgs @@ -259,16 +185,3 @@ def __init__(símismo, huésped, etps_entra, etp_depred, etp_emerg, etp_recip): símismo.fantasmas.reverse() super().__init__([huésped, etp_depred.org]) - - -class SumaEtapas(SumaCosos): - - def __add__(símismo, otro): - if isinstance(otro, Etapa): - return SumaEtapas([otro, *símismo.cosos]) - else: - return SumaEtapas(*list(otro), *símismo.cosos) - - -Tipo_Similar_A_Etapa = Union[Organismo, Etapa, SumaCosos, EspecificaciónEtapa, str] -Tipo_Resolvable_A_Etapas = Union[Tipo_Similar_A_Etapa, Iterable[Tipo_Similar_A_Etapa]] diff --git "a/tikon/m\303\263ds/rae/orgs/plantas/base.py" "b/tikon/m\303\263ds/rae/orgs/plantas/base.py" index 6e248673..5896cbf0 100644 --- "a/tikon/m\303\263ds/rae/orgs/plantas/base.py" +++ "b/tikon/m\303\263ds/rae/orgs/plantas/base.py" @@ -1,7 +1,8 @@ from tikon.móds.rae.orgs.ecs.utils import ECS_CREC, ECS_DEPR, ECS_EDAD, ECS_MOV, ECS_MRTE, ECS_TRANS, ECS_REPR, \ ECS_ESTOC -from ..organismo import Organismo, Etapa +from ..organismo import Organismo +from ..etapa import Etapa class Planta(Organismo): diff --git "a/tikon/m\303\263ds/rae/red/obs.py" "b/tikon/m\303\263ds/rae/red/obs.py" index 0ea6235a..86449449 100644 --- "a/tikon/m\303\263ds/rae/red/obs.py" +++ "b/tikon/m\303\263ds/rae/red/obs.py" @@ -4,7 +4,7 @@ from tikon.utils import EJE_PARC, EJE_DEST from .red import RedAE -from ..orgs.organismo import Etapa +from ..orgs.etapa import Etapa class ObsRAE(Obs): diff --git "a/tikon/m\303\263ds/rae/red/red.py" "b/tikon/m\303\263ds/rae/red/red.py" index a68d4b06..19bc5e71 100644 --- "a/tikon/m\303\263ds/rae/red/red.py" +++ "b/tikon/m\303\263ds/rae/red/red.py" @@ -8,13 +8,21 @@ from tikon.central.simul import SimulMódulo from tikon.consts import CORREO_AUTOR from tikon.ecs.paráms import Inter -from tikon.móds.rae.utils import RES_POBS, RES_COHORTES, EJE_COH, EJE_ETAPA, EJE_VÍCTIMA, contexto +from tikon.móds.rae.utils import ( + RES_POBS, + RES_COHORTES, + EJE_COH, + EJE_ETAPA, + EJE_VÍCTIMA, + contexto, +) from .res.cohortes import ResCohortes from ..orgs.ecs import EcsOrgs from ..orgs.ecs.utils import ECS_TRANS, ECS_REPR +from ..orgs.etapa import Etapa, EtapaFantasma from ..orgs.insectos import Parasitoide from ..orgs.insectos.paras import EtapaJuvenilParasitoide -from ..orgs.organismo import EtapaFantasma, Etapa, Organismo +from ..orgs.organismo import Organismo from ..red.res import res as res_red @@ -24,100 +32,159 @@ class ErrorParasitismoCircular(ValueError): class SimulRed(SimulMódulo): resultados = [ - res_red.ResPobs, res_red.ResEdad, res_red.ResCrec, res_red.ResDepred, res_red.ResRepr, - res_red.ResMuerte, res_red.ResTrans, res_red.ResMov, res_red.ResEstoc, ResCohortes + res_red.ResPobs, + res_red.ResEdad, + res_red.ResCrec, + res_red.ResDepred, + res_red.ResRepr, + res_red.ResMuerte, + res_red.ResTrans, + res_red.ResMov, + res_red.ResEstoc, + ResCohortes, ] def __init__(símismo, mód: "RedAE", simul_exper, ecs, vars_interés): símismo.etapas = mód.etapas - símismo.orgs = mód.orgs + símismo.orgs = mód.organismos presas = mód.presas() símismo.víctimas = presas.union({h for h in mód.huéspedes() if h not in presas}) modelo = simul_exper.modelo exper = simul_exper.exper - símismo.recip_repr = tuple([ - id(etp.org[0]) for etp in símismo.etapas if etp.categ_activa(ECS_REPR, modelo, mód=mód, exper=exper) - ]) - - símismo.recip_trans = tuple([ - (id(etp), id(etp.siguiente())) for etp in símismo.etapas - if etp.categ_activa(ECS_TRANS, modelo, mód=mód, exper=exper) and etp.siguiente() - ]) + símismo.recip_repr = tuple( + [ + id(etp.org[0]) + for etp in símismo.etapas + if etp.categ_activa(ECS_REPR, modelo, mód=mód, exper=exper) + ] + ) + + símismo.recip_trans = tuple( + [ + (id(etp), id(etp.siguiente())) + for etp in símismo.etapas + if etp.categ_activa(ECS_TRANS, modelo, mód=mód, exper=exper) + and etp.siguiente() + ] + ) símismo.parás_hués = [] for etp in símismo.etapas: - if isinstance(etp.org, Parasitoide) and etp.nombre == 'adulto': + if isinstance(etp.org, Parasitoide) and etp.nombre == "adulto": huéspedes = list(mód.huéspedes(etp)) - fantasmas = sorted(mód.fantasmas(huéspedes, paras=etp), key=lambda x: huéspedes.index(x.etp_hués)) - símismo.parás_hués.append((id(etp), (tuple(id(h) for h in huéspedes), tuple(id(f) for f in fantasmas)))) + fantasmas = sorted( + mód.fantasmas(huéspedes, paras=etp), + key=lambda x: huéspedes.index(x.etp_hués), + ) + símismo.parás_hués.append( + ( + id(etp), + ( + tuple(id(h) for h in huéspedes), + tuple(id(f) for f in fantasmas), + ), + ) + ) # Índices para luego poder encontrar las interacciones entre parasitoides y víctimas en las matrices de # depredación - depredadores = tuple(id(etp) for etp in símismo.etapas if mód.presas(etp) or mód.huéspedes(etp)) - símismo.máscara_parás = Datos(False, dims=[EJE_ETAPA, EJE_VÍCTIMA], coords=frozendict({ - EJE_ETAPA: depredadores, EJE_VÍCTIMA: tuple(id(v) for v in símismo.víctimas) - })) + depredadores = tuple( + id(etp) for etp in símismo.etapas if mód.presas(etp) or mód.huéspedes(etp) + ) + símismo.máscara_parás = Datos( + False, + dims=[EJE_ETAPA, EJE_VÍCTIMA], + coords=frozendict( + { + EJE_ETAPA: depredadores, + EJE_VÍCTIMA: tuple(id(v) for v in símismo.víctimas), + } + ), + ) for paras, hués_fants in símismo.parás_hués: - símismo.máscara_parás.loc[frozendict({EJE_ETAPA: paras, EJE_VÍCTIMA: hués_fants[0]})] = True + símismo.máscara_parás.loc[ + frozendict({EJE_ETAPA: paras, EJE_VÍCTIMA: hués_fants[0]}) + ] = True - super().__init__(mód, simul_exper=simul_exper, ecs=ecs, vars_interés=vars_interés) + super().__init__( + mód, simul_exper=simul_exper, ecs=ecs, vars_interés=vars_interés + ) def poner_valor(símismo, var, val, rel=False): if var == RES_POBS: cambio = val if rel else val - símismo[RES_POBS].datos símismo[RES_COHORTES].ajustar(cambio) - super().poner_valor(var, val, rel) # **Debe** venir después de ajustar cohortes sino `rel` no funciona + super().poner_valor( + var, val, rel + ) # **Debe** venir después de ajustar cohortes sino `rel` no funciona def verificar_estado(símismo): super().verificar_estado() - mnsg = '\tSi acabas de agregar nuevas ecuaciones, es probablemente culpa tuya.' \ - '\n\tSino, es culpa mía, avísame al {correo}.'.format(correo=CORREO_AUTOR) + mnsg = ( + "\tSi acabas de agregar nuevas ecuaciones, es probablemente culpa tuya." + f"\n\tSino, es culpa mía, avísame al {CORREO_AUTOR}." + ) pobs = símismo[RES_POBS].datos if símismo[RES_COHORTES].activa: - pobs_coh = símismo[RES_COHORTES].datos[{'comp': 0}] - if np.any(~np.array_equal(pobs_coh.matr, np.round(pobs_coh.matr))): - raise ValueError('Población de cohorte fraccional.\n{mnsg}'.format(mnsg=mnsg)) - pobs_corresp_coh = pobs.loc[frozendict({EJE_ETAPA: pobs_coh.coords_internas[EJE_ETAPA]})] - if np.any(np.array_equal(pobs_coh.suma(dim=EJE_COH).matr, pobs_corresp_coh.matr)): - raise ValueError('Población de cohorte no suma a población total.\n{mnsg}'.format(mnsg=mnsg)) + pobs_coh = símismo[RES_COHORTES].datos[{"comp": 0}] + if ~np.array_equal(pobs_coh.matr, np.round(pobs_coh.matr)): + raise ValueError( + f"Población de cohorte fraccional.\n{mnsg}" + ) + pobs_corresp_coh = pobs.loc[ + frozendict({EJE_ETAPA: pobs_coh.coords_internas[EJE_ETAPA]}) + ] + if ~np.array_equal(pobs_coh.suma(dim=EJE_COH).matr, pobs_corresp_coh.matr): + raise ValueError( + f"Población de cohorte no suma a población total.\n{mnsg}" + ) class RedAE(Módulo): - nombre = 'red' + nombre = "red" cls_simul = SimulRed eje_coso = EJE_ETAPA def __init__(símismo, orgs: Iterable[Organismo] = None): super().__init__(cosos=orgs) - símismo.relaciones = {'presa': [], 'paras': []} + símismo.relaciones = {"presa": [], "paras": []} @property def etapas(símismo): - etps_fant = [fnt for r_p in símismo.relaciones['paras'] for fnt in r_p.fantasmas] + para_resolver = [etp for org in símismo for etp in símismo[org].etapas] + etapas = [] + + + while len(para_resolver): + resolvables = [] # Para hacer + + if not resolvables: + raise ErrorParasitismoCircular + resueltas = [] # Para hacer + etapas += resueltas + + return etapas + + etps_fant = [ + fnt for r_p in símismo.relaciones["paras"] for fnt in r_p.fantasmas + ] return [etp for org in símismo for etp in símismo[org].etapas] + etps_fant @property - def orgs(símismo): + def organismos(símismo): return [símismo[org] for org in símismo] - def añadir_org(símismo, org): - símismo._cosos[str(org)] = org - - def quitar_org(símismo, org): - try: - símismo._cosos.pop(str(org)) - except KeyError: - raise KeyError('El organismo "{org}" no existía en la red "{r}".'.format(org=org, r=símismo)) - def agregar_relación(símismo, relación): - for org in relación.orgs: - if org not in símismo.orgs: - raise ValueError('Organismo "{org}" no existe en red "{r}".'.format(org=org, r=símismo)) + for org in relación.organismos: + if org not in símismo.organismos: + raise ValueError( + f'Organismo "{org}" no existe en red "{símismo}".' + ) símismo.relaciones[relación.tipo].append(relación) def inter(símismo, modelo, coso, tipo: str): @@ -127,10 +194,10 @@ def inter(símismo, modelo, coso, tipo: str): etps_inter = set() coords = set() for tp in tipo: - if tp == 'presa': + if tp == "presa": etps_inter.update(símismo.presas(coso)) coords.update(símismo.presas()) - elif tp == 'huésped': + elif tp == "huésped": etps_inter.update(símismo.huéspedes(coso)) coords.update(símismo.huéspedes()) else: @@ -142,12 +209,17 @@ def presas(símismo, depred=None, presa=None): presa = símismo._resolver_etapas(presa) depred = símismo._resolver_etapas(depred) presas = { - rel.etp_presa for rel in símismo.relaciones['presa'] + rel.etp_presa + for rel in símismo.relaciones["presa"] if rel.etp_presa in presa and rel.etp_depred in depred } # Incluir los fantasmas de las presas - fants_presas = {etp for etp in símismo.etapas if isinstance(etp, EtapaFantasma) and etp.etp_hués in presas} + fants_presas = { + etp + for etp in símismo.etapas + if isinstance(etp, EtapaFantasma) and etp.etp_hués in presas + } return presas.union(fants_presas) @@ -156,8 +228,11 @@ def huéspedes(símismo, paras=None, hués=None): paras = símismo._resolver_etapas(paras) hués = símismo._resolver_etapas(hués) etps = { - hsp for rel in símismo.relaciones['paras'] if rel.etp_depred in paras - for hsp in rel.etps_entra if hsp in hués + hsp + for rel in símismo.relaciones["paras"] + if rel.etp_depred in paras + for hsp in rel.etps_entra + if hsp in hués } for etp in set(etps): @@ -170,12 +245,16 @@ def huéspedes(símismo, paras=None, hués=None): def fantasmas(símismo, hués=None, paras=None): hués = símismo._resolver_etapas(hués) paras = [ - prs for prs in símismo._resolver_etapas(paras) - if isinstance(prs.org, Parasitoide) and prs.nombre == 'adulto' + prs + for prs in símismo._resolver_etapas(paras) + if isinstance(prs.org, Parasitoide) and prs.nombre == "adulto" ] return [ - fnt for rel in símismo.relaciones['paras'] if rel.etp_depred in paras - for fnt in rel.fantasmas if fnt.etp_hués in hués + fnt + for rel in símismo.relaciones["paras"] + if rel.etp_depred in paras + for fnt in rel.fantasmas + if fnt.etp_hués in hués ] def gen_ecs(símismo, modelo, mód, exper, n_reps): diff --git "a/tikon/m\303\263ds/rae/red/res/res.py" "b/tikon/m\303\263ds/rae/red/res/res.py" index 4a023ad7..d5b615f5 100644 --- "a/tikon/m\303\263ds/rae/red/res/res.py" +++ "b/tikon/m\303\263ds/rae/red/res/res.py" @@ -9,7 +9,7 @@ RES_ESTOC, EJE_ETAPA from tikon.utils import EJE_DEST, EJE_TIEMPO from ...orgs.insectos.paras import EtapaJuvenilParasitoide -from ...orgs.organismo import EtapaFantasma +from ...orgs.etapa import EtapaFantasma class ResultadoRed(Resultado):