stjames 0.0.77__py3-none-any.whl → 0.0.78__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of stjames might be problematic. Click here for more details.

@@ -3,7 +3,7 @@
3
3
  import re
4
4
  from datetime import datetime
5
5
  from itertools import chain, groupby
6
- from typing import Any, Callable
6
+ from typing import Any, Callable, TypedDict
7
7
 
8
8
  from .data import CODES
9
9
  from .mmcif import add_secondary_structure_to_polymers
@@ -476,15 +476,17 @@ def add_atom_to_polymer(line: str, model: dict[Any, Any], chain_id: str, res_id:
476
476
  :param str res_id: the molecule ID to add to.
477
477
  :param dict aniso_dict: lookup dictionary for anisotropy information."""
478
478
 
479
+ atom = atom_line_to_dict(line, aniso_dict)
480
+
479
481
  try:
480
- model["polymer"][chain_id]["residues"][res_id]["atoms"][int(line[6:11])] = atom_line_to_dict(line, aniso_dict)
482
+ model["polymer"][chain_id]["residues"][res_id]["atoms"][int(line[6:11])] = atom
481
483
  except Exception:
482
484
  name = line[17:20].strip()
483
485
  try:
484
486
  model["polymer"][chain_id]["residues"][res_id] = {
485
487
  "name": name,
486
488
  "full_name": full_names.get(name),
487
- "atoms": {int(line[6:11]): atom_line_to_dict(line, aniso_dict)},
489
+ "atoms": {int(line[6:11]): atom},
488
490
  "number": len(model["polymer"][chain_id]["residues"]) + 1,
489
491
  }
490
492
  except Exception:
@@ -495,7 +497,7 @@ def add_atom_to_polymer(line: str, model: dict[Any, Any], chain_id: str, res_id:
495
497
  "residues": {
496
498
  res_id: {
497
499
  "name": line[17:20].strip(),
498
- "atoms": {int(line[6:11]): atom_line_to_dict(line, aniso_dict)},
500
+ "atoms": {int(line[6:11]): atom},
499
501
  "number": 1,
500
502
  "full_name": None,
501
503
  }
@@ -511,10 +513,11 @@ def add_atom_to_non_polymer(line: str, model: dict[Any, Any], res_id: str, aniso
511
513
  :param dict model: the model to update.
512
514
  :param str res_id: the molecule ID to add to.
513
515
  :param dict aniso_dict: lookup dictionary for anisotropy information."""
516
+ atom = atom_line_to_dict(line, aniso_dict)
514
517
 
515
518
  key = "water" if line[17:20] in ["HOH", "DOD"] else "non_polymer"
516
519
  try:
517
- model[key][res_id]["atoms"][int(line[6:11])] = atom_line_to_dict(line, aniso_dict)
520
+ model[key][res_id]["atoms"][int(line[6:11])] = atom
518
521
  except Exception:
519
522
  name = line[17:20].strip()
520
523
  model[key][res_id] = {
@@ -522,7 +525,7 @@ def add_atom_to_non_polymer(line: str, model: dict[Any, Any], res_id: str, aniso
522
525
  "full_name": full_names.get(name),
523
526
  "internal_id": line[21],
524
527
  "polymer": line[21],
525
- "atoms": {int(line[6:11]): atom_line_to_dict(line, aniso_dict)},
528
+ "atoms": {int(line[6:11]): atom},
526
529
  }
527
530
 
528
531
 
@@ -545,14 +548,32 @@ def guess_element_from_name(atom_name: str) -> str | None:
545
548
  return atom_name[0].upper()
546
549
 
547
550
 
548
- def atom_line_to_dict(line: str, aniso_dict: dict[Any, Any]) -> dict[str, Any]:
549
- """Converts an ATOM or HETATM record to an atom dictionary.
551
+ class AtomDict(TypedDict, total=False):
552
+ """A dictionary representing an atom in a PDB file."""
553
+
554
+ occupancy: float | None
555
+ bvalue: float | None
556
+ charge: int | None
557
+ anisotropy: float | None
558
+ is_hetatm: bool | None
559
+ name: str | None
560
+ alt_loc: str | None
561
+ x: float
562
+ y: float
563
+ z: float
564
+ element: str | None
565
+
566
+
567
+ def atom_line_to_dict(line: str, aniso_dict: dict[Any, Any]) -> AtomDict:
568
+ """
569
+ Converts an ATOM or HETATM record to an atom dictionary.
550
570
 
551
571
  :param str line: the record to convert.
552
572
  :param dict aniso_dict: the anisotropy dictionary to use.
553
- :rtype: ``dict``"""
573
+ :return: atom dictionary
574
+ """
554
575
 
555
- a = {"occupancy": 1, "bvalue": None, "charge": 0, "anisotropy": aniso_dict.get(int(line[6:11].strip()), None)}
576
+ a: AtomDict = {"occupancy": 1, "bvalue": None, "charge": 0, "anisotropy": aniso_dict.get(int(line[6:11].strip()), None)}
556
577
  a["is_hetatm"] = line[:6] == "HETATM"
557
578
  a["name"] = line[12:16].strip() or None
558
579
  a["alt_loc"] = line[16].strip() or None
@@ -565,6 +586,9 @@ def atom_line_to_dict(line: str, aniso_dict: dict[Any, Any]) -> dict[str, Any]:
565
586
  a["bvalue"] = float(line[60:66].strip())
566
587
  a["element"] = line[76:78].strip() or None
567
588
  if not a["element"]:
589
+ if not a["name"]:
590
+ raise ValueError("Cannot guess element from empty name.")
591
+ assert isinstance(a["name"], str)
568
592
  a["element"] = guess_element_from_name(a["name"])
569
593
  if line[78:80].strip():
570
594
  try:
@@ -582,6 +606,7 @@ def atom_line_to_dict(line: str, aniso_dict: dict[Any, Any]) -> dict[str, Any]:
582
606
  a["occupancy"] = None
583
607
  if a["name"] == a["element"]:
584
608
  a["name"] = None
609
+
585
610
  return a
586
611
 
587
612
 
stjames/molecule.py CHANGED
@@ -90,20 +90,27 @@ class Molecule(Base):
90
90
  r"""
91
91
  Calculate the angle between three atoms.
92
92
 
93
- >>> Molecule.from_xyz("H 0 0 0\nO 0 0 1\nH 0 1 1").angle(0, 1, 2)
93
+ >>> Molecule.from_xyz("H 0 0 0\nO 0 0 1\nH 0 1 1").angle(1, 2, 3)
94
94
  90.0
95
95
  """
96
96
 
97
- return angle(self.coordinates[i], self.coordinates[j], self.coordinates[k], degrees=degrees)
97
+ return angle(self.coordinates[i - 1], self.coordinates[j - 1], self.coordinates[k - 1], degrees=degrees)
98
98
 
99
99
  def dihedral(self, i: int, j: int, k: int, l: int, degrees: bool = True, positive_domain: bool = True) -> float:
100
100
  r"""
101
101
  Calculate the dihedral angle between four atoms.
102
102
 
103
- >>> Molecule.from_xyz("H 0 0 0\nO 0 0 1\nO 0 1 1\nH 1 1 1").dihedral(0, 1, 2, 3)
103
+ >>> Molecule.from_xyz("H 0 0 0\nO 0 0 1\nO 0 1 1\nH 1 1 1").dihedral(1, 2, 3, 4)
104
104
  270.0
105
105
  """
106
- return dihedral(self.coordinates[i], self.coordinates[j], self.coordinates[k], self.coordinates[l], degrees=degrees, positive_domain=positive_domain)
106
+ return dihedral(
107
+ self.coordinates[i - 1],
108
+ self.coordinates[j - 1],
109
+ self.coordinates[k - 1],
110
+ self.coordinates[l - 1],
111
+ degrees=degrees,
112
+ positive_domain=positive_domain,
113
+ )
107
114
 
108
115
  @property
109
116
  def coordinates(self) -> Vector3DPerAtom:
@@ -365,19 +372,17 @@ class Molecule(Base):
365
372
  return cls(atoms=atoms, cell=cell, charge=charge, multiplicity=multiplicity, energy=energy, gradient=gradients)
366
373
 
367
374
  @classmethod
368
- def from_rdkit(cls: type[Self], rdkm: RdkitMol, cid: int = 0) -> Self:
375
+ def from_rdkit(cls: type[Self], rdkm: RdkitMol, cid: int = 0, multiplicity: int = 1) -> Self:
369
376
  if len(rdkm.GetConformers()) == 0:
370
377
  rdkm = _embed_rdkit_mol(rdkm)
371
378
 
372
- atoms = []
373
379
  atomic_numbers = [atom.GetAtomicNum() for atom in rdkm.GetAtoms()] # type: ignore [no-untyped-call, unused-ignore]
374
- geom = rdkm.GetConformers()[cid].GetPositions()
375
-
376
- for i in range(len(atomic_numbers)):
377
- atoms.append(Atom(atomic_number=atomic_numbers[i], position=geom[i]))
380
+ atoms = [
381
+ Atom(atomic_number=atom, position=xyz) # keep open
382
+ for atom, xyz in zip(atomic_numbers, rdkm.GetConformers()[cid].GetPositions(), strict=True)
383
+ ]
378
384
 
379
385
  charge = Chem.GetFormalCharge(rdkm)
380
- multiplicity = 1
381
386
 
382
387
  return cls(atoms=atoms, charge=charge, multiplicity=multiplicity)
383
388
 
@@ -7,10 +7,10 @@ from .workflow import MoleculeWorkflow
7
7
 
8
8
  class HydrogenBondAcceptorSite(Base):
9
9
  """
10
- A hydrogen bond acceptor site.
10
+ A hydrogen-bond-acceptor site.
11
11
 
12
12
  :param atom_idx: index of the atom
13
- :param pkbhx: Hydrogen bond basicity
13
+ :param pkbhx: Hydrogen-bond basicity
14
14
  :param position: position of the atom
15
15
  :param name: name of the atom
16
16
  """
@@ -21,9 +21,23 @@ class HydrogenBondAcceptorSite(Base):
21
21
  name: str | None = None
22
22
 
23
23
 
24
+ class HydrogenBondDonorSite(Base):
25
+ """
26
+ A hydrogen-bond-donor site.
27
+
28
+ :param atom_idx: index of the atom
29
+ :param pk_alpha: Hydrogen-bond acidity
30
+ :param position: position of the atom
31
+ """
32
+
33
+ atom_idx: int # zero-indexed
34
+ pk_alpha: float
35
+ position: tuple[float, float, float]
36
+
37
+
24
38
  class HydrogenBondBasicityWorkflow(MoleculeWorkflow):
25
39
  """
26
- Workflow for calculating hydrogen bond basicity.
40
+ Workflow for calculating hydrogen-bond basicity and acidity.
27
41
 
28
42
  Inherited:
29
43
  :param initial_molecule: Molecule of interest
@@ -36,6 +50,7 @@ class HydrogenBondBasicityWorkflow(MoleculeWorkflow):
36
50
  Results:
37
51
  :param optimization: UUID of optimization
38
52
  :param hba_sites: hydrogen-bond-acceptor sites
53
+ :param hbd_sites: hydrogen-bond-donor sites
39
54
  """
40
55
 
41
56
  do_csearch: bool = True
@@ -43,3 +58,4 @@ class HydrogenBondBasicityWorkflow(MoleculeWorkflow):
43
58
 
44
59
  optimization: UUID | None = None
45
60
  hba_sites: list[HydrogenBondAcceptorSite] = [] # noqa: RUF012
61
+ hbd_sites: list[HydrogenBondDonorSite] = [] # noqa: RUF012
@@ -1,15 +1,19 @@
1
1
  """Multi-stage optimization workflow."""
2
2
 
3
+ import re
3
4
  from typing import Self, Sequence
4
5
 
6
+ import more_itertools as mit
5
7
  from pydantic import BaseModel, Field, model_validator
6
8
 
9
+ from stjames.correction import Correction
10
+
7
11
  from ..constraint import Constraint
8
12
  from ..method import XTB_METHODS, Method
9
13
  from ..mode import Mode
10
14
  from ..opt_settings import OptimizationSettings
11
15
  from ..settings import Settings
12
- from ..solvent import Solvent, SolventSettings
16
+ from ..solvent import Solvent, SolventModel, SolventSettings
13
17
  from ..task import Task
14
18
  from ..types import UUID
15
19
  from .workflow import MoleculeWorkflow
@@ -264,6 +268,73 @@ class MultiStageOptMixin(BaseModel):
264
268
  return self
265
269
 
266
270
 
271
+ def mso_settings_from_method_string(
272
+ methods: str,
273
+ solvent: Solvent | None = None,
274
+ use_solvent_for_opt: bool = False,
275
+ constraints: list[Constraint] | None = None,
276
+ transition_state: bool = False,
277
+ frequencies: bool = False,
278
+ ) -> MultiStageOptSettings:
279
+ """
280
+ Helper function to construct multi-stage opt settings objects from a method string.
281
+
282
+ >>> mso_settings_from_method_string("r2SCAN-3c/CPCM(Water)//B3LYP-D3/6-31G(d)/ALPB(Water)//GFN2-xTB/CPCM(Water)//GFN0-xTB").level_of_theory
283
+ 'r2scan_3c/cpcm(water)//b3lyp-d3/6-31g(d)/alpb(water)//gfn2_xtb/cpcm(water)//gfn0_xtb'
284
+ """
285
+ solvent_models = "|".join(model.name for model in SolventModel)
286
+
287
+ pattern = rf"""
288
+ (?P<method>[^/()]+) # Method + optional corrections
289
+ (?:/(?P<basis_set>(?!{solvent_models})[^/]+?))? # Optional basis_set, not starting with solvent model name
290
+ (?:/(?P<solvent_model>{solvent_models}) # Optional solvent model
291
+ \((?P<solvent>[^()]+)\))? # Solvent name in parentheses
292
+ (?:\/\/|$) # End or separator
293
+ """
294
+ constraints = constraints or []
295
+ opt_settings = OptimizationSettings(constraints=constraints, transition_state=transition_state)
296
+ OPT = [Task.OPTIMIZE if not transition_state else Task.OPTIMIZE_TS]
297
+
298
+ valid_corrections = {c.name.lower() for c in Correction} # Python3.11 hack
299
+
300
+ def process(match: re.Match[str]) -> Settings:
301
+ data = match.groupdict()
302
+
303
+ method, corrections = mit.partition(lambda x: x.lower() in valid_corrections, data["method"].split("-"))
304
+ solvent_settings = SolventSettings(solvent=data["solvent"], model=data["solvent_model"]) if data["solvent"] else None
305
+
306
+ return Settings(
307
+ method="-".join(method),
308
+ basis_set=data["basis_set"],
309
+ tasks=OPT,
310
+ solvent_settings=solvent_settings,
311
+ opt_settings=opt_settings,
312
+ corrections=list(corrections),
313
+ )
314
+
315
+ optimization_settings = [process(match) for match in re.finditer(pattern, methods, re.VERBOSE | re.IGNORECASE)]
316
+ if len(optimization_settings) > 1:
317
+ sp_settings = optimization_settings.pop(0)
318
+ sp_settings.tasks = [Task.ENERGY]
319
+ else:
320
+ sp_settings = None
321
+
322
+ optimization_settings = optimization_settings[::-1]
323
+ if frequencies:
324
+ optimization_settings[-1].tasks.append(Task.FREQUENCIES)
325
+
326
+ return MultiStageOptSettings(
327
+ mode=Mode.MANUAL,
328
+ optimization_settings=optimization_settings,
329
+ singlepoint_settings=sp_settings,
330
+ solvent=solvent,
331
+ xtb_preopt=False,
332
+ constraints=constraints,
333
+ transition_state=transition_state,
334
+ frequencies=frequencies,
335
+ )
336
+
337
+
267
338
  def build_mso_settings(
268
339
  sp_method: Method,
269
340
  sp_basis_set: str | None,
@@ -12,6 +12,7 @@ class CofoldingModel(LowercaseStrEnum):
12
12
 
13
13
  CHAI_1R = "chai_1r"
14
14
  BOLTZ_1 = "boltz_1"
15
+ BOLTZ_2 = "boltz_2"
15
16
 
16
17
 
17
18
  class CofoldingScores(BaseModel):
@@ -20,6 +21,15 @@ class CofoldingScores(BaseModel):
20
21
  iptm: float # interface predicted template modeling score
21
22
 
22
23
 
24
+ class AffinityScore(BaseModel):
25
+ pred_value: float
26
+ probability_binary: float
27
+ pred_value1: float
28
+ probability_binary1: float
29
+ pred_value2: float
30
+ probability_binary2: float
31
+
32
+
23
33
  class ProteinCofoldingWorkflow(FASTAWorkflow):
24
34
  """
25
35
  A workflow for predicting structures. Especially protein structures.
@@ -38,4 +48,5 @@ class ProteinCofoldingWorkflow(FASTAWorkflow):
38
48
  use_templates_server: bool = False
39
49
  predicted_structure_uuid: UUID | None = None
40
50
  scores: CofoldingScores | None = None
41
- model: CofoldingModel = CofoldingModel.CHAI_1R
51
+ model: CofoldingModel = CofoldingModel.BOLTZ_2
52
+ affinity_score: AffinityScore | None = None
@@ -32,6 +32,7 @@ class FASTAWorkflow(Workflow):
32
32
 
33
33
  initial_protein_sequences: list[str]
34
34
  initial_smiles_list: list[str] | None = None
35
+ ligand_binding_affinity_index: int | None = None
35
36
 
36
37
  def __repr__(self) -> str:
37
38
  return f"<{type(self).__name__} {self.initial_protein_sequences} {self.initial_smiles_list}>"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: stjames
3
- Version: 0.0.77
3
+ Version: 0.0.78
4
4
  Summary: standardized JSON atom/molecule encoding scheme
5
5
  Author-email: Corin Wagen <corin@rowansci.com>
6
6
  Project-URL: Homepage, https://github.com/rowansci/stjames
@@ -12,6 +12,7 @@ Requires-Dist: pydantic>=2.4
12
12
  Requires-Dist: numpy
13
13
  Requires-Dist: requests
14
14
  Requires-Dist: rdkit
15
+ Requires-Dist: more-itertools
15
16
  Dynamic: license-file
16
17
 
17
18
  # stjames
@@ -13,7 +13,7 @@ stjames/int_settings.py,sha256=5HXp8opt5ZyY1UpmfaK7NVloWVLM5jkG0elEEqpVLUo,896
13
13
  stjames/message.py,sha256=Rq6QqmHZKecWxYH8fVyXmuoCCPZv8YinvgykSeorXSU,216
14
14
  stjames/method.py,sha256=k7WtNvD_0OyXWWDjukmVXFFYfIO-Wg4vY1W58o99-gQ,2821
15
15
  stjames/mode.py,sha256=xw46Cc7f3eTS8i35qECi-8DocAlANhayK3w4akD4HBU,496
16
- stjames/molecule.py,sha256=q1pgcfcae3Z6klTaiIqAVaE966GxhuRKP92O9TP1h3Y,20529
16
+ stjames/molecule.py,sha256=l4S6prH1xflnZtbwidSF2THsiHaSJtRiy1rmUbH366Q,20631
17
17
  stjames/opt_settings.py,sha256=LEwGXUEKq5TfU5rr60Z4QQBhCqiw1Ch5w0M_lXawWo8,642
18
18
  stjames/pdb.py,sha256=_pIdJCMhIzS4t2HWQa_susDWjZEl0oLn4Njb1QoKvKw,26460
19
19
  stjames/periodic_cell.py,sha256=eV_mArsY_MPEFSrFEsTC-CyCc6V8ITAXdk7yhjjNI7M,1080
@@ -28,7 +28,7 @@ stjames/types.py,sha256=rs2CdpkruIfU-PS98rjr9HAJNFGdZDB_zl-u3wa5rAs,4092
28
28
  stjames/atomium_stjames/__init__.py,sha256=gZkzC7i9D_fmWUTN55gtygITo3-qvJUda5CXLR0jyCQ,306
29
29
  stjames/atomium_stjames/data.py,sha256=-hzwBpTHq5JetsOVyopUJswKnKAkMtJ_XkONxjXVupU,5675
30
30
  stjames/atomium_stjames/mmcif.py,sha256=em1fNt6577OaUjL7Pctru7aJp3ceZ9kEnj5w6BRWdVs,27090
31
- stjames/atomium_stjames/pdb.py,sha256=3cVvLsdK0_6m5Z5P0XlathoEvRXCv174MdmxecM4cis,23280
31
+ stjames/atomium_stjames/pdb.py,sha256=hoEV1xmbkYkXW8A6YX5uspvSRvGlhzM1o_BiikQ6DiU,23734
32
32
  stjames/atomium_stjames/utilities.py,sha256=-YtM7sRMvMk0wWrC3svWUWH4CGI0NtY77nXsg9tjHfc,4964
33
33
  stjames/data/__init__.py,sha256=O59Ksp7AIqwOELCWymfCx7YeBzwNOGCMlGQi7tNLqiE,24
34
34
  stjames/data/bragg_radii.json,sha256=hhbn-xyZNSdmnULIjN2Cvq-_BGIZIqG243Ls_mey61w,1350
@@ -47,22 +47,22 @@ stjames/workflows/descriptors.py,sha256=T4tc7xdtBdxESGO86KR323jPQ2pgwxBqgV0khA6M
47
47
  stjames/workflows/docking.py,sha256=GCW_-JeEZcMXKZ9EQFOxWUYRo0jsbzwIv10aSz8KuaQ,3027
48
48
  stjames/workflows/electronic_properties.py,sha256=GT3-NC7w-dbcOJ-3AzJ7LgzH6frTbiH2Iyb9BCa-SvY,4112
49
49
  stjames/workflows/fukui.py,sha256=T6TDg-lcE-sfTDVpa3KFBenLe7PGUO2QrQ2jNuw_iiU,1756
50
- stjames/workflows/hydrogen_bond_basicity.py,sha256=XDpHEluw6DQ9Zk5g2Je2a81HqIkqPglZ-6f2YZnd4Bc,1159
50
+ stjames/workflows/hydrogen_bond_basicity.py,sha256=q9eXty68ZyCmrB6G_8bfeOT8Ui_IQquRPu6z-3rNreQ,1589
51
51
  stjames/workflows/ion_mobility.py,sha256=e6XSidrud5qSkrAcjzOzgHaf-G09JoP09V76myjdyjc,1097
52
52
  stjames/workflows/irc.py,sha256=ZP7icylW8rgo_Uh7h3bmyumn0ru1IyF-61nP5Jnmq3M,3402
53
53
  stjames/workflows/macropka.py,sha256=KRIyk4gsSYL3eqyzCDndStGLwjWSo60cgCAzvAoD1Nk,3754
54
54
  stjames/workflows/molecular_dynamics.py,sha256=kxugE73Ntzpj-xpJSoQ1EwGzXXdvi_NTyeP4913EVwE,3173
55
- stjames/workflows/multistage_opt.py,sha256=pPLAZDztHd37q8cxCUkdq8EzOFyrTzZJHNfDV5auiHs,13638
55
+ stjames/workflows/multistage_opt.py,sha256=P8rxMUhKXMmDi7Id95IOTEmM0xN1ErsKcDJfgY08vjc,16538
56
56
  stjames/workflows/pka.py,sha256=j3vBh2YM3nJzJ1XJKPsmYahRCeaU9n3P-G-u9_moaFw,2065
57
- stjames/workflows/protein_cofolding.py,sha256=lHeAYRzELo5VMeZsVX-FHJteR9cXCAx_DznDwX7WwGg,1188
57
+ stjames/workflows/protein_cofolding.py,sha256=y4egDFTVEmUPN4gVeivgO83IA3CG6AVbRtvU5Ng3iuE,1454
58
58
  stjames/workflows/redox_potential.py,sha256=7S18t9Y3eynSnA3lZbRlvLfdbgeBopdiigLzt1zxg5c,3871
59
59
  stjames/workflows/scan.py,sha256=DXQBpa2t2PowAtOwmdgpxaSLq--fEShljzAGSb8Nf5U,2993
60
60
  stjames/workflows/solubility.py,sha256=kGfVyPPGDLRpf2j6dSY7woCkfsoXSbUzdSImA4mcMpw,1898
61
61
  stjames/workflows/spin_states.py,sha256=0degmE-frovgoXweshZyjfjqL7nkbaFoO9YoJhvQnaI,4748
62
62
  stjames/workflows/tautomer.py,sha256=7eYKziGPg8Km6lfowTzSkgJfJ4SHUPrAmnTf8Bi-SB0,1164
63
- stjames/workflows/workflow.py,sha256=NA4H9W_47UQWnrsdB5UXFODMaZWrmULYqW1pud5ErO4,1900
64
- stjames-0.0.77.dist-info/licenses/LICENSE,sha256=i7ehYBS-6gGmbTcgU4mgk28pyOx2kScJ0kcx8n7bWLM,1084
65
- stjames-0.0.77.dist-info/METADATA,sha256=3Hheys7rszNhTmStDBl9Xk6rS1BmeNHS4h5SdjWrUXc,1694
66
- stjames-0.0.77.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
67
- stjames-0.0.77.dist-info/top_level.txt,sha256=FYCwxl6quhYOAgG-mnPQcCK8vsVM7B8rIUrO-WrQ_PI,8
68
- stjames-0.0.77.dist-info/RECORD,,
63
+ stjames/workflows/workflow.py,sha256=OE05pt2ZOd8TzTOlBngXCVg9wv_553ZR60VNRPlq0f8,1953
64
+ stjames-0.0.78.dist-info/licenses/LICENSE,sha256=i7ehYBS-6gGmbTcgU4mgk28pyOx2kScJ0kcx8n7bWLM,1084
65
+ stjames-0.0.78.dist-info/METADATA,sha256=F9H_rYG2dPbIsXty-Du4PnKs64_-k1f1VRt69dqZU9U,1724
66
+ stjames-0.0.78.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
67
+ stjames-0.0.78.dist-info/top_level.txt,sha256=FYCwxl6quhYOAgG-mnPQcCK8vsVM7B8rIUrO-WrQ_PI,8
68
+ stjames-0.0.78.dist-info/RECORD,,