RNApolis 0.10.5__py3-none-any.whl → 0.10.7__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.
- rnapolis/adapter.py +357 -6
 - rnapolis/common.py +21 -13
 - {rnapolis-0.10.5.dist-info → rnapolis-0.10.7.dist-info}/METADATA +1 -1
 - {rnapolis-0.10.5.dist-info → rnapolis-0.10.7.dist-info}/RECORD +8 -8
 - {rnapolis-0.10.5.dist-info → rnapolis-0.10.7.dist-info}/WHEEL +0 -0
 - {rnapolis-0.10.5.dist-info → rnapolis-0.10.7.dist-info}/entry_points.txt +0 -0
 - {rnapolis-0.10.5.dist-info → rnapolis-0.10.7.dist-info}/licenses/LICENSE +0 -0
 - {rnapolis-0.10.5.dist-info → rnapolis-0.10.7.dist-info}/top_level.txt +0 -0
 
    
        rnapolis/adapter.py
    CHANGED
    
    | 
         @@ -8,7 +8,7 @@ from collections import defaultdict 
     | 
|
| 
       8 
8 
     | 
    
         
             
            from dataclasses import dataclass
         
     | 
| 
       9 
9 
     | 
    
         
             
            from enum import Enum
         
     | 
| 
       10 
10 
     | 
    
         
             
            from tempfile import NamedTemporaryFile
         
     | 
| 
       11 
     | 
    
         
            -
            from typing import DefaultDict, Dict, List, Optional, Set, Tuple
         
     | 
| 
      
 11 
     | 
    
         
            +
            from typing import Any, DefaultDict, Dict, List, Optional, Set, Tuple, Union
         
     | 
| 
       12 
12 
     | 
    
         | 
| 
       13 
13 
     | 
    
         
             
            import orjson
         
     | 
| 
       14 
14 
     | 
    
         | 
| 
         @@ -49,6 +49,7 @@ class ExternalTool(Enum): 
     | 
|
| 
       49 
49 
     | 
    
         
             
                BPNET = "bpnet"
         
     | 
| 
       50 
50 
     | 
    
         
             
                MAXIT = "maxit"
         
     | 
| 
       51 
51 
     | 
    
         
             
                BARNABA = "barnaba"
         
     | 
| 
      
 52 
     | 
    
         
            +
                MCANNOTATE = "mc-annotate"
         
     | 
| 
       52 
53 
     | 
    
         | 
| 
       53 
54 
     | 
    
         | 
| 
       54 
55 
     | 
    
         
             
            logging.basicConfig(level=os.getenv("LOGLEVEL", "INFO").upper())
         
     | 
| 
         @@ -68,25 +69,30 @@ def auto_detect_tool(external_files: List[str]) -> ExternalTool: 
     | 
|
| 
       68 
69 
     | 
    
         
             
                    return ExternalTool.MAXIT
         
     | 
| 
       69 
70 
     | 
    
         | 
| 
       70 
71 
     | 
    
         
             
                for file_path in external_files:
         
     | 
| 
      
 72 
     | 
    
         
            +
                    basename = os.path.basename(file_path)
         
     | 
| 
      
 73 
     | 
    
         
            +
             
     | 
| 
       71 
74 
     | 
    
         
             
                    # Check for FR3D pattern
         
     | 
| 
       72 
     | 
    
         
            -
                    if  
     | 
| 
      
 75 
     | 
    
         
            +
                    if basename.endswith("basepair_detail.txt"):
         
     | 
| 
       73 
76 
     | 
    
         
             
                        return ExternalTool.FR3D
         
     | 
| 
       74 
77 
     | 
    
         | 
| 
       75 
78 
     | 
    
         
             
                    # Check for RNAView pattern
         
     | 
| 
       76 
     | 
    
         
            -
                    if  
     | 
| 
      
 79 
     | 
    
         
            +
                    if basename.endswith(".out"):
         
     | 
| 
       77 
80 
     | 
    
         
             
                        return ExternalTool.RNAVIEW
         
     | 
| 
       78 
81 
     | 
    
         | 
| 
       79 
82 
     | 
    
         
             
                    # Check for BPNet pattern
         
     | 
| 
       80 
     | 
    
         
            -
                    if  
     | 
| 
      
 83 
     | 
    
         
            +
                    if basename.endswith("basepair.json"):
         
     | 
| 
       81 
84 
     | 
    
         
             
                        return ExternalTool.BPNET
         
     | 
| 
       82 
85 
     | 
    
         | 
| 
      
 86 
     | 
    
         
            +
                    # Check for MC-Annotate pattern
         
     | 
| 
      
 87 
     | 
    
         
            +
                    if basename.endswith("stdout.txt"):
         
     | 
| 
      
 88 
     | 
    
         
            +
                        return ExternalTool.MCANNOTATE
         
     | 
| 
      
 89 
     | 
    
         
            +
             
     | 
| 
       83 
90 
     | 
    
         
             
                    # Check for Barnaba pattern
         
     | 
| 
       84 
     | 
    
         
            -
                    basename = os.path.basename(file_path)
         
     | 
| 
       85 
91 
     | 
    
         
             
                    if "pairing" in basename or "stacking" in basename:
         
     | 
| 
       86 
92 
     | 
    
         
             
                        return ExternalTool.BARNABA
         
     | 
| 
       87 
93 
     | 
    
         | 
| 
       88 
94 
     | 
    
         
             
                    # Check for JSON files (DSSR)
         
     | 
| 
       89 
     | 
    
         
            -
                    if  
     | 
| 
      
 95 
     | 
    
         
            +
                    if basename.endswith(".json"):
         
     | 
| 
       90 
96 
     | 
    
         
             
                        return ExternalTool.DSSR
         
     | 
| 
       91 
97 
     | 
    
         | 
| 
       92 
98 
     | 
    
         
             
                # Default to MAXIT if no patterns match
         
     | 
| 
         @@ -1157,6 +1163,346 @@ def parse_barnaba_output( 
     | 
|
| 
       1157 
1163 
     | 
    
         
             
                )
         
     | 
| 
       1158 
1164 
     | 
    
         | 
| 
       1159 
1165 
     | 
    
         | 
| 
      
 1166 
     | 
    
         
            +
            class MCAnnotateAdapter:
         
     | 
| 
      
 1167 
     | 
    
         
            +
                # Represents state of parsing MC-Annotate result
         
     | 
| 
      
 1168 
     | 
    
         
            +
                # Luckily every important part of file
         
     | 
| 
      
 1169 
     | 
    
         
            +
                # begins with a unique sentence
         
     | 
| 
      
 1170 
     | 
    
         
            +
                class ParseState(str, Enum):
         
     | 
| 
      
 1171 
     | 
    
         
            +
                    RESIDUES_INFORMATION = "Residue conformations"
         
     | 
| 
      
 1172 
     | 
    
         
            +
                    ADJACENT_STACKINGS = "Adjacent stackings"
         
     | 
| 
      
 1173 
     | 
    
         
            +
                    NON_ADJACENT_STACKINGS = "Non-Adjacent stackings"
         
     | 
| 
      
 1174 
     | 
    
         
            +
                    BASE_PAIRS_SECTION = "Base-pairs"
         
     | 
| 
      
 1175 
     | 
    
         
            +
                    SUMMARY_SECTION = "Number of"
         
     | 
| 
      
 1176 
     | 
    
         
            +
             
     | 
| 
      
 1177 
     | 
    
         
            +
                # This dictionary maps our model edges
         
     | 
| 
      
 1178 
     | 
    
         
            +
                # to edge representation used by MC-Annotate
         
     | 
| 
      
 1179 
     | 
    
         
            +
                EDGES: Dict[str, Tuple[str, ...]] = {
         
     | 
| 
      
 1180 
     | 
    
         
            +
                    "H": ("Hh", "Hw", "Bh", "C8"),
         
     | 
| 
      
 1181 
     | 
    
         
            +
                    "W": ("Wh", "Ww", "Ws"),
         
     | 
| 
      
 1182 
     | 
    
         
            +
                    "S": ("Ss", "Sw", "Bs"),
         
     | 
| 
      
 1183 
     | 
    
         
            +
                }
         
     | 
| 
      
 1184 
     | 
    
         
            +
             
     | 
| 
      
 1185 
     | 
    
         
            +
                # Contains flatten EDGES values (in one touple)
         
     | 
| 
      
 1186 
     | 
    
         
            +
                ALL_EDGES = sum(EDGES.values(), ())
         
     | 
| 
      
 1187 
     | 
    
         
            +
             
     | 
| 
      
 1188 
     | 
    
         
            +
                # Based on these tokens
         
     | 
| 
      
 1189 
     | 
    
         
            +
                # BaseRiboseInteractions and BasePhosphateInteractions are created
         
     | 
| 
      
 1190 
     | 
    
         
            +
                RIBOSE_ATOM = "O2'"
         
     | 
| 
      
 1191 
     | 
    
         
            +
                PHOSPHATE_ATOM = "O2P"
         
     | 
| 
      
 1192 
     | 
    
         
            +
             
     | 
| 
      
 1193 
     | 
    
         
            +
                # Single hydrogen bond - for us it's OtherInteraction
         
     | 
| 
      
 1194 
     | 
    
         
            +
                ONE_HBOND = "one_hbond"
         
     | 
| 
      
 1195 
     | 
    
         
            +
             
     | 
| 
      
 1196 
     | 
    
         
            +
                # Cis/trans tokens used by MC-Annotate
         
     | 
| 
      
 1197 
     | 
    
         
            +
                CIS = "cis"
         
     | 
| 
      
 1198 
     | 
    
         
            +
                TRANS = "trans"
         
     | 
| 
      
 1199 
     | 
    
         
            +
             
     | 
| 
      
 1200 
     | 
    
         
            +
                # Tokens used in PDB files
         
     | 
| 
      
 1201 
     | 
    
         
            +
                ATOM = "ATOM"
         
     | 
| 
      
 1202 
     | 
    
         
            +
                HETATM = "HETATM"
         
     | 
| 
      
 1203 
     | 
    
         
            +
             
     | 
| 
      
 1204 
     | 
    
         
            +
                # This regex is used to capture 6 groups of residues information:
         
     | 
| 
      
 1205 
     | 
    
         
            +
                # (1) (2) (3) (4) (5) (6)
         
     | 
| 
      
 1206 
     | 
    
         
            +
                # 1, 4 - chain IDs
         
     | 
| 
      
 1207 
     | 
    
         
            +
                # 2, 5 - numbers
         
     | 
| 
      
 1208 
     | 
    
         
            +
                # 3, 6 - icodes (or empty string if no icode)
         
     | 
| 
      
 1209 
     | 
    
         
            +
                # Example - match and groups:
         
     | 
| 
      
 1210 
     | 
    
         
            +
                # A-100.X-B200
         
     | 
| 
      
 1211 
     | 
    
         
            +
                # ('A'), ('-100'), ('X'), ('B'), ('200'), ('')
         
     | 
| 
      
 1212 
     | 
    
         
            +
                RESIDUE_REGEX = re.compile(
         
     | 
| 
      
 1213 
     | 
    
         
            +
                    r"'?(.)'?(-?[0-9]+)\.?([a-zA-Z]?)-'?(.)'?(-?[0-9]+)\.?([a-zA-Z]?)"
         
     | 
| 
      
 1214 
     | 
    
         
            +
                )
         
     | 
| 
      
 1215 
     | 
    
         
            +
             
     | 
| 
      
 1216 
     | 
    
         
            +
                # Roman numerals used by Saenger
         
     | 
| 
      
 1217 
     | 
    
         
            +
                # both in our model and MC-Annotate
         
     | 
| 
      
 1218 
     | 
    
         
            +
                ROMAN_NUMERALS = ("I", "V", "X")
         
     | 
| 
      
 1219 
     | 
    
         
            +
             
     | 
| 
      
 1220 
     | 
    
         
            +
                # Positions of residues info in PDB files
         
     | 
| 
      
 1221 
     | 
    
         
            +
                CHAIN_INDEX = 21
         
     | 
| 
      
 1222 
     | 
    
         
            +
                NUMBER_INDEX = slice(22, 26)
         
     | 
| 
      
 1223 
     | 
    
         
            +
                ICODE_INDEX = 26
         
     | 
| 
      
 1224 
     | 
    
         
            +
                NAME_INDEX = slice(17, 20)
         
     | 
| 
      
 1225 
     | 
    
         
            +
             
     | 
| 
      
 1226 
     | 
    
         
            +
                def __init__(self) -> None:
         
     | 
| 
      
 1227 
     | 
    
         
            +
                    # Since names are not present in adjacent and non-adjacent stackings
         
     | 
| 
      
 1228 
     | 
    
         
            +
                    # we need save these values eariler
         
     | 
| 
      
 1229 
     | 
    
         
            +
                    self.names: Dict[str, str] = {}
         
     | 
| 
      
 1230 
     | 
    
         
            +
                    self.base_pairs: List[BasePair] = []
         
     | 
| 
      
 1231 
     | 
    
         
            +
                    self.stackings: List[Stacking] = []
         
     | 
| 
      
 1232 
     | 
    
         
            +
                    self.base_ribose_interactions: List[BaseRibose] = []
         
     | 
| 
      
 1233 
     | 
    
         
            +
                    self.base_phosphate_interactions: List[BasePhosphate] = []
         
     | 
| 
      
 1234 
     | 
    
         
            +
                    self.other_interactions: List[OtherInteraction] = []
         
     | 
| 
      
 1235 
     | 
    
         
            +
             
     | 
| 
      
 1236 
     | 
    
         
            +
                def classify_edge(self, edge_type: str) -> Optional[str]:
         
     | 
| 
      
 1237 
     | 
    
         
            +
                    for edge, edges in self.EDGES.items():
         
     | 
| 
      
 1238 
     | 
    
         
            +
                        if edge_type in edges:
         
     | 
| 
      
 1239 
     | 
    
         
            +
                            return edge
         
     | 
| 
      
 1240 
     | 
    
         
            +
                    logging.warning('Edge type "{type}" unknown')
         
     | 
| 
      
 1241 
     | 
    
         
            +
                    return None
         
     | 
| 
      
 1242 
     | 
    
         
            +
             
     | 
| 
      
 1243 
     | 
    
         
            +
                def get_residue(self, residue_info_list: Tuple[Union[str, Any], ...]) -> Residue:
         
     | 
| 
      
 1244 
     | 
    
         
            +
                    chain = residue_info_list[0]
         
     | 
| 
      
 1245 
     | 
    
         
            +
                    number = int(residue_info_list[1])
         
     | 
| 
      
 1246 
     | 
    
         
            +
             
     | 
| 
      
 1247 
     | 
    
         
            +
                    if residue_info_list[2] == "":
         
     | 
| 
      
 1248 
     | 
    
         
            +
                        icode = None
         
     | 
| 
      
 1249 
     | 
    
         
            +
                        residue_info = f"{chain}{number}"
         
     | 
| 
      
 1250 
     | 
    
         
            +
                    else:
         
     | 
| 
      
 1251 
     | 
    
         
            +
                        icode = residue_info_list[2]
         
     | 
| 
      
 1252 
     | 
    
         
            +
                        residue_info = f"{chain}{number}.{icode}"
         
     | 
| 
      
 1253 
     | 
    
         
            +
             
     | 
| 
      
 1254 
     | 
    
         
            +
                    return Residue(
         
     | 
| 
      
 1255 
     | 
    
         
            +
                        None, ResidueAuth(chain, number, icode, self.names[residue_info])
         
     | 
| 
      
 1256 
     | 
    
         
            +
                    )
         
     | 
| 
      
 1257 
     | 
    
         
            +
             
     | 
| 
      
 1258 
     | 
    
         
            +
                def get_residues(
         
     | 
| 
      
 1259 
     | 
    
         
            +
                    self, residues_info: str
         
     | 
| 
      
 1260 
     | 
    
         
            +
                ) -> Tuple[Optional[Residue], Optional[Residue]]:
         
     | 
| 
      
 1261 
     | 
    
         
            +
                    regex_result = re.search(self.RESIDUE_REGEX, residues_info)
         
     | 
| 
      
 1262 
     | 
    
         
            +
                    if regex_result is None:
         
     | 
| 
      
 1263 
     | 
    
         
            +
                        logging.error("MC-Annotate regex failed: {residues_info}")
         
     | 
| 
      
 1264 
     | 
    
         
            +
                        return None, None
         
     | 
| 
      
 1265 
     | 
    
         
            +
                    residues_info_list = regex_result.groups()
         
     | 
| 
      
 1266 
     | 
    
         
            +
                    # Expects (chain1, number1, icode1, chain2, number2, icode2)
         
     | 
| 
      
 1267 
     | 
    
         
            +
                    if len(residues_info_list) != 6:
         
     | 
| 
      
 1268 
     | 
    
         
            +
                        logging.error(f"MC-Annotate regex failed for {residues_info}")
         
     | 
| 
      
 1269 
     | 
    
         
            +
                        return None, None
         
     | 
| 
      
 1270 
     | 
    
         
            +
                    residue_left = self.get_residue(residues_info_list[:3])
         
     | 
| 
      
 1271 
     | 
    
         
            +
                    residue_right = self.get_residue(residues_info_list[3:])
         
     | 
| 
      
 1272 
     | 
    
         
            +
                    return residue_left, residue_right
         
     | 
| 
      
 1273 
     | 
    
         
            +
             
     | 
| 
      
 1274 
     | 
    
         
            +
                def append_stacking(self, line: str, topology_position: int) -> None:
         
     | 
| 
      
 1275 
     | 
    
         
            +
                    splitted_line = line.split()
         
     | 
| 
      
 1276 
     | 
    
         
            +
                    topology_info = splitted_line[topology_position]
         
     | 
| 
      
 1277 
     | 
    
         
            +
                    residue_left, residue_right = self.get_residues(splitted_line[0])
         
     | 
| 
      
 1278 
     | 
    
         
            +
                    if residue_left is None or residue_right is None:
         
     | 
| 
      
 1279 
     | 
    
         
            +
                        logging.warning(f"Could not parse residues in line: {line}")
         
     | 
| 
      
 1280 
     | 
    
         
            +
                        return
         
     | 
| 
      
 1281 
     | 
    
         
            +
                    stacking = Stacking(
         
     | 
| 
      
 1282 
     | 
    
         
            +
                        residue_left, residue_right, StackingTopology[topology_info]
         
     | 
| 
      
 1283 
     | 
    
         
            +
                    )
         
     | 
| 
      
 1284 
     | 
    
         
            +
                    self.stackings.append(stacking)
         
     | 
| 
      
 1285 
     | 
    
         
            +
             
     | 
| 
      
 1286 
     | 
    
         
            +
                def get_ribose_interaction(
         
     | 
| 
      
 1287 
     | 
    
         
            +
                    self, residues: Tuple[Residue, Residue], token: str
         
     | 
| 
      
 1288 
     | 
    
         
            +
                ) -> BaseRibose:
         
     | 
| 
      
 1289 
     | 
    
         
            +
                    # BasePair is preffered first so swap if necessary
         
     | 
| 
      
 1290 
     | 
    
         
            +
                    if token.split("/", 1)[0] == self.RIBOSE_ATOM:
         
     | 
| 
      
 1291 
     | 
    
         
            +
                        residue_left, residue_right = residues[1], residues[0]
         
     | 
| 
      
 1292 
     | 
    
         
            +
                    else:
         
     | 
| 
      
 1293 
     | 
    
         
            +
                        residue_left, residue_right = residues[0], residues[1]
         
     | 
| 
      
 1294 
     | 
    
         
            +
                    return BaseRibose(residue_left, residue_right, None)
         
     | 
| 
      
 1295 
     | 
    
         
            +
             
     | 
| 
      
 1296 
     | 
    
         
            +
                def get_phosphate_interaction(
         
     | 
| 
      
 1297 
     | 
    
         
            +
                    self, residues: Tuple[Residue, Residue], token: str
         
     | 
| 
      
 1298 
     | 
    
         
            +
                ) -> BasePhosphate:
         
     | 
| 
      
 1299 
     | 
    
         
            +
                    # BasePair is preffered first so swap if necessary
         
     | 
| 
      
 1300 
     | 
    
         
            +
                    if token.split("/", 1)[0] == self.PHOSPHATE_ATOM:
         
     | 
| 
      
 1301 
     | 
    
         
            +
                        residue_left, residue_right = residues[1], residues[0]
         
     | 
| 
      
 1302 
     | 
    
         
            +
                    else:
         
     | 
| 
      
 1303 
     | 
    
         
            +
                        residue_left, residue_right = residues[0], residues[1]
         
     | 
| 
      
 1304 
     | 
    
         
            +
                    return BasePhosphate(residue_left, residue_right, None)
         
     | 
| 
      
 1305 
     | 
    
         
            +
             
     | 
| 
      
 1306 
     | 
    
         
            +
                def get_base_interaction(
         
     | 
| 
      
 1307 
     | 
    
         
            +
                    self,
         
     | 
| 
      
 1308 
     | 
    
         
            +
                    residues: Tuple[Residue, Residue],
         
     | 
| 
      
 1309 
     | 
    
         
            +
                    token: str,
         
     | 
| 
      
 1310 
     | 
    
         
            +
                    tokens: List[str],
         
     | 
| 
      
 1311 
     | 
    
         
            +
                ) -> Optional[BasePair]:
         
     | 
| 
      
 1312 
     | 
    
         
            +
                    if self.CIS in tokens:
         
     | 
| 
      
 1313 
     | 
    
         
            +
                        cis_trans = "c"
         
     | 
| 
      
 1314 
     | 
    
         
            +
                    elif self.TRANS in tokens:
         
     | 
| 
      
 1315 
     | 
    
         
            +
                        cis_trans = "t"
         
     | 
| 
      
 1316 
     | 
    
         
            +
                    else:
         
     | 
| 
      
 1317 
     | 
    
         
            +
                        logging.warning(f"Cis/trans expected, but not present in {tokens}")
         
     | 
| 
      
 1318 
     | 
    
         
            +
                        return None
         
     | 
| 
      
 1319 
     | 
    
         
            +
             
     | 
| 
      
 1320 
     | 
    
         
            +
                    # example saenger: XIX or XII,XIII (?)
         
     | 
| 
      
 1321 
     | 
    
         
            +
                    for potential_saenger_token in tokens:
         
     | 
| 
      
 1322 
     | 
    
         
            +
                        potential_saenger_without_comma = potential_saenger_token.split(",")[0]
         
     | 
| 
      
 1323 
     | 
    
         
            +
                        if all(
         
     | 
| 
      
 1324 
     | 
    
         
            +
                            char in self.ROMAN_NUMERALS for char in potential_saenger_without_comma
         
     | 
| 
      
 1325 
     | 
    
         
            +
                        ):
         
     | 
| 
      
 1326 
     | 
    
         
            +
                            saenger = Saenger[potential_saenger_without_comma]
         
     | 
| 
      
 1327 
     | 
    
         
            +
                            break
         
     | 
| 
      
 1328 
     | 
    
         
            +
                    else:
         
     | 
| 
      
 1329 
     | 
    
         
            +
                        saenger = None
         
     | 
| 
      
 1330 
     | 
    
         
            +
             
     | 
| 
      
 1331 
     | 
    
         
            +
                    left_edge, right_edge = token.split("/", 1)
         
     | 
| 
      
 1332 
     | 
    
         
            +
                    leontis_westhof_left = self.classify_edge(left_edge)
         
     | 
| 
      
 1333 
     | 
    
         
            +
                    leontis_westohf_right = self.classify_edge(right_edge)
         
     | 
| 
      
 1334 
     | 
    
         
            +
             
     | 
| 
      
 1335 
     | 
    
         
            +
                    if leontis_westhof_left is None or leontis_westohf_right is None:
         
     | 
| 
      
 1336 
     | 
    
         
            +
                        return None
         
     | 
| 
      
 1337 
     | 
    
         
            +
             
     | 
| 
      
 1338 
     | 
    
         
            +
                    leontis_westhof = LeontisWesthof[
         
     | 
| 
      
 1339 
     | 
    
         
            +
                        f"{cis_trans}{leontis_westhof_left}{leontis_westohf_right}"
         
     | 
| 
      
 1340 
     | 
    
         
            +
                    ]
         
     | 
| 
      
 1341 
     | 
    
         
            +
                    residue_left, residue_right = residues
         
     | 
| 
      
 1342 
     | 
    
         
            +
                    return BasePair(residue_left, residue_right, leontis_westhof, saenger)
         
     | 
| 
      
 1343 
     | 
    
         
            +
             
     | 
| 
      
 1344 
     | 
    
         
            +
                def get_other_interaction(
         
     | 
| 
      
 1345 
     | 
    
         
            +
                    self, residues: Tuple[Residue, Residue]
         
     | 
| 
      
 1346 
     | 
    
         
            +
                ) -> OtherInteraction:
         
     | 
| 
      
 1347 
     | 
    
         
            +
                    return OtherInteraction(residues[0], residues[1])
         
     | 
| 
      
 1348 
     | 
    
         
            +
             
     | 
| 
      
 1349 
     | 
    
         
            +
                def append_interactions(self, line: str) -> None:
         
     | 
| 
      
 1350 
     | 
    
         
            +
                    splitted_line = line.split()
         
     | 
| 
      
 1351 
     | 
    
         
            +
                    residues = self.get_residues(splitted_line[0])
         
     | 
| 
      
 1352 
     | 
    
         
            +
                    if residues[0] is None or residues[1] is None:
         
     | 
| 
      
 1353 
     | 
    
         
            +
                        logging.warning(f"Could not parse residues in line: {line}")
         
     | 
| 
      
 1354 
     | 
    
         
            +
                        return
         
     | 
| 
      
 1355 
     | 
    
         
            +
                    # Assumes that one pair can belong to every interaction type
         
     | 
| 
      
 1356 
     | 
    
         
            +
                    # no more than once!
         
     | 
| 
      
 1357 
     | 
    
         
            +
                    base_added, ribose_added, phosphate_added = False, False, False
         
     | 
| 
      
 1358 
     | 
    
         
            +
                    # example tokens: Ww/Ww pairing antiparallel cis XX
         
     | 
| 
      
 1359 
     | 
    
         
            +
                    tokens: List[str] = splitted_line[3:]
         
     | 
| 
      
 1360 
     | 
    
         
            +
             
     | 
| 
      
 1361 
     | 
    
         
            +
                    # Special case
         
     | 
| 
      
 1362 
     | 
    
         
            +
                    # IF single hydrogen bond and base pairs only THEN
         
     | 
| 
      
 1363 
     | 
    
         
            +
                    # append to OtherIneraction list
         
     | 
| 
      
 1364 
     | 
    
         
            +
                    if self.ONE_HBOND in tokens:
         
     | 
| 
      
 1365 
     | 
    
         
            +
                        for token in tokens:
         
     | 
| 
      
 1366 
     | 
    
         
            +
                            if self.RIBOSE_ATOM in token or self.PHOSPHATE_ATOM in token:
         
     | 
| 
      
 1367 
     | 
    
         
            +
                                break
         
     | 
| 
      
 1368 
     | 
    
         
            +
                        else:
         
     | 
| 
      
 1369 
     | 
    
         
            +
                            other_interaction = self.get_other_interaction(residues)
         
     | 
| 
      
 1370 
     | 
    
         
            +
                            self.other_interactions.append(other_interaction)
         
     | 
| 
      
 1371 
     | 
    
         
            +
                            return
         
     | 
| 
      
 1372 
     | 
    
         
            +
             
     | 
| 
      
 1373 
     | 
    
         
            +
                    for token in tokens:
         
     | 
| 
      
 1374 
     | 
    
         
            +
                        if self.RIBOSE_ATOM in token and not ribose_added:
         
     | 
| 
      
 1375 
     | 
    
         
            +
                            # example token: Ss/O2'
         
     | 
| 
      
 1376 
     | 
    
         
            +
                            ribose_interaction = self.get_ribose_interaction(residues, token)
         
     | 
| 
      
 1377 
     | 
    
         
            +
                            self.base_ribose_interactions.append(ribose_interaction)
         
     | 
| 
      
 1378 
     | 
    
         
            +
                            ribose_added = True
         
     | 
| 
      
 1379 
     | 
    
         
            +
             
     | 
| 
      
 1380 
     | 
    
         
            +
                        elif self.PHOSPHATE_ATOM in token and not phosphate_added:
         
     | 
| 
      
 1381 
     | 
    
         
            +
                            # example token: O2P/Bh
         
     | 
| 
      
 1382 
     | 
    
         
            +
                            phosphate_interaction = self.get_phosphate_interaction(residues, token)
         
     | 
| 
      
 1383 
     | 
    
         
            +
                            self.base_phosphate_interactions.append(phosphate_interaction)
         
     | 
| 
      
 1384 
     | 
    
         
            +
                            phosphate_added = True
         
     | 
| 
      
 1385 
     | 
    
         
            +
             
     | 
| 
      
 1386 
     | 
    
         
            +
                        elif len(token.split("/", 1)) > 1:
         
     | 
| 
      
 1387 
     | 
    
         
            +
                            token_left, token_right = token.split("/", 1)
         
     | 
| 
      
 1388 
     | 
    
         
            +
                            tokens_in_edges = (
         
     | 
| 
      
 1389 
     | 
    
         
            +
                                token_left in self.ALL_EDGES and token_right in self.ALL_EDGES
         
     | 
| 
      
 1390 
     | 
    
         
            +
                            )
         
     | 
| 
      
 1391 
     | 
    
         
            +
                            if tokens_in_edges and not base_added:
         
     | 
| 
      
 1392 
     | 
    
         
            +
                                # example token_left: Ww | example token_right: Ws
         
     | 
| 
      
 1393 
     | 
    
         
            +
                                base_pair_interaction = self.get_base_interaction(
         
     | 
| 
      
 1394 
     | 
    
         
            +
                                    residues, token, tokens
         
     | 
| 
      
 1395 
     | 
    
         
            +
                                )
         
     | 
| 
      
 1396 
     | 
    
         
            +
                                if base_pair_interaction is not None:
         
     | 
| 
      
 1397 
     | 
    
         
            +
                                    self.base_pairs.append(base_pair_interaction)
         
     | 
| 
      
 1398 
     | 
    
         
            +
                                base_added = True
         
     | 
| 
      
 1399 
     | 
    
         
            +
             
     | 
| 
      
 1400 
     | 
    
         
            +
                def append_names(self, file_content: str) -> None:
         
     | 
| 
      
 1401 
     | 
    
         
            +
                    for line in file_content.splitlines():
         
     | 
| 
      
 1402 
     | 
    
         
            +
                        if line.startswith(self.ATOM) or line.startswith(self.HETATM):
         
     | 
| 
      
 1403 
     | 
    
         
            +
                            chain = line[self.CHAIN_INDEX].strip()
         
     | 
| 
      
 1404 
     | 
    
         
            +
                            number = line[self.NUMBER_INDEX].strip()
         
     | 
| 
      
 1405 
     | 
    
         
            +
                            icode = line[self.ICODE_INDEX].strip()
         
     | 
| 
      
 1406 
     | 
    
         
            +
                            name = line[self.NAME_INDEX].strip()
         
     | 
| 
      
 1407 
     | 
    
         
            +
                            residue_info = (
         
     | 
| 
      
 1408 
     | 
    
         
            +
                                f"{chain}{number}" if icode == "" else f"{chain}{number}.{icode}"
         
     | 
| 
      
 1409 
     | 
    
         
            +
                            )
         
     | 
| 
      
 1410 
     | 
    
         
            +
                            self.names[residue_info] = name
         
     | 
| 
      
 1411 
     | 
    
         
            +
             
     | 
| 
      
 1412 
     | 
    
         
            +
                def analyze_by_mc_annotate(
         
     | 
| 
      
 1413 
     | 
    
         
            +
                    self, pdb_content: str, mc_result: str, **_: Dict[str, Any]
         
     | 
| 
      
 1414 
     | 
    
         
            +
                ) -> BaseInteractions:
         
     | 
| 
      
 1415 
     | 
    
         
            +
                    self.append_names(pdb_content)
         
     | 
| 
      
 1416 
     | 
    
         
            +
                    current_state = None
         
     | 
| 
      
 1417 
     | 
    
         
            +
             
     | 
| 
      
 1418 
     | 
    
         
            +
                    for line in mc_result.splitlines():
         
     | 
| 
      
 1419 
     | 
    
         
            +
                        for state in self.ParseState:
         
     | 
| 
      
 1420 
     | 
    
         
            +
                            if line.startswith(state.value):
         
     | 
| 
      
 1421 
     | 
    
         
            +
                                current_state = state
         
     | 
| 
      
 1422 
     | 
    
         
            +
                                break
         
     | 
| 
      
 1423 
     | 
    
         
            +
                        # Loop ended without break - parse file
         
     | 
| 
      
 1424 
     | 
    
         
            +
                        else:
         
     | 
| 
      
 1425 
     | 
    
         
            +
                            if current_state == self.ParseState.RESIDUES_INFORMATION:
         
     | 
| 
      
 1426 
     | 
    
         
            +
                                # example line: X7.H : G C3p_endo anti
         
     | 
| 
      
 1427 
     | 
    
         
            +
                                # Skip residues information - meaningless information
         
     | 
| 
      
 1428 
     | 
    
         
            +
                                pass
         
     | 
| 
      
 1429 
     | 
    
         
            +
                            elif current_state == self.ParseState.ADJACENT_STACKINGS:
         
     | 
| 
      
 1430 
     | 
    
         
            +
                                # example line: X4.E-X5.F : adjacent_5p upward
         
     | 
| 
      
 1431 
     | 
    
         
            +
                                self.append_stacking(line, 3)
         
     | 
| 
      
 1432 
     | 
    
         
            +
                            elif current_state == self.ParseState.NON_ADJACENT_STACKINGS:
         
     | 
| 
      
 1433 
     | 
    
         
            +
                                # example line: Y40.M-Y67.N : inward pairing
         
     | 
| 
      
 1434 
     | 
    
         
            +
                                self.append_stacking(line, 2)
         
     | 
| 
      
 1435 
     | 
    
         
            +
                            elif current_state == self.ParseState.BASE_PAIRS_SECTION:
         
     | 
| 
      
 1436 
     | 
    
         
            +
                                # example line: Y38.K-Y51.X : A-U Ww/Ww pairing antiparallel cis XX
         
     | 
| 
      
 1437 
     | 
    
         
            +
                                self.append_interactions(line)
         
     | 
| 
      
 1438 
     | 
    
         
            +
                            elif current_state == self.ParseState.SUMMARY_SECTION:
         
     | 
| 
      
 1439 
     | 
    
         
            +
                                # example line: Number of non adjacent stackings = 26
         
     | 
| 
      
 1440 
     | 
    
         
            +
                                # Skip summary section - meaningless information
         
     | 
| 
      
 1441 
     | 
    
         
            +
                                pass
         
     | 
| 
      
 1442 
     | 
    
         
            +
             
     | 
| 
      
 1443 
     | 
    
         
            +
                    return (
         
     | 
| 
      
 1444 
     | 
    
         
            +
                        self.base_pairs,
         
     | 
| 
      
 1445 
     | 
    
         
            +
                        self.stackings,
         
     | 
| 
      
 1446 
     | 
    
         
            +
                        self.base_ribose_interactions,
         
     | 
| 
      
 1447 
     | 
    
         
            +
                        self.base_phosphate_interactions,
         
     | 
| 
      
 1448 
     | 
    
         
            +
                        self.other_interactions,
         
     | 
| 
      
 1449 
     | 
    
         
            +
                    )
         
     | 
| 
      
 1450 
     | 
    
         
            +
             
     | 
| 
      
 1451 
     | 
    
         
            +
             
     | 
| 
      
 1452 
     | 
    
         
            +
            def parse_mcannotate_output(
         
     | 
| 
      
 1453 
     | 
    
         
            +
                file_paths: List[str], structure3d: Structure3D
         
     | 
| 
      
 1454 
     | 
    
         
            +
            ) -> BaseInteractions:
         
     | 
| 
      
 1455 
     | 
    
         
            +
                """
         
     | 
| 
      
 1456 
     | 
    
         
            +
                Parse mc-annotate output and convert to BaseInteractions.
         
     | 
| 
      
 1457 
     | 
    
         
            +
                This function expects a file with mc-annotate stdout and a PDB file.
         
     | 
| 
      
 1458 
     | 
    
         
            +
                """
         
     | 
| 
      
 1459 
     | 
    
         
            +
                stdout_file = None
         
     | 
| 
      
 1460 
     | 
    
         
            +
                structure_file = None
         
     | 
| 
      
 1461 
     | 
    
         
            +
                for file_path in file_paths:
         
     | 
| 
      
 1462 
     | 
    
         
            +
                    if os.path.basename(file_path).endswith("stdout.txt"):
         
     | 
| 
      
 1463 
     | 
    
         
            +
                        stdout_file = file_path
         
     | 
| 
      
 1464 
     | 
    
         
            +
                    elif file_path.endswith(".pdb"):
         
     | 
| 
      
 1465 
     | 
    
         
            +
                        structure_file = file_path
         
     | 
| 
      
 1466 
     | 
    
         
            +
             
     | 
| 
      
 1467 
     | 
    
         
            +
                if not stdout_file:
         
     | 
| 
      
 1468 
     | 
    
         
            +
                    logging.warning("No stdout.txt file found for mc-annotate.")
         
     | 
| 
      
 1469 
     | 
    
         
            +
                    return BaseInteractions([], [], [], [], [])
         
     | 
| 
      
 1470 
     | 
    
         
            +
             
     | 
| 
      
 1471 
     | 
    
         
            +
                if not structure_file:
         
     | 
| 
      
 1472 
     | 
    
         
            +
                    logging.warning("No PDB file found for mc-annotate.")
         
     | 
| 
      
 1473 
     | 
    
         
            +
                    return BaseInteractions([], [], [], [], [])
         
     | 
| 
      
 1474 
     | 
    
         
            +
             
     | 
| 
      
 1475 
     | 
    
         
            +
                logging.info(f"Processing mc-annotate stdout file: {stdout_file}")
         
     | 
| 
      
 1476 
     | 
    
         
            +
                logging.info(f"Using structure file for residue names: {structure_file}")
         
     | 
| 
      
 1477 
     | 
    
         
            +
             
     | 
| 
      
 1478 
     | 
    
         
            +
                try:
         
     | 
| 
      
 1479 
     | 
    
         
            +
                    with open(stdout_file, "r") as f:
         
     | 
| 
      
 1480 
     | 
    
         
            +
                        mc_result = f.read()
         
     | 
| 
      
 1481 
     | 
    
         
            +
                    with open(structure_file, "r") as f:
         
     | 
| 
      
 1482 
     | 
    
         
            +
                        pdb_content = f.read()
         
     | 
| 
      
 1483 
     | 
    
         
            +
                except Exception as e:
         
     | 
| 
      
 1484 
     | 
    
         
            +
                    logging.warning(f"Could not read input files for mc-annotate: {e}")
         
     | 
| 
      
 1485 
     | 
    
         
            +
                    return BaseInteractions([], [], [], [], [])
         
     | 
| 
      
 1486 
     | 
    
         
            +
             
     | 
| 
      
 1487 
     | 
    
         
            +
                adapter = MCAnnotateAdapter()
         
     | 
| 
      
 1488 
     | 
    
         
            +
                (
         
     | 
| 
      
 1489 
     | 
    
         
            +
                    base_pairs,
         
     | 
| 
      
 1490 
     | 
    
         
            +
                    stackings,
         
     | 
| 
      
 1491 
     | 
    
         
            +
                    base_ribose_interactions,
         
     | 
| 
      
 1492 
     | 
    
         
            +
                    base_phosphate_interactions,
         
     | 
| 
      
 1493 
     | 
    
         
            +
                    other_interactions,
         
     | 
| 
      
 1494 
     | 
    
         
            +
                ) = adapter.analyze_by_mc_annotate(pdb_content, mc_result)
         
     | 
| 
      
 1495 
     | 
    
         
            +
             
     | 
| 
      
 1496 
     | 
    
         
            +
                return BaseInteractions.from_structure3d(
         
     | 
| 
      
 1497 
     | 
    
         
            +
                    structure3d,
         
     | 
| 
      
 1498 
     | 
    
         
            +
                    base_pairs,
         
     | 
| 
      
 1499 
     | 
    
         
            +
                    stackings,
         
     | 
| 
      
 1500 
     | 
    
         
            +
                    base_ribose_interactions,
         
     | 
| 
      
 1501 
     | 
    
         
            +
                    base_phosphate_interactions,
         
     | 
| 
      
 1502 
     | 
    
         
            +
                    other_interactions,
         
     | 
| 
      
 1503 
     | 
    
         
            +
                )
         
     | 
| 
      
 1504 
     | 
    
         
            +
             
     | 
| 
      
 1505 
     | 
    
         
            +
             
     | 
| 
       1160 
1506 
     | 
    
         
             
            def parse_external_output(
         
     | 
| 
       1161 
1507 
     | 
    
         
             
                file_paths: List[str], tool: ExternalTool, structure3d: Structure3D
         
     | 
| 
       1162 
1508 
     | 
    
         
             
            ) -> BaseInteractions:
         
     | 
| 
         @@ -1183,6 +1529,8 @@ def parse_external_output( 
     | 
|
| 
       1183 
1529 
     | 
    
         
             
                    return parse_rnaview_output(file_paths, structure3d)
         
     | 
| 
       1184 
1530 
     | 
    
         
             
                elif tool == ExternalTool.BARNABA:
         
     | 
| 
       1185 
1531 
     | 
    
         
             
                    return parse_barnaba_output(file_paths, structure3d)
         
     | 
| 
      
 1532 
     | 
    
         
            +
                elif tool == ExternalTool.MCANNOTATE:
         
     | 
| 
      
 1533 
     | 
    
         
            +
                    return parse_mcannotate_output(file_paths, structure3d)
         
     | 
| 
       1186 
1534 
     | 
    
         
             
                else:
         
     | 
| 
       1187 
1535 
     | 
    
         
             
                    raise ValueError(f"Unsupported external tool: {tool}")
         
     | 
| 
       1188 
1536 
     | 
    
         | 
| 
         @@ -1259,6 +1607,9 @@ def process_external_tool_output( 
     | 
|
| 
       1259 
1607 
     | 
    
         
             
                if not external_file_paths:
         
     | 
| 
       1260 
1608 
     | 
    
         
             
                    # For MAXIT or when no external files are provided, use the input file
         
     | 
| 
       1261 
1609 
     | 
    
         
             
                    file_paths_to_process = [input_file_path]
         
     | 
| 
      
 1610 
     | 
    
         
            +
                elif tool == ExternalTool.MCANNOTATE:
         
     | 
| 
      
 1611 
     | 
    
         
            +
                    # MC-Annotate requires both the stdout and the PDB file
         
     | 
| 
      
 1612 
     | 
    
         
            +
                    file_paths_to_process = external_file_paths + [input_file_path]
         
     | 
| 
       1262 
1613 
     | 
    
         
             
                else:
         
     | 
| 
       1263 
1614 
     | 
    
         
             
                    # Process all external files
         
     | 
| 
       1264 
1615 
     | 
    
         
             
                    file_paths_to_process = external_file_paths
         
     | 
    
        rnapolis/common.py
    CHANGED
    
    | 
         @@ -1084,33 +1084,41 @@ class BaseInteractions: 
     | 
|
| 
       1084 
1084 
     | 
    
         
             
                    base_phosphate_interactions: List[BasePhosphate],
         
     | 
| 
       1085 
1085 
     | 
    
         
             
                    other_interactions: List[OtherInteraction],
         
     | 
| 
       1086 
1086 
     | 
    
         
             
                ) -> "BaseInteractions":
         
     | 
| 
       1087 
     | 
    
         
            -
                     
     | 
| 
       1088 
     | 
    
         
            -
                     
     | 
| 
       1089 
     | 
    
         
            -
                     
     | 
| 
      
 1087 
     | 
    
         
            +
                    cni2residue = {}
         
     | 
| 
      
 1088 
     | 
    
         
            +
                    cni2label = {}
         
     | 
| 
      
 1089 
     | 
    
         
            +
                    cni2auth = {}
         
     | 
| 
       1090 
1090 
     | 
    
         | 
| 
       1091 
1091 
     | 
    
         
             
                    for residue3d in structure3d.residues:
         
     | 
| 
       1092 
     | 
    
         
            -
                         
     | 
| 
       1093 
     | 
    
         
            -
                         
     | 
| 
       1094 
     | 
    
         
            -
                         
     | 
| 
      
 1092 
     | 
    
         
            +
                        cni = (residue3d.chain, residue3d.number, residue3d.icode or None)
         
     | 
| 
      
 1093 
     | 
    
         
            +
                        cni2auth[cni] = residue3d.auth
         
     | 
| 
      
 1094 
     | 
    
         
            +
                        cni2label[cni] = residue3d.label
         
     | 
| 
      
 1095 
     | 
    
         
            +
                        cni2residue[cni] = residue3d
         
     | 
| 
       1095 
1096 
     | 
    
         | 
| 
       1096 
1097 
     | 
    
         
             
                    def unify_nt(nt: Residue) -> Residue:
         
     | 
| 
       1097 
1098 
     | 
    
         
             
                        if nt.auth is not None and nt.label is not None:
         
     | 
| 
       1098 
1099 
     | 
    
         
             
                            return nt
         
     | 
| 
      
 1100 
     | 
    
         
            +
                        cni = (nt.chain, nt.number, nt.icode or None)
         
     | 
| 
       1099 
1101 
     | 
    
         
             
                        if nt.auth is not None:
         
     | 
| 
       1100 
     | 
    
         
            -
                            return Residue(label= 
     | 
| 
      
 1102 
     | 
    
         
            +
                            return Residue(label=cni2label.get(cni, None), auth=nt.auth)
         
     | 
| 
       1101 
1103 
     | 
    
         
             
                        if nt.label is not None:
         
     | 
| 
       1102 
     | 
    
         
            -
                            return Residue(label=nt.label, auth= 
     | 
| 
      
 1104 
     | 
    
         
            +
                            return Residue(label=nt.label, auth=cni2auth.get(cni, None))
         
     | 
| 
       1103 
1105 
     | 
    
         
             
                        return nt
         
     | 
| 
       1104 
1106 
     | 
    
         | 
| 
       1105 
1107 
     | 
    
         
             
                    base_pairs_new = []
         
     | 
| 
       1106 
1108 
     | 
    
         
             
                    for base_pair in base_pairs:
         
     | 
| 
       1107 
1109 
     | 
    
         
             
                        nt1 = unify_nt(base_pair.nt1)
         
     | 
| 
       1108 
1110 
     | 
    
         
             
                        nt2 = unify_nt(base_pair.nt2)
         
     | 
| 
       1109 
     | 
    
         
            -
             
     | 
| 
       1110 
     | 
    
         
            -
             
     | 
| 
       1111 
     | 
    
         
            -
             
     | 
| 
       1112 
     | 
    
         
            -
             
     | 
| 
       1113 
     | 
    
         
            -
             
     | 
| 
      
 1111 
     | 
    
         
            +
             
     | 
| 
      
 1112 
     | 
    
         
            +
                        cni1 = (nt1.chain, nt1.number, nt1.icode or None)
         
     | 
| 
      
 1113 
     | 
    
         
            +
                        cni2 = (nt2.chain, nt2.number, nt2.icode or None)
         
     | 
| 
      
 1114 
     | 
    
         
            +
                        if cni1 not in cni2residue or cni2 not in cni2residue:
         
     | 
| 
      
 1115 
     | 
    
         
            +
                            saenger = base_pair.saenger
         
     | 
| 
      
 1116 
     | 
    
         
            +
                        else:
         
     | 
| 
      
 1117 
     | 
    
         
            +
                            saenger = base_pair.saenger or Saenger.from_leontis_westhof(
         
     | 
| 
      
 1118 
     | 
    
         
            +
                                cni2residue[cni1].one_letter_name,
         
     | 
| 
      
 1119 
     | 
    
         
            +
                                cni2residue[cni2].one_letter_name,
         
     | 
| 
      
 1120 
     | 
    
         
            +
                                base_pair.lw,
         
     | 
| 
      
 1121 
     | 
    
         
            +
                            )
         
     | 
| 
       1114 
1122 
     | 
    
         
             
                        if (
         
     | 
| 
       1115 
1123 
     | 
    
         
             
                            nt1 != base_pair.nt1
         
     | 
| 
       1116 
1124 
     | 
    
         
             
                            or nt2 != base_pair.nt2
         
     | 
| 
         @@ -1,8 +1,8 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            rnapolis/adapter.py,sha256= 
     | 
| 
      
 1 
     | 
    
         
            +
            rnapolis/adapter.py,sha256=6hJTweIqUXH8CEGvi8oupFzk5etkIt8Q2bqRvgsqako,62169
         
     | 
| 
       2 
2 
     | 
    
         
             
            rnapolis/aligner.py,sha256=o7rQyjAZ3n4VXcnSPY3HVB8nLNRkVbl552O3NVh0mfg,3429
         
     | 
| 
       3 
3 
     | 
    
         
             
            rnapolis/annotator.py,sha256=HA2hfEUXdmBElObqRlASAB1FgkysjiHgwMTjEhsDiDE,30277
         
     | 
| 
       4 
4 
     | 
    
         
             
            rnapolis/clashfinder.py,sha256=AC9_tIx7QIk57sELq_aKfU1u3UMOXbgcccQeGHhMR6c,8517
         
     | 
| 
       5 
     | 
    
         
            -
            rnapolis/common.py,sha256= 
     | 
| 
      
 5 
     | 
    
         
            +
            rnapolis/common.py,sha256=qifqTIiq43jeR1xKK3301PbRMo7vaZgjQauG-G7asSc,36686
         
     | 
| 
       6 
6 
     | 
    
         
             
            rnapolis/component_A.csv,sha256=koirS-AwUZwoYGItT8yn3wS6Idvmh2FANfTQcOS_xh8,2897
         
     | 
| 
       7 
7 
     | 
    
         
             
            rnapolis/component_C.csv,sha256=NtvsAu_YrUgTjzZm3j4poW4IZ99x3dPARB09XVIiMCc,2803
         
     | 
| 
       8 
8 
     | 
    
         
             
            rnapolis/component_G.csv,sha256=Z5wl8OnHRyx4XhTyBiWgRZiEvmZXhoxtVRH8bn6Vxf0,2898
         
     | 
| 
         @@ -22,9 +22,9 @@ rnapolis/tertiary_v2.py,sha256=SgijTv0bPqMJwsMqyQk0O8QAnS2Ozk45vk8igxt9hRs,38001 
     | 
|
| 
       22 
22 
     | 
    
         
             
            rnapolis/transformer.py,sha256=aC0nBmHHJf5TyLvBIV57Jj3tlwpvHbPo347opfAOlQA,3844
         
     | 
| 
       23 
23 
     | 
    
         
             
            rnapolis/unifier.py,sha256=2ge7IB9FdRgzSAiVD39U_ciwtdDJ2fGzf8mUIudbrqY,5820
         
     | 
| 
       24 
24 
     | 
    
         
             
            rnapolis/util.py,sha256=IdquFO3PV1_KDqodjupzm0Rqvgy0CeSzxGHaGEHYXVU,543
         
     | 
| 
       25 
     | 
    
         
            -
            rnapolis-0.10. 
     | 
| 
       26 
     | 
    
         
            -
            rnapolis-0.10. 
     | 
| 
       27 
     | 
    
         
            -
            rnapolis-0.10. 
     | 
| 
       28 
     | 
    
         
            -
            rnapolis-0.10. 
     | 
| 
       29 
     | 
    
         
            -
            rnapolis-0.10. 
     | 
| 
       30 
     | 
    
         
            -
            rnapolis-0.10. 
     | 
| 
      
 25 
     | 
    
         
            +
            rnapolis-0.10.7.dist-info/licenses/LICENSE,sha256=ZGRu12MzCgbYA-Lt8MyBlmjvPZh7xfiD5u5wBx0enq4,1066
         
     | 
| 
      
 26 
     | 
    
         
            +
            rnapolis-0.10.7.dist-info/METADATA,sha256=QPuGPZ96VIjvQPiLkk4bS4vstWqO6cok6e4vID33vg0,54611
         
     | 
| 
      
 27 
     | 
    
         
            +
            rnapolis-0.10.7.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
         
     | 
| 
      
 28 
     | 
    
         
            +
            rnapolis-0.10.7.dist-info/entry_points.txt,sha256=MZMWnYBUYnis-zWDmFfuA5yXtU3W5YdQrm5HA5LrkeM,474
         
     | 
| 
      
 29 
     | 
    
         
            +
            rnapolis-0.10.7.dist-info/top_level.txt,sha256=LcO18koxZcWoJ21KDRRRo_tyIbmXL5z61dPitZpy8yc,9
         
     | 
| 
      
 30 
     | 
    
         
            +
            rnapolis-0.10.7.dist-info/RECORD,,
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     | 
| 
         
            File without changes
         
     |