1# 2# Gramps - a GTK+/GNOME based genealogy program 3# 4# Copyright (C) 2003-2005 Donald N. Allingham 5# 6# This program is free software; you can redistribute it and/or modify 7# it under the terms of the GNU General Public License as published by 8# the Free Software Foundation; either version 2 of the License, or 9# (at your option) any later version. 10# 11# This program is distributed in the hope that it will be useful, 12# but WITHOUT ANY WARRANTY; without even the implied warranty of 13# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14# GNU General Public License for more details. 15# 16# You should have received a copy of the GNU General Public License 17# along with this program; if not, write to the Free Software 18# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. 19# 20""" 21Classes for relationships. 22""" 23 24#------------------------------------------------------------------------- 25# 26# Python modules 27# 28#------------------------------------------------------------------------- 29import logging 30 31#------------------------------------------------------------------------- 32# 33# Gramps modules 34# 35#------------------------------------------------------------------------- 36from .lib import Person, ChildRefType, EventType, FamilyRelType 37from .plug import PluginRegister, BasePluginManager 38from .const import GRAMPS_LOCALE as glocale 39_ = glocale.translation.sgettext 40 41MALE = Person.MALE 42FEMALE = Person.FEMALE 43UNKNOWN = Person.UNKNOWN 44 45LOG = logging.getLogger("gen.relationship") 46LOG.addHandler(logging.StreamHandler()) 47 48#------------------------------------------------------------------------- 49# 50# 51# 52#------------------------------------------------------------------------- 53 54_LEVEL_NAME = ["", "first", "second", "third", "fourth", "fifth", "sixth", 55 "seventh", "eighth", "ninth", "tenth", "eleventh", "twelfth", 56 "thirteenth", "fourteenth", "fifteenth", "sixteenth", 57 "seventeenth", "eighteenth", "nineteenth", "twentieth", 58 "twenty-first", "twenty-second", "twenty-third", "twenty-fourth", 59 "twenty-fifth", "twenty-sixth", "twenty-seventh", "twenty-eighth", 60 "twenty-ninth", "thirtieth", "thirty-first", "thirty-second", 61 "thirty-third", "thirty-fourth", "thirty-fifth", "thirty-sixth", 62 "thirty-seventh", "thirty-eighth", "thirty-ninth", "fortieth", 63 "forty-first", "forty-second", "forty-third", "forty-fourth", 64 "forty-fifth", "forty-sixth", "forty-seventh", "forty-eighth", 65 "forty-ninth", "fiftieth" ] 66 67_REMOVED_LEVEL = ["", " once removed", " twice removed", 68 " three times removed", 69 " four times removed", " five times removed", 70 " six times removed", 71 " seven times removed", " eight times removed", 72 " nine times removed", 73 " ten times removed", " eleven times removed", 74 " twelve times removed", 75 " thirteen times removed", " fourteen times removed", 76 " fifteen times removed", 77 " sixteen times removed", " seventeen times removed", 78 " eighteen times removed", 79 " nineteen times removed", " twenty times removed", 80 " twenty-one times removed", " twenty-two times removed", 81 " twenty-three times removed", " twenty-four times removed", 82 " twenty-five times removed", " twenty-six times removed", 83 " twenty-seven times removed", " twenty-eight times removed", 84 " twenty-nine times removed", " thirty times removed", 85 " thirty-one times removed", " thirty-two times removed", 86 " thirty-three times removed", " thirty-four times removed", 87 " thirty-five times removed", " thirty-six times removed", 88 " thirty-seven times removed", " thirty-eight times removed", 89 " thirty-nine times removed", " forty times removed", 90 " forty-one times removed", " forty-two times removed", 91 " forty-three times removed", " forty-four times removed", 92 " forty-five times removed", " forty-six times removed", 93 " forty-seven times removed", " forty-eight times removed", 94 " forty-nine times removed", " fifty times removed", ] 95 96_PARENTS_LEVEL = ["", "parents", "grandparents", "great grandparents", 97 "second great grandparents", 98 "third great grandparents", 99 "fourth great grandparents", 100 "fifth great grandparents", 101 "sixth great grandparents", 102 "seventh great grandparents", 103 "eighth great grandparents", 104 "ninth great grandparents", 105 "tenth great grandparents", 106 "eleventh great grandparents", 107 "twelfth great grandparents", 108 "thirteenth great grandparents", 109 "fourteenth great grandparents", 110 "fifteenth great grandparents", 111 "sixteenth great grandparents", 112 "seventeenth great grandparents", 113 "eighteenth great grandparents", 114 "nineteenth great grandparents", 115 "twentieth great grandparents", 116 "twenty-first great grandparents", 117 "twenty-second great grandparents", 118 "twenty-third great grandparents", 119 "twenty-fourth great grandparents", 120 "twenty-fifth great grandparents", 121 "twenty-sixth great grandparents", 122 "twenty-seventh great grandparents", 123 "twenty-eighth great grandparents", 124 "twenty-ninth great grandparents", 125 "thirtieth great grandparents", 126 "thirty-first great grandparents", 127 "thirty-second great grandparents", 128 "thirty-third great grandparents", 129 "thirty-fourth great grandparents", 130 "thirty-fifth great grandparents", 131 "thirty-sixth great grandparents", 132 "thirty-seventh great grandparents", 133 "thirty-eighth great grandparents", 134 "thirty-ninth great grandparents", 135 "fortieth great grandparents", 136 "forty-first great grandparents", 137 "forty-second great grandparents", 138 "forty-third great grandparents", 139 "forty-fourth great grandparents", 140 "forty-fifth great grandparents", 141 "forty-sixth great grandparents", 142 "forty-seventh great grandparents", 143 "forty-eighth great grandparents", 144 "forty-ninth great grandparents", 145 "fiftieth great grandparents", ] 146 147_FATHER_LEVEL = ["", "%(step)sfather%(inlaw)s", "%(step)sgrandfather%(inlaw)s", 148 "great %(step)sgrandfather%(inlaw)s", 149 "second great %(step)sgrandfather%(inlaw)s", 150 "third great %(step)sgrandfather%(inlaw)s", 151 "fourth great %(step)sgrandfather%(inlaw)s", 152 "fifth great %(step)sgrandfather%(inlaw)s", 153 "sixth great %(step)sgrandfather%(inlaw)s", 154 "seventh great %(step)sgrandfather%(inlaw)s", 155 "eighth great %(step)sgrandfather%(inlaw)s", 156 "ninth great %(step)sgrandfather%(inlaw)s", 157 "tenth great %(step)sgrandfather%(inlaw)s", 158 "eleventh great %(step)sgrandfather%(inlaw)s", 159 "twelfth great %(step)sgrandfather%(inlaw)s", 160 "thirteenth great %(step)sgrandfather%(inlaw)s", 161 "fourteenth great %(step)sgrandfather%(inlaw)s", 162 "fifteenth great %(step)sgrandfather%(inlaw)s", 163 "sixteenth great %(step)sgrandfather%(inlaw)s", 164 "seventeenth great %(step)sgrandfather%(inlaw)s", 165 "eighteenth great %(step)sgrandfather%(inlaw)s", 166 "nineteenth great %(step)sgrandfather%(inlaw)s", 167 "twentieth great %(step)sgrandfather%(inlaw)s", 168 "twenty-first great %(step)sgrandfather%(inlaw)s", 169 "twenty-second great %(step)sgrandfather%(inlaw)s", 170 "twenty-third great %(step)sgrandfather%(inlaw)s", 171 "twenty-fourth great %(step)sgrandfather%(inlaw)s", 172 "twenty-fifth great %(step)sgrandfather%(inlaw)s", 173 "twenty-sixth great %(step)sgrandfather%(inlaw)s", 174 "twenty-seventh great %(step)sgrandfather%(inlaw)s", 175 "twenty-eighth great %(step)sgrandfather%(inlaw)s", 176 "twenty-ninth great %(step)sgrandfather%(inlaw)s", 177 "thirtieth great %(step)sgrandfather%(inlaw)s", 178 "thirty-first great %(step)sgrandfather%(inlaw)s", 179 "thirty-second great %(step)sgrandfather%(inlaw)s", 180 "thirty-third great %(step)sgrandfather%(inlaw)s", 181 "thirty-fourth great %(step)sgrandfather%(inlaw)s", 182 "thirty-fifth great %(step)sgrandfather%(inlaw)s", 183 "thirty-sixth great %(step)sgrandfather%(inlaw)s", 184 "thirty-seventh great %(step)sgrandfather%(inlaw)s", 185 "thirty-eighth great %(step)sgrandfather%(inlaw)s", 186 "thirty-ninth great %(step)sgrandfather%(inlaw)s", 187 "fourtieth great %(step)sgrandfather%(inlaw)s", 188 "forty-first great %(step)sgrandfather%(inlaw)s", 189 "forty-second great %(step)sgrandfather%(inlaw)s", 190 "forty-third great %(step)sgrandfather%(inlaw)s", 191 "forty-fourth great %(step)sgrandfather%(inlaw)s", 192 "forty-fifth great %(step)sgrandfather%(inlaw)s", 193 "forty-sixth great %(step)sgrandfather%(inlaw)s", 194 "forty-seventh great %(step)sgrandfather%(inlaw)s", 195 "forty-eighth great %(step)sgrandfather%(inlaw)s", 196 "forty-ninth great %(step)sgrandfather%(inlaw)s", 197 "fiftieth great %(step)sgrandfather%(inlaw)s", ] 198 199_MOTHER_LEVEL = ["", "%(step)smother%(inlaw)s", 200 "%(step)sgrandmother%(inlaw)s", 201 "great %(step)sgrandmother%(inlaw)s", 202 "second great %(step)sgrandmother%(inlaw)s", 203 "third great %(step)sgrandmother%(inlaw)s", 204 "fourth great %(step)sgrandmother%(inlaw)s", 205 "fifth great %(step)sgrandmother%(inlaw)s", 206 "sixth great %(step)sgrandmother%(inlaw)s", 207 "seventh great %(step)sgrandmother%(inlaw)s", 208 "eighth great %(step)sgrandmother%(inlaw)s", 209 "ninth great %(step)sgrandmother%(inlaw)s", 210 "tenth great %(step)sgrandmother%(inlaw)s", 211 "eleventh great %(step)sgrandmother%(inlaw)s", 212 "twelfth great %(step)sgrandmother%(inlaw)s", 213 "thirteenth great %(step)sgrandmother%(inlaw)s", 214 "fourteenth great %(step)sgrandmother%(inlaw)s", 215 "fifteenth great %(step)sgrandmother%(inlaw)s", 216 "sixteenth great %(step)sgrandmother%(inlaw)s", 217 "seventeenth great %(step)sgrandmother%(inlaw)s", 218 "eighteenth great %(step)sgrandmother%(inlaw)s", 219 "nineteenth great %(step)sgrandmother%(inlaw)s", 220 "twentieth great %(step)sgrandmother%(inlaw)s", 221 "twenty-first great %(step)sgrandmother%(inlaw)s", 222 "twenty-second great %(step)sgrandmother%(inlaw)s", 223 "twenty-third great %(step)sgrandmother%(inlaw)s", 224 "twenty-fourth great %(step)sgrandmother%(inlaw)s", 225 "twenty-fifth great %(step)sgrandmother%(inlaw)s", 226 "twenty-sixth great %(step)sgrandmother%(inlaw)s", 227 "twenty-seventh great %(step)sgrandmother%(inlaw)s", 228 "twenty-eighth great %(step)sgrandmother%(inlaw)s", 229 "twenty-ninth great %(step)sgrandmother%(inlaw)s", 230 "thirtieth great %(step)sgrandmother%(inlaw)s", 231 "thirty-first great %(step)sgrandmother%(inlaw)s", 232 "thirty-second great %(step)sgrandmother%(inlaw)s", 233 "thirty-third great %(step)sgrandmother%(inlaw)s", 234 "thirty-forth great %(step)sgrandmother%(inlaw)s", 235 "thirty-fifth great %(step)sgrandmother%(inlaw)s", 236 "thirty-sixth great %(step)sgrandmother%(inlaw)s", 237 "thirty-seventh great %(step)sgrandmother%(inlaw)s", 238 "thirty-eighth great %(step)sgrandmother%(inlaw)s", 239 "thirty-ninth great %(step)sgrandmother%(inlaw)s", 240 "fourtieth great %(step)sgrandmother%(inlaw)s", 241 "forty-first great %(step)sgrandmother%(inlaw)s", 242 "forty-second great %(step)sgrandmother%(inlaw)s", 243 "forty-third great %(step)sgrandmother%(inlaw)s", 244 "forty-fourth great %(step)sgrandmother%(inlaw)s", 245 "forty-fifth great %(step)sgrandmother%(inlaw)s", 246 "forty-sixth great %(step)sgrandmother%(inlaw)s", 247 "forty-seventh great %(step)sgrandmother%(inlaw)s", 248 "forty-eighth great %(step)sgrandmother%(inlaw)s", 249 "forty-ninth great %(step)sgrandmother%(inlaw)s", 250 "fiftieth great %(step)sgrandmother%(inlaw)s", ] 251 252_SON_LEVEL = ["", "%(step)sson%(inlaw)s", "%(step)sgrandson%(inlaw)s", 253 "great %(step)sgrandson%(inlaw)s", 254 "second great %(step)sgrandson%(inlaw)s", 255 "third great %(step)sgrandson%(inlaw)s", 256 "fourth great %(step)sgrandson%(inlaw)s", 257 "fifth great %(step)sgrandson%(inlaw)s", 258 "sixth great %(step)sgrandson%(inlaw)s", 259 "seventh great %(step)sgrandson%(inlaw)s", 260 "eighth great %(step)sgrandson%(inlaw)s", 261 "ninth great %(step)sgrandson%(inlaw)s", 262 "tenth great %(step)sgrandson%(inlaw)s", 263 "eleventh great %(step)sgrandson%(inlaw)s", 264 "twelfth great %(step)sgrandson%(inlaw)s", 265 "thirteenth great %(step)sgrandson%(inlaw)s", 266 "fourteenth great %(step)sgrandson%(inlaw)s", 267 "fifteenth great %(step)sgrandson%(inlaw)s", 268 "sixteenth great %(step)sgrandson%(inlaw)s", 269 "seventeenth great %(step)sgrandson%(inlaw)s", 270 "eighteenth great %(step)sgrandson%(inlaw)s", 271 "nineteenth great %(step)sgrandson%(inlaw)s", 272 "twentieth great %(step)sgrandson%(inlaw)s", 273 "twenty-first great %(step)sgrandson%(inlaw)s", 274 "twenty-second great %(step)sgrandson%(inlaw)s", 275 "twenty-third great %(step)sgrandson%(inlaw)s", 276 "twenty-fourth great %(step)sgrandson%(inlaw)s", 277 "twenty-fifth great %(step)sgrandson%(inlaw)s", 278 "twenty-sixth great %(step)sgrandson%(inlaw)s", 279 "twenty-seventh great %(step)sgrandson%(inlaw)s", 280 "twenty-eighth great %(step)sgrandson%(inlaw)s", 281 "twenty-ninth great %(step)sgrandson%(inlaw)s", 282 "thirtieth great %(step)sgrandson%(inlaw)s", 283 "thirty-first great %(step)sgrandson%(inlaw)s", 284 "thirty-second great %(step)sgrandson%(inlaw)s", 285 "thirty-third great %(step)sgrandson%(inlaw)s", 286 "thirty-forth great %(step)sgrandson%(inlaw)s", 287 "thirty-fifth great %(step)sgrandson%(inlaw)s", 288 "thirty-sixth great %(step)sgrandson%(inlaw)s", 289 "thirty-seventh great %(step)sgrandson%(inlaw)s", 290 "thirty-eighth great %(step)sgrandson%(inlaw)s", 291 "thirty-ninth great %(step)sgrandson%(inlaw)s", 292 "fourtieth great %(step)sgrandson%(inlaw)s", 293 "forty-first great %(step)sgrandson%(inlaw)s", 294 "forty-second great %(step)sgrandson%(inlaw)s", 295 "forty-third great %(step)sgrandson%(inlaw)s", 296 "forty-fourth great %(step)sgrandson%(inlaw)s", 297 "forty-fifth great %(step)sgrandson%(inlaw)s", 298 "forty-sixth great %(step)sgrandson%(inlaw)s", 299 "forty-seventh great %(step)sgrandson%(inlaw)s", 300 "forty-eighth great %(step)sgrandson%(inlaw)s", 301 "forty-ninth great %(step)sgrandson%(inlaw)s", 302 "fiftieth great %(step)sgrandson%(inlaw)s", ] 303 304_DAUGHTER_LEVEL = ["", "%(step)sdaughter%(inlaw)s", 305 "%(step)sgranddaughter%(inlaw)s", 306 "great %(step)sgranddaughter%(inlaw)s", 307 "second great %(step)sgranddaughter%(inlaw)s", 308 "third great %(step)sgranddaughter%(inlaw)s", 309 "fourth great %(step)sgranddaughter%(inlaw)s", 310 "fifth great %(step)sgranddaughter%(inlaw)s", 311 "sixth great %(step)sgranddaughter%(inlaw)s", 312 "seventh great %(step)sgranddaughter%(inlaw)s", 313 "eighth great %(step)sgranddaughter%(inlaw)s", 314 "ninth great %(step)sgranddaughter%(inlaw)s", 315 "tenth great %(step)sgranddaughter%(inlaw)s", 316 "eleventh great %(step)sgranddaughter%(inlaw)s", 317 "twelfth great %(step)sgranddaughter%(inlaw)s", 318 "thirteenth great %(step)sgranddaughter%(inlaw)s", 319 "fourteenth great %(step)sgranddaughter%(inlaw)s", 320 "fifteenth great %(step)sgranddaughter%(inlaw)s", 321 "sixteenth great %(step)sgranddaughter%(inlaw)s", 322 "seventeenth great %(step)sgranddaughter%(inlaw)s", 323 "eighteenth great %(step)sgranddaughter%(inlaw)s", 324 "nineteenth great %(step)sgranddaughter%(inlaw)s", 325 "twentieth great %(step)sgranddaughter%(inlaw)s", 326 "twenty-first great %(step)sgranddaughter%(inlaw)s", 327 "twenty-second great %(step)sgranddaughter%(inlaw)s", 328 "twenty-third great %(step)sgranddaughter%(inlaw)s", 329 "twenty-fourth great %(step)sgranddaughter%(inlaw)s", 330 "twenty-fifth great %(step)sgranddaughter%(inlaw)s", 331 "twenty-sixth great %(step)sgranddaughter%(inlaw)s", 332 "twenty-seventh great %(step)sgranddaughter%(inlaw)s", 333 "twenty-eighth great %(step)sgranddaughter%(inlaw)s", 334 "twenty-ninth great %(step)sgranddaughter%(inlaw)s", 335 "thirtieth great %(step)sgranddaughter%(inlaw)s", 336 "thirty-first great %(step)sgranddaughter%(inlaw)s", 337 "thirty-second great %(step)sgranddaughter%(inlaw)s", 338 "thirty-third great %(step)sgranddaughter%(inlaw)s", 339 "thirty-forth great %(step)sgranddaughter%(inlaw)s", 340 "thirty-fifth great %(step)sgranddaughter%(inlaw)s", 341 "thirty-sixth great %(step)sgranddaughter%(inlaw)s", 342 "thirty-seventh great %(step)sgranddaughter%(inlaw)s", 343 "thirty-eighth great %(step)sgranddaughter%(inlaw)s", 344 "thirty-ninth great %(step)sgranddaughter%(inlaw)s", 345 "fourtieth great %(step)sgranddaughter%(inlaw)s", 346 "forty-first great %(step)sgranddaughter%(inlaw)s", 347 "forty-second great %(step)sgranddaughter%(inlaw)s", 348 "forty-third great %(step)sgranddaughter%(inlaw)s", 349 "forty-fourth great %(step)sgranddaughter%(inlaw)s", 350 "forty-fifth great %(step)sgranddaughter%(inlaw)s", 351 "forty-sixth great %(step)sgranddaughter%(inlaw)s", 352 "forty-seventh great %(step)sgranddaughter%(inlaw)s", 353 "forty-eighth great %(step)sgranddaughter%(inlaw)s", 354 "forty-ninth great %(step)sgranddaughter%(inlaw)s", 355 "fiftieth great %(step)sgranddaughter%(inlaw)s", ] 356 357_SISTER_LEVEL = ["", "%(step)ssister%(inlaw)s", "%(step)saunt%(inlaw)s", 358 "%(step)sgrandaunt%(inlaw)s", 359 "great %(step)sgrandaunt%(inlaw)s", 360 "second great %(step)sgrandaunt%(inlaw)s", 361 "third great %(step)sgrandaunt%(inlaw)s", 362 "fourth great %(step)sgrandaunt%(inlaw)s", 363 "fifth great %(step)sgrandaunt%(inlaw)s", 364 "sixth great %(step)sgrandaunt%(inlaw)s", 365 "seventh great %(step)sgrandaunt%(inlaw)s", 366 "eighth great %(step)sgrandaunt%(inlaw)s", 367 "ninth great %(step)sgrandaunt%(inlaw)s", 368 "tenth great %(step)sgrandaunt%(inlaw)s", 369 "eleventh great %(step)sgrandaunt%(inlaw)s", 370 "twelfth great %(step)sgrandaunt%(inlaw)s", 371 "thirteenth great %(step)sgrandaunt%(inlaw)s", 372 "fourteenth great %(step)sgrandaunt%(inlaw)s", 373 "fifteenth great %(step)sgrandaunt%(inlaw)s", 374 "sixteenth great %(step)sgrandaunt%(inlaw)s", 375 "seventeenth great %(step)sgrandaunt%(inlaw)s", 376 "eighteenth great %(step)sgrandaunt%(inlaw)s", 377 "nineteenth great %(step)sgrandaunt%(inlaw)s", 378 "twentieth great %(step)sgrandaunt%(inlaw)s", 379 "twenty-first great %(step)sgrandaunt%(inlaw)s", 380 "twenty-second great %(step)sgrandaunt%(inlaw)s", 381 "twenty-third great %(step)sgrandaunt%(inlaw)s", 382 "twenty-fourth great %(step)sgrandaunt%(inlaw)s", 383 "twenty-fifth great %(step)sgrandaunt%(inlaw)s", 384 "twenty-sixth great %(step)sgrandaunt%(inlaw)s", 385 "twenty-seventh great %(step)sgrandaunt%(inlaw)s", 386 "twenty-eighth great %(step)sgrandaunt%(inlaw)s", 387 "twenty-ninth great %(step)sgrandaunt%(inlaw)s", 388 "thirtieth great %(step)sgrandaunt%(inlaw)s", 389 "thirty-first great %(step)sgrandaunt%(inlaw)s", 390 "thirty-second great %(step)sgrandaunt%(inlaw)s", 391 "thirty-third great %(step)sgrandaunt%(inlaw)s", 392 "thirty-forth great %(step)sgrandaunt%(inlaw)s", 393 "thirty-fifth great %(step)sgrandaunt%(inlaw)s", 394 "thirty-sixth great %(step)sgrandaunt%(inlaw)s", 395 "thirty-seventh great %(step)sgrandaunt%(inlaw)s", 396 "thirty-eighth great %(step)sgrandaunt%(inlaw)s", 397 "thirty-ninth great %(step)sgrandaunt%(inlaw)s", 398 "fourtieth great %(step)sgrandaunt%(inlaw)s", 399 "forty-first great %(step)sgrandaunt%(inlaw)s", 400 "forty-second great %(step)sgrandaunt%(inlaw)s", 401 "forty-third great %(step)sgrandaunt%(inlaw)s", 402 "forty-fourth great %(step)sgrandaunt%(inlaw)s", 403 "forty-fifth great %(step)sgrandaunt%(inlaw)s", 404 "forty-sixth great %(step)sgrandaunt%(inlaw)s", 405 "forty-seventh great %(step)sgrandaunt%(inlaw)s", 406 "forty-eighth great %(step)sgrandaunt%(inlaw)s", 407 "forty-ninth great %(step)sgrandaunt%(inlaw)s", 408 "fiftieth great %(step)sgrandaunt%(inlaw)s", ] 409 410_BROTHER_LEVEL = ["", "%(step)sbrother%(inlaw)s", "%(step)suncle%(inlaw)s", 411 "%(step)sgranduncle%(inlaw)s", 412 "great %(step)sgranduncle%(inlaw)s", 413 "second great %(step)sgranduncle%(inlaw)s", 414 "third great %(step)sgranduncle%(inlaw)s", 415 "fourth great %(step)sgranduncle%(inlaw)s", 416 "fifth great %(step)sgranduncle%(inlaw)s", 417 "sixth great %(step)sgranduncle%(inlaw)s", 418 "seventh great %(step)sgranduncle%(inlaw)s", 419 "eighth great %(step)sgranduncle%(inlaw)s", 420 "ninth great %(step)sgranduncle%(inlaw)s", 421 "tenth great %(step)sgranduncle%(inlaw)s", 422 "eleventh great %(step)sgranduncle%(inlaw)s", 423 "twelfth great %(step)sgranduncle%(inlaw)s", 424 "thirteenth great %(step)sgranduncle%(inlaw)s", 425 "fourteenth great %(step)sgranduncle%(inlaw)s", 426 "fifteenth great %(step)sgranduncle%(inlaw)s", 427 "sixteenth great %(step)sgranduncle%(inlaw)s", 428 "seventeenth great %(step)sgranduncle%(inlaw)s", 429 "eighteenth great %(step)sgranduncle%(inlaw)s", 430 "nineteenth great %(step)sgranduncle%(inlaw)s", 431 "twentieth great %(step)sgranduncle%(inlaw)s", 432 "twenty-first great %(step)sgranduncle%(inlaw)s", 433 "twenty-second great %(step)sgranduncle%(inlaw)s", 434 "twenty-third great %(step)sgranduncle%(inlaw)s", 435 "twenty-fourth great %(step)sgranduncle%(inlaw)s", 436 "twenty-fifth great %(step)sgranduncle%(inlaw)s", 437 "twenty-sixth great %(step)sgranduncle%(inlaw)s", 438 "twenty-seventh great %(step)sgranduncle%(inlaw)s", 439 "twenty-eighth great %(step)sgranduncle%(inlaw)s", 440 "twenty-ninth great %(step)sgranduncle%(inlaw)s", 441 "thirtieth great %(step)sgranduncle%(inlaw)s", 442 "thirty-first great %(step)sgranduncle%(inlaw)s", 443 "thirty-second great %(step)sgranduncle%(inlaw)s", 444 "thirty-third great %(step)sgranduncle%(inlaw)s", 445 "thirty-fourth great %(step)sgranduncle%(inlaw)s", 446 "thirty-fifth great %(step)sgranduncle%(inlaw)s", 447 "thirty-sixth great %(step)sgranduncle%(inlaw)s", 448 "thirty-seventh great %(step)sgranduncle%(inlaw)s", 449 "thirty-eighth great %(step)sgranduncle%(inlaw)s", 450 "thirty-ninth great %(step)sgranduncle%(inlaw)s", 451 "fourtieth great %(step)sgranduncle%(inlaw)s", 452 "forty-first great %(step)sgranduncle%(inlaw)s", 453 "forty-second great %(step)sgranduncle%(inlaw)s", 454 "forty-third great %(step)sgranduncle%(inlaw)s", 455 "forty-fourth great %(step)sgranduncle%(inlaw)s", 456 "forty-fifth great %(step)sgranduncle%(inlaw)s", 457 "forty-sixth great %(step)sgranduncle%(inlaw)s", 458 "forty-seventh great %(step)sgranduncle%(inlaw)s", 459 "forty-eighth great %(step)sgranduncle%(inlaw)s", 460 "forty-ninth great %(step)sgranduncle%(inlaw)s", 461 "fiftieth great %(step)sgranduncle%(inlaw)s", ] 462 463_NEPHEW_LEVEL = ["", "%(step)snephew%(inlaw)s", "%(step)sgrandnephew%(inlaw)s", 464 "great %(step)sgrandnephew%(inlaw)s", 465 "second great %(step)sgrandnephew%(inlaw)s", 466 "third great %(step)sgrandnephew%(inlaw)s", 467 "fourth great %(step)sgrandnephew%(inlaw)s", 468 "fifth great %(step)sgrandnephew%(inlaw)s", 469 "sixth great %(step)sgrandnephew%(inlaw)s", 470 "seventh great %(step)sgrandnephew%(inlaw)s", 471 "eighth great %(step)sgrandnephew%(inlaw)s", 472 "ninth great %(step)sgrandnephew%(inlaw)s", 473 "tenth great %(step)sgrandnephew%(inlaw)s", 474 "eleventh great %(step)sgrandnephew%(inlaw)s", 475 "twelfth great %(step)sgrandnephew%(inlaw)s", 476 "thirteenth great %(step)sgrandnephew%(inlaw)s", 477 "fourteenth great %(step)sgrandnephew%(inlaw)s", 478 "fifteenth great %(step)sgrandnephew%(inlaw)s", 479 "sixteenth great %(step)sgrandnephew%(inlaw)s", 480 "seventeenth great %(step)sgrandnephew%(inlaw)s", 481 "eighteenth great %(step)sgrandnephew%(inlaw)s", 482 "nineteenth great %(step)sgrandnephew%(inlaw)s", 483 "twentieth great %(step)sgrandnephew%(inlaw)s", 484 "twenty-first great %(step)sgrandnephew%(inlaw)s", 485 "twenty-second great %(step)sgrandnephew%(inlaw)s", 486 "twenty-third great %(step)sgrandnephew%(inlaw)s", 487 "twenty-fourth great %(step)sgrandnephew%(inlaw)s", 488 "twenty-fifth great %(step)sgrandnephew%(inlaw)s", 489 "twenty-sixth great %(step)sgrandnephew%(inlaw)s", 490 "twenty-seventh great %(step)sgrandnephew%(inlaw)s", 491 "twenty-eighth great %(step)sgrandnephew%(inlaw)s", 492 "twenty-ninth great %(step)sgrandnephew%(inlaw)s", 493 "thirtieth great %(step)sgrandnephew%(inlaw)s", 494 "thirty-first great %(step)sgrandnephew%(inlaw)s", 495 "thirty-second great %(step)sgrandnephew%(inlaw)s", 496 "thirty-third great %(step)sgrandnephew%(inlaw)s", 497 "thirty-fourth great %(step)sgrandnephew%(inlaw)s", 498 "thirty-fifth great %(step)sgrandnephew%(inlaw)s", 499 "thirty-sixth great %(step)sgrandnephew%(inlaw)s", 500 "thirty-seventh great %(step)sgrandnephew%(inlaw)s", 501 "thirty-eighth great %(step)sgrandnephew%(inlaw)s", 502 "thirty-ninth great %(step)sgrandnephew%(inlaw)s", 503 "fourtieth great %(step)sgrandnephew%(inlaw)s", 504 "forty-first great %(step)sgrandnephew%(inlaw)s", 505 "forty-second great %(step)sgrandnephew%(inlaw)s", 506 "forty-third great %(step)sgrandnephew%(inlaw)s", 507 "forty-fourth great %(step)sgrandnephew%(inlaw)s", 508 "forty-fifth great %(step)sgrandnephew%(inlaw)s", 509 "forty-sixth great %(step)sgrandnephew%(inlaw)s", 510 "forty-seventh great %(step)sgrandnephew%(inlaw)s", 511 "forty-eighth great %(step)sgrandnephew%(inlaw)s", 512 "forty-ninth great %(step)sgrandnephew%(inlaw)s", 513 "fiftieth great %(step)sgrandnephew%(inlaw)s", ] 514 515_NIECE_LEVEL = ["", "%(step)sniece%(inlaw)s", "%(step)sgrandniece%(inlaw)s", 516 "great %(step)sgrandniece%(inlaw)s", 517 "second great %(step)sgrandniece%(inlaw)s", 518 "third great %(step)sgrandniece%(inlaw)s", 519 "fourth great %(step)sgrandniece%(inlaw)s", 520 "fifth great %(step)sgrandniece%(inlaw)s", 521 "sixth great %(step)sgrandniece%(inlaw)s", 522 "seventh great %(step)sgrandniece%(inlaw)s", 523 "eighth great %(step)sgrandniece%(inlaw)s", 524 "ninth great %(step)sgrandniece%(inlaw)s", 525 "tenth great %(step)sgrandniece%(inlaw)s", 526 "eleventh great %(step)sgrandniece%(inlaw)s", 527 "twelfth great %(step)sgrandniece%(inlaw)s", 528 "thirteenth great %(step)sgrandniece%(inlaw)s", 529 "fourteenth great %(step)sgrandniece%(inlaw)s", 530 "fifteenth great %(step)sgrandniece%(inlaw)s", 531 "sixteenth great %(step)sgrandniece%(inlaw)s", 532 "seventeenth great %(step)sgrandniece%(inlaw)s", 533 "eighteenth great %(step)sgrandniece%(inlaw)s", 534 "nineteenth great %(step)sgrandniece%(inlaw)s", 535 "twentieth great %(step)sgrandniece%(inlaw)s", 536 "twenty-first great %(step)sgrandniece%(inlaw)s", 537 "twenty-second great %(step)sgrandniece%(inlaw)s", 538 "twenty-third great %(step)sgrandniece%(inlaw)s", 539 "twenty-fourth great %(step)sgrandniece%(inlaw)s", 540 "twenty-fifth great %(step)sgrandniece%(inlaw)s", 541 "twenty-sixth great %(step)sgrandniece%(inlaw)s", 542 "twenty-seventh great %(step)sgrandniece%(inlaw)s", 543 "twenty-eighth great %(step)sgrandniece%(inlaw)s", 544 "twenty-ninth great %(step)sgrandniece%(inlaw)s", 545 "thirtieth great %(step)sgrandniece%(inlaw)s", 546 "thirty-first great %(step)sgrandniece%(inlaw)s", 547 "thirty-second great %(step)sgrandniece%(inlaw)s", 548 "thirty-third great %(step)sgrandniece%(inlaw)s", 549 "thirty-fourth great %(step)sgrandniece%(inlaw)s", 550 "thirty-fifth great %(step)sgrandniece%(inlaw)s", 551 "thirty-sixth great %(step)sgrandniece%(inlaw)s", 552 "thirty-seventh great %(step)sgrandniece%(inlaw)s", 553 "thirty-eighth great %(step)sgrandniece%(inlaw)s", 554 "thirty-ninth great %(step)sgrandniece%(inlaw)s", 555 "fourtieth great %(step)sgrandniece%(inlaw)s", 556 "forty-first great %(step)sgrandniece%(inlaw)s", 557 "forty-second great %(step)sgrandniece%(inlaw)s", 558 "forty-third great %(step)sgrandniece%(inlaw)s", 559 "forty-fourth great %(step)sgrandniece%(inlaw)s", 560 "forty-fifth great %(step)sgrandniece%(inlaw)s", 561 "forty-sixth great %(step)sgrandniece%(inlaw)s", 562 "forty-seventh great %(step)sgrandniece%(inlaw)s", 563 "forty-eighth great %(step)sgrandniece%(inlaw)s", 564 "forty-ninth great %(step)sgrandniece%(inlaw)s", 565 "fiftieth great %(step)sgrandniece%(inlaw)s", ] 566 567_CHILDREN_LEVEL = ["", 568 "children", 569 "grandchildren", 570 "great grandchildren", 571 "second great grandchildren", 572 "third great grandchildren", 573 "fourth great grandchildren", 574 "fifth great grandchildren", 575 "sixth great grandchildren", 576 "seventh great grandchildren", 577 "eighth great grandchildren", 578 "ninth great grandchildren", 579 "tenth great grandchildren", 580 "eleventh great grandchildren", 581 "twelfth great grandchildren", 582 "thirteenth great grandchildren", 583 "fourteenth great grandchildren", 584 "fifteenth great grandchildren", 585 "sixteenth great grandchildren", 586 "seventeenth great grandchildren", 587 "eighteenth great grandchildren", 588 "nineteenth great grandchildren", 589 "twentieth great grandchildren", 590 "twenty-first great grandchildren", 591 "twenty-second great grandchildren", 592 "twenty-third great grandchildren", 593 "twenty-fourth great grandchildren", 594 "twenty-fifth great grandchildren", 595 "twenty-sixth great grandchildren", 596 "twenty-seventh great grandchildren", 597 "twenty-eighth great grandchildren", 598 "twenty-ninth great grandchildren", 599 "thirtieth great grandchildren", 600 "thirty-first great grandchildren", 601 "thirty-second great grandchildren", 602 "thirty-third great grandchildren", 603 "thirty-fourth great grandchildren", 604 "thirty-fifth great grandchildren", 605 "thirty-sixth great grandchildren", 606 "thirty-seventh great grandchildren", 607 "thirty-eighth great grandchildren", 608 "thirty-ninth great grandchildren", 609 "fourtieth great grandchildren", 610 "forty-first great grandchildren", 611 "forty-second great grandchildren", 612 "forty-third great grandchildren", 613 "forty-fourth great grandchildren", 614 "forty-fifth great grandchildren", 615 "forty-sixth great grandchildren", 616 "forty-seventh great grandchildren", 617 "forty-eighth great grandchildren", 618 "forty-ninth great grandchildren", 619 "fiftieth great grandchildren", ] 620 621_SIBLINGS_LEVEL = ["", 622 "siblings", 623 "uncles/aunts", 624 "granduncles/aunts", 625 "great granduncles/aunts", 626 "second great granduncles/aunts", 627 "third great granduncles/aunts", 628 "fourth great granduncles/aunts", 629 "fifth great granduncles/aunts", 630 "sixth great granduncles/aunts", 631 "seventh great granduncles/aunts", 632 "eighth great granduncles/aunts", 633 "ninth great granduncles/aunts", 634 "tenth great granduncles/aunts", 635 "eleventh great granduncles/aunts", 636 "twelfth great granduncles/aunts", 637 "thirteenth great granduncles/aunts", 638 "fourteenth great granduncles/aunts", 639 "fifteenth great granduncles/aunts", 640 "sixteenth great granduncles/aunts", 641 "seventeenth great granduncles/aunts", 642 "eighteenth great granduncles/aunts", 643 "nineteenth great granduncles/aunts", 644 "twentieth great granduncles/aunts", 645 "twenty-first great granduncles/aunts", 646 "twenty-second great granduncles/aunts", 647 "twenty-third great granduncles/aunts", 648 "twenty-fourth great granduncles/aunts", 649 "twenty-fifth great granduncles/aunts", 650 "twenty-sixth great granduncles/aunts", 651 "twenty-seventh great granduncles/aunts", 652 "twenty-eighth great granduncles/aunts", 653 "twenty-ninth great granduncles/aunts", 654 "thirtieth great granduncles/aunts", 655 "thirty-first great granduncles/aunts", 656 "thirty-second great granduncles/aunts", 657 "thirty-third great granduncles/aunts", 658 "thirty-fourth great granduncles/aunts", 659 "thirty-fifth great granduncles/aunts", 660 "thirty-sixth great granduncles/aunts", 661 "thirty-seventh great granduncles/aunts", 662 "thirty-eighth great granduncles/aunts", 663 "thirty-ninth great granduncles/aunts", 664 "fortieth great granduncles/aunts", 665 "forty-first great granduncles/aunts", 666 "forty-second great granduncles/aunts", 667 "forty-third great granduncles/aunts", 668 "forty-fourth great granduncles/aunts", 669 "forty-fifth great granduncles/aunts", 670 "forty-sixth great granduncles/aunts", 671 "forty-seventh great granduncles/aunts", 672 "forty-eighth great granduncles/aunts", 673 "forty-ninth great granduncles/aunts", 674 "fiftienth great granduncles/aunts", ] 675 676_SIBLING_LEVEL = ["", 677 "%(step)ssibling%(inlaw)s", 678 "%(step)suncle/aunt%(inlaw)s", 679 "%(step)sgranduncle/aunt%(inlaw)s", 680 "great %(step)sgranduncle/aunt%(inlaw)s", 681 "second great %(step)sgranduncle/aunt%(inlaw)s", 682 "third great %(step)sgranduncle/aunt%(inlaw)s", 683 "fourth great %(step)sgranduncle/aunt%(inlaw)s", 684 "fifth great %(step)sgranduncle/aunt%(inlaw)s", 685 "sixth great %(step)sgranduncle/aunt%(inlaw)s", 686 "seventh great %(step)sgranduncle/aunt%(inlaw)s", 687 "eighth great %(step)sgranduncle/aunt%(inlaw)s", 688 "ninth great %(step)sgranduncle/aunt%(inlaw)s", 689 "tenth great %(step)sgranduncle/aunt%(inlaw)s", 690 "eleventh great %(step)sgranduncle/aunt%(inlaw)s", 691 "twelfth great %(step)sgranduncle/aunt%(inlaw)s", 692 "thirteenth great %(step)sgranduncle/aunt%(inlaw)s", 693 "fourteenth great %(step)sgranduncle/aunt%(inlaw)s", 694 "fifteenth great %(step)sgranduncle/aunt%(inlaw)s", 695 "sixteenth great %(step)sgranduncle/aunt%(inlaw)s", 696 "seventeenth great %(step)sgranduncle/aunt%(inlaw)s", 697 "eighteenth great %(step)sgranduncle/aunt%(inlaw)s", 698 "nineteenth great %(step)sgranduncle/aunt%(inlaw)s", 699 "twentieth great %(step)sgranduncle/aunt%(inlaw)s", 700 "twenty-first great %(step)sgranduncle/aunt%(inlaw)s", 701 "twenty-second great %(step)sgranduncle/aunt%(inlaw)s", 702 "twenty-third great %(step)sgranduncle/aunt%(inlaw)s", 703 "twenty-fourth great %(step)sgranduncle/aunt%(inlaw)s", 704 "twenty-fifth great %(step)sgranduncle/aunt%(inlaw)s", 705 "twenty-sixth great %(step)sgranduncle/aunt%(inlaw)s", 706 "twenty-seventh great %(step)sgranduncle/aunt%(inlaw)s", 707 "twenty-eighth great %(step)sgranduncle/aunt%(inlaw)s", 708 "twenty-ninth great %(step)sgranduncle/aunt%(inlaw)s", 709 "thirtieth great %(step)sgranduncle/aunt%(inlaw)s", 710 "thirty-first great %(step)sgranduncle/aunt%(inlaw)s", 711 "thirty-second great %(step)sgranduncle/aunt%(inlaw)s", 712 "thirty-third great %(step)sgranduncle/aunt%(inlaw)s", 713 "thirty-fourth great %(step)sgranduncle/aunt%(inlaw)s", 714 "thirty-fifth great %(step)sgranduncle/aunt%(inlaw)s", 715 "thirty-sixth great %(step)sgranduncle/aunt%(inlaw)s", 716 "thirty-seventh great %(step)sgranduncle/aunt%(inlaw)s", 717 "thirty-eighth great %(step)sgranduncle/aunt%(inlaw)s", 718 "thirty-ninth great %(step)sgranduncle/aunt%(inlaw)s", 719 "fortieth great %(step)sgranduncle/aunt%(inlaw)s", 720 "forty-first great %(step)sgranduncle/aunt%(inlaw)s", 721 "forty-second great %(step)sgranduncle/aunt%(inlaw)s", 722 "forty-third great %(step)sgranduncle/aunt%(inlaw)s", 723 "forty-fourth great %(step)sgranduncle/aunt%(inlaw)s", 724 "forty-fifth great %(step)sgranduncle/aunt%(inlaw)s", 725 "forty-sixth great %(step)sgranduncle/aunt%(inlaw)s", 726 "forty-seventh great %(step)sgranduncle/aunt%(inlaw)s", 727 "forty-eighth great %(step)sgranduncle/aunt%(inlaw)s", 728 "forty-ninth great %(step)sgranduncle/aunt%(inlaw)s", 729 "fiftieth great %(step)sgranduncle/aunt%(inlaw)s", ] 730 731_NEPHEWS_NIECES_LEVEL = ["", 732 "siblings", 733 "nephews/nieces", 734 "grandnephews/nieces", 735 "great grandnephews/nieces", 736 "second great grandnephews/nieces", 737 "third great grandnephews/nieces", 738 "fourth great grandnephews/nieces", 739 "fifth great grandnephews/nieces", 740 "sixth great grandnephews/nieces", 741 "seventh great grandnephews/nieces", 742 "eighth great grandnephews/nieces", 743 "ninth great grandnephews/nieces", 744 "tenth great grandnephews/nieces", 745 "eleventh great grandnephews/nieces", 746 "twelfth great grandnephews/nieces", 747 "thirteenth great grandnephews/nieces", 748 "fourteenth great grandnephews/nieces", 749 "fifteenth great grandnephews/nieces", 750 "sixteenth great grandnephews/nieces", 751 "seventeenth great grandnephews/nieces", 752 "eighteenth great grandnephews/nieces", 753 "nineteenth great grandnephews/nieces", 754 "twentieth great grandnephews/nieces", 755 "twenty-first great grandnephews/nieces", 756 "twenty-second great grandnephews/nieces", 757 "twenty-third great grandnephews/nieces", 758 "twenty-fourth great grandnephews/nieces", 759 "twenty-fifth great grandnephews/nieces", 760 "twenty-sixth great grandnephews/nieces", 761 "twenty-seventh great grandnephews/nieces", 762 "twenty-eighth great grandnephews/nieces", 763 "twenty-ninth great grandnephews/nieces", 764 "thirtieth great grandnephews/nieces", 765 "thirty-first great grandnephews/nieces", 766 "thirty-second great grandnephews/nieces", 767 "thirty-third great grandnephews/nieces", 768 "thirty-fourth great grandnephews/nieces", 769 "thirty-fifth great grandnephews/nieces", 770 "thirty-sixth great grandnephews/nieces", 771 "thirty-seventh great grandnephews/nieces", 772 "thirty-eighth great grandnephews/nieces", 773 "thirty-ninth great grandnephews/nieces", 774 "fortieth great grandnephews/nieces", 775 "forty-first great grandnephews/nieces", 776 "forty-second great grandnephews/nieces", 777 "forty-third great grandnephews/nieces", 778 "forty-fourth great grandnephews/nieces", 779 "forty-fifth great grandnephews/nieces", 780 "forty-sixth great grandnephews/nieces", 781 "forty-seventh great grandnephews/nieces", 782 "forty-eighth great grandnephews/nieces", 783 "forty-ninth great grandnephews/nieces", 784 "fiftieth great grandnephews/nieces", ] 785 786 787#------------------------------------------------------------------------- 788# 789# RelationshipCalculator 790# 791#------------------------------------------------------------------------- 792class RelationshipCalculator: 793 """ 794 The relationship calculator helps to determine the relationship between 795 two people. 796 """ 797 REL_MOTHER = 'm' # going up to mother 798 REL_FATHER = 'f' # going up to father 799 REL_MOTHER_NOTBIRTH = 'M' # going up to mother, not birth relation 800 REL_FATHER_NOTBIRTH = 'F' # going up to father, not birth relation 801 REL_SIBLING = 's' # going sideways to sibling (no parents) 802 REL_FAM_BIRTH = 'a' # going up to family (mother and father) 803 REL_FAM_NONBIRTH = 'A' # going up to family, not birth relation 804 REL_FAM_BIRTH_MOTH_ONLY = 'b' # going up to fam, only birth rel to mother 805 REL_FAM_BIRTH_FATH_ONLY = 'c' # going up to fam, only birth rel to father 806 807 REL_FAM_INLAW_PREFIX = 'L' # going to the partner. 808 809 #sibling types 810 NORM_SIB = 0 # same birth parents 811 HALF_SIB_MOTHER = 1 # same mother, father known to be different 812 HALF_SIB_FATHER = 2 # same father, mother known to be different 813 STEP_SIB = 3 # birth parents known to be different 814 UNKNOWN_SIB = 4 # insufficient data to draw conclusion 815 816 #sibling strings 817 STEP = 'step' 818 HALF = 'half-' 819 820 INLAW = '-in-law' 821 822 #partner types 823 PARTNER_MARRIED = 1 824 PARTNER_UNMARRIED = 2 825 PARTNER_CIVIL_UNION = 3 826 PARTNER_UNKNOWN_REL = 4 827 PARTNER_EX_MARRIED = 5 828 PARTNER_EX_UNMARRIED = 6 829 PARTNER_EX_CIVIL_UNION = 7 830 PARTNER_EX_UNKNOWN_REL = 8 831 832 def __init__(self): 833 self.signal_keys = [] 834 self.state_signal_key = None 835 self.storemap = False 836 self.dirtymap = True 837 self.stored_map = None 838 self.map_handle = None 839 self.map_meta = None 840 self.__db_connected = False 841 self.depth = 15 842 try: 843 from .config import config 844 self.set_depth(config.get('behavior.generation-depth')) 845 except ImportError: 846 pass 847 848 #data storage to communicate with recursive functions 849 self.__max_depth_reached = False 850 self.__loop_detected = False 851 self.__max_depth = 0 852 self.__all_families = False 853 self.__all_dist = False 854 self.__only_birth = False 855 self.__crosslinks = False 856 self.__msg = [] 857 858 def set_depth(self, depth): 859 """ 860 Set how deep relationships must be searched. Input must be an 861 integer > 0 862 """ 863 if depth != self.depth: 864 self.depth = depth 865 self.dirtymap = True 866 867 def get_depth(self): 868 """ 869 Obtain depth of relationship search 870 """ 871 return self.depth 872 873 DIST_FATHER = "distant %(step)sancestor%(inlaw)s (%(level)d generations)" 874 875 def _get_father(self, level, step='', inlaw=''): 876 """ 877 Internal english method to create relation string 878 """ 879 if level > len(_FATHER_LEVEL) - 1: 880 return self.DIST_FATHER % {'step': step, 'inlaw': inlaw, 881 'level': level} 882 else: 883 return _FATHER_LEVEL[level] % {'step': step, 'inlaw': inlaw} 884 885 DIST_SON = "distant %(step)sdescendant%(inlaw)s (%(level)d generations)" 886 887 def _get_son(self, level, step='', inlaw=''): 888 """ 889 Internal english method to create relation string 890 """ 891 if level > len(_SON_LEVEL) - 1: 892 return self.DIST_SON % {'step': step, 'inlaw': inlaw, 893 'level': level} 894 else: 895 return _SON_LEVEL[level] % {'step': step, 'inlaw': inlaw} 896 897 DIST_MOTHER = "distant %(step)sancestor%(inlaw)s (%(level)d generations)" 898 899 def _get_mother(self, level, step='', inlaw=''): 900 """ 901 Internal english method to create relation string 902 """ 903 if level > len(_MOTHER_LEVEL) - 1: 904 return self.DIST_MOTHER % {'step': step, 'inlaw': inlaw, 905 'level': level} 906 else: 907 return _MOTHER_LEVEL[level] % {'step': step, 'inlaw': inlaw} 908 909 DIST_DAUGHTER = "distant %(step)sdescendant%(inlaw)s (%(level)d generations)" 910 911 def _get_daughter(self, level, step='', inlaw=''): 912 """ 913 Internal english method to create relation string 914 """ 915 if level > len(_DAUGHTER_LEVEL) - 1: 916 return self.DIST_DAUGHTER % {'step': step, 'inlaw': inlaw, 917 'level': level} 918 else: 919 return _DAUGHTER_LEVEL[level] % {'step': step, 'inlaw': inlaw} 920 921 def _get_parent_unknown(self, level, step='', inlaw=''): 922 """ 923 Internal english method to create relation string 924 """ 925 if level < len(_LEVEL_NAME): 926 return _LEVEL_NAME[level] + ' ' + '%sancestor%s' % (step, inlaw) 927 else: 928 return "distant %sancestor%s (%d generations)" % (step, inlaw, 929 level) 930 931 DIST_CHILD = "distant %(step)sdescendant (%(level)d generations)" 932 933 def _get_child_unknown(self, level, step='', inlaw=''): 934 """ 935 Internal english method to create relation string 936 """ 937 if level < len(_LEVEL_NAME): 938 return _LEVEL_NAME[level] + ' ' + '%(step)sdescendant%(inlaw)s' % { 939 'step': step, 'inlaw': inlaw} 940 else: 941 return self.DIST_CHILD % {'step': step, 'level': level} 942 943 DIST_AUNT = "distant %(step)saunt%(inlaw)s" 944 945 def _get_aunt(self, level, step='', inlaw=''): 946 """ 947 Internal english method to create relation string 948 """ 949 if level > len(_SISTER_LEVEL) - 1: 950 return self.DIST_AUNT % {'step': step, 'inlaw': inlaw} 951 else: 952 return _SISTER_LEVEL[level] % {'step': step, 'inlaw': inlaw} 953 954 DIST_UNCLE = "distant %(step)suncle%(inlaw)s" 955 956 def _get_uncle(self, level, step='', inlaw=''): 957 """ 958 Internal english method to create relation string 959 """ 960 if level > len(_BROTHER_LEVEL) - 1: 961 return self.DIST_UNCLE % {'step': step, 'inlaw': inlaw} 962 else: 963 return _BROTHER_LEVEL[level] % {'step': step, 'inlaw': inlaw} 964 965 DIST_NEPHEW = "distant %(step)snephew%(inlaw)s" 966 967 def _get_nephew(self, level, step='', inlaw=''): 968 """ 969 Internal english method to create relation string 970 """ 971 if level > len(_NEPHEW_LEVEL) - 1: 972 return self.DIST_NEPHEW % {'step': step, 'inlaw': inlaw} 973 else: 974 return _NEPHEW_LEVEL[level] % {'step': step, 'inlaw': inlaw} 975 976 DIST_NIECE = "distant %(step)sniece%(inlaw)s" 977 978 def _get_niece(self, level, step='', inlaw=''): 979 """ 980 Internal english method to create relation string 981 """ 982 if level > len(_NIECE_LEVEL) - 1: 983 return self.DIST_NIECE % {'step': step, 'inlaw': inlaw} 984 else: 985 return _NIECE_LEVEL[level] % {'step': step, 'inlaw': inlaw} 986 987 def _get_cousin(self, level, removed, dir='', step='', inlaw=''): 988 """ 989 Internal english method to create relation string 990 """ 991 if removed == 0 and level < len(_LEVEL_NAME): 992 return "%s %scousin%s" % (_LEVEL_NAME[level], step, inlaw) 993 elif removed > len(_REMOVED_LEVEL)-1 or level > len(_LEVEL_NAME)-1: 994 return "distant %srelative%s" % (step, inlaw) 995 else: 996 return "%s %scousin%s%s%s" % (_LEVEL_NAME[level], 997 step, inlaw, 998 _REMOVED_LEVEL[removed], dir) 999 1000 DIST_SIB = "distant %(step)suncle/aunt%(inlaw)s" 1001 1002 def _get_sibling(self, level, step='', inlaw=''): 1003 """ 1004 Internal english method to create relation string 1005 """ 1006 if level < len(_SIBLING_LEVEL): 1007 return _SIBLING_LEVEL[level] % {'step': step, 'inlaw': inlaw} 1008 else: 1009 return self.DIST_SIB % {'step': step, 'inlaw': inlaw} 1010 1011 def get_sibling_type(self, db, orig, other): 1012 """ 1013 Translation free determination of type of orig and other as siblings 1014 The procedure returns sibling types, these can be passed to 1015 get_sibling_relationship_string. 1016 Only call this method if known that orig and other are siblings 1017 """ 1018 fatherorig, motherorig = self.get_birth_parents(db, orig) 1019 fatherother, motherother = self.get_birth_parents(db, other) 1020 if fatherorig and motherorig and fatherother and motherother: 1021 if fatherother == fatherorig and motherother == motherorig: 1022 return self.NORM_SIB 1023 elif fatherother == fatherorig: 1024 #all birth parents are known, one 1025 return self.HALF_SIB_FATHER 1026 elif motherother == motherorig: 1027 return self.HALF_SIB_MOTHER 1028 else: 1029 return self.STEP_SIB 1030 else: 1031 # some birth parents are not known, hence we or cannot know if 1032 # half siblings. step siblings might be possible, otherwise give up 1033 orig_nb_par = self._get_nonbirth_parent_list(db, orig) 1034 if fatherother and fatherother in orig_nb_par: 1035 #the birth parent of other is non-birth of orig 1036 if motherother and motherother == motherorig: 1037 return self.HALF_SIB_MOTHER 1038 else: 1039 return self.STEP_SIB 1040 if motherother and motherother in orig_nb_par: 1041 #the birth parent of other is non-birth of orig 1042 if fatherother and fatherother == fatherorig: 1043 return self.HALF_SIB_FATHER 1044 else: 1045 return self.STEP_SIB 1046 other_nb_par = self._get_nonbirth_parent_list(db, other) 1047 if fatherorig and fatherorig in other_nb_par: 1048 #the one birth parent of other is non-birth of orig 1049 if motherorig and motherother == motherorig: 1050 return self.HALF_SIB_MOTHER 1051 else: 1052 return self.STEP_SIB 1053 if motherorig and motherorig in other_nb_par: 1054 #the one birth parent of other is non-birth of orig 1055 if fatherother and fatherother == fatherorig: 1056 return self.HALF_SIB_FATHER 1057 else: 1058 return self.STEP_SIB 1059 #there is an unknown birth parent, it could be that this is the 1060 # birth parent of the other person 1061 return self.UNKNOWN_SIB 1062 1063 def get_birth_parents(self, db, person): 1064 """ 1065 Method that returns the birthparents of a person as tuple 1066 (mother handle, father handle), if no known birthparent, the 1067 handle is replaced by None 1068 """ 1069 birthfather = None 1070 birthmother = None 1071 for fam in person.get_parent_family_handle_list(): 1072 family = db.get_family_from_handle(fam) 1073 if not family: 1074 continue 1075 childrel = [(ref.get_mother_relation(), ref.get_father_relation()) 1076 for ref in family.get_child_ref_list() 1077 if ref.ref == person.handle] 1078 if not birthmother and childrel[0][0] == ChildRefType.BIRTH: 1079 birthmother = family.get_mother_handle() 1080 if not birthfather and childrel[0][1] == ChildRefType.BIRTH: 1081 birthfather = family.get_father_handle() 1082 if birthmother and birthfather: 1083 break 1084 return (birthmother, birthfather) 1085 1086 def _get_nonbirth_parent_list(self, db, person): 1087 """ 1088 Returns a list of handles of parents of which it is known 1089 they are not birth parents. 1090 So all parents which do not have relation BIRTH or UNKNOWN 1091 are returned. 1092 """ 1093 nb_parents = [] 1094 for fam in person.get_parent_family_handle_list(): 1095 family = db.get_family_from_handle(fam) 1096 if not family: 1097 continue 1098 childrel = [(ref.get_mother_relation(), ref.get_father_relation()) 1099 for ref in family.get_child_ref_list() 1100 if ref.ref == person.handle] 1101 if childrel[0][0] != ChildRefType.BIRTH \ 1102 and childrel[0][0] != ChildRefType.UNKNOWN: 1103 nb_parents.append(family.get_mother_handle()) 1104 if childrel[0][1] != ChildRefType.BIRTH \ 1105 and childrel[0][1] != ChildRefType.UNKNOWN: 1106 nb_parents.append(family.get_father_handle()) 1107 #make every person appear only once: 1108 return list(set(nb_parents)) 1109 1110 def _get_spouse_type(self, db, orig, other, all_rel=False): 1111 """ 1112 Translation free determination if orig and other are partners. 1113 The procedure returns partner types, these can be passed to 1114 get_partner_relationship_string. 1115 If all_rel=False, returns None or a partner type. 1116 If all_rel=True, returns a list, empty if no partner 1117 """ 1118 val = [] 1119 for family_handle in orig.get_family_handle_list(): 1120 family = db.get_family_from_handle(family_handle) 1121 # return first found spouse type 1122 if family and other.get_handle() in [family.get_father_handle(), 1123 family.get_mother_handle()]: 1124 family_rel = family.get_relationship() 1125 #check for divorce event: 1126 ex = False 1127 for eventref in family.get_event_ref_list(): 1128 event = db.get_event_from_handle(eventref.ref) 1129 if event and (event.get_type() == EventType.DIVORCE 1130 or event.get_type() == EventType.ANNULMENT): 1131 ex = True 1132 break 1133 if family_rel == FamilyRelType.MARRIED: 1134 if ex: 1135 val.append(self.PARTNER_EX_MARRIED) 1136 else: 1137 val.append(self.PARTNER_MARRIED) 1138 elif family_rel == FamilyRelType.UNMARRIED: 1139 if ex: 1140 val.append(self.PARTNER_EX_UNMARRIED) 1141 else: 1142 val.append(self.PARTNER_UNMARRIED) 1143 elif family_rel == FamilyRelType.CIVIL_UNION: 1144 if ex: 1145 val.append(self.PARTNER_EX_CIVIL_UNION) 1146 else: 1147 val.append(self.PARTNER_CIVIL_UNION) 1148 else: 1149 if ex: 1150 val.append(self.PARTNER_EX_UNKNOWN_REL) 1151 else: 1152 val.append(self.PARTNER_UNKNOWN_REL) 1153 1154 if all_rel: 1155 return val 1156 else: 1157 #last relation is normally the defenitive relation 1158 if val: 1159 return val[-1] 1160 else: 1161 return None 1162 1163 def is_spouse(self, db, orig, other, all_rel=False): 1164 """ 1165 Determine the spouse relation 1166 """ 1167 spouse_type = self._get_spouse_type(db, orig, other, all_rel) 1168 if spouse_type: 1169 return self.get_partner_relationship_string(spouse_type, 1170 orig.get_gender(), 1171 other.get_gender()) 1172 else: 1173 return None 1174 1175 def get_relationship_distance_new(self, db, orig_person, 1176 other_person, 1177 all_families=False, 1178 all_dist=False, 1179 only_birth=True): 1180 """ 1181 Return if all_dist == True a 'tuple, string': 1182 (rank, person handle, firstRel_str, firstRel_fam, 1183 secondRel_str, secondRel_fam), msg 1184 or if all_dist == True a 'list of tuple, string': 1185 [.....], msg: 1186 1187 .. note:: _new can be removed once all rel_xx modules no longer 1188 overwrite get_relationship_distance 1189 1190 The tuple or list of tuples consists of: 1191 1192 ============== ===================================================== 1193 Element Description 1194 ============== ===================================================== 1195 rank Total number of generations from common ancestor to 1196 the two persons, rank is -1 if no relations found 1197 person_handle The Common ancestor 1198 firstRel_str String with the path to the common ancestor 1199 from orig Person 1200 firstRel_fam Family numbers along the path as a list, eg [0,0,1]. 1201 For parent in multiple families, eg [0. [0, 2], 1] 1202 secondRel_str String with the path to the common ancestor 1203 from otherPerson 1204 secondRel_fam Family numbers along the path, eg [0,0,1]. 1205 For parent in multiple families, eg [0. [0, 2], 1] 1206 msg List of messages indicating errors. Empyt list if no 1207 errors. 1208 ============== ===================================================== 1209 1210 Example: firstRel_str = 'ffm' and firstRel_fam = [2,0,1] means 1211 common ancestor is mother of the second family of the father of the 1212 first family of the father of the third family. 1213 1214 Note that the same person might be present twice if the person is 1215 reached via a different branch too. Path (firstRel_str and 1216 secondRel_str) will of course be different. 1217 1218 :param db: database to work on 1219 :param orig_person: first person 1220 :type orig_person: Person Obj 1221 :param other_person: second person, relation is sought between 1222 first and second person 1223 :type other_person: Person Obj 1224 :param all_families: if False only Main family is searched, otherwise 1225 all families are used 1226 :type all_families: bool 1227 :param all_dist: if False only the shortest distance is returned, 1228 otherwise all relationships 1229 :type all_dist: bool 1230 :param only_birth: if True only parents with birth relation are 1231 considered 1232 :type only_birth: bool 1233 """ 1234 #data storage to communicate with recursive functions 1235 self.__max_depth_reached = False 1236 self.__loop_detected = False 1237 self.__max_depth = self.get_depth() 1238 self.__all_families = all_families 1239 self.__all_dist = all_dist 1240 self.__only_birth = only_birth 1241 self.__crosslinks = False # no crosslinks 1242 1243 first_rel = -1 1244 second_rel = -1 1245 self.__msg = [] 1246 1247 common = [] 1248 first_map = {} 1249 second_map = {} 1250 rank = 9999999 1251 1252 try: 1253 if (self.storemap and self.stored_map is not None 1254 and self.map_handle == orig_person.handle 1255 and not self.dirtymap): 1256 first_map = self.stored_map 1257 self.__max_depth_reached, self.__loop_detected, \ 1258 self.__all_families,\ 1259 self.__all_dist, self.__only_birth,\ 1260 self.__crosslinks, self.__msg = self.map_meta 1261 self.__msg = list(self.__msg) 1262 else: 1263 self.__apply_filter(db, orig_person, '', [], first_map) 1264 self.map_meta = (self.__max_depth_reached, 1265 self.__loop_detected, 1266 self.__all_families, 1267 self.__all_dist, self.__only_birth, 1268 self.__crosslinks, list(self.__msg)) 1269 self.__apply_filter(db, other_person, '', [], second_map, 1270 stoprecursemap=first_map) 1271 except RuntimeError: 1272 return (-1, None, -1, [], -1, []), \ 1273 [_("Relationship loop detected")] + self.__msg 1274 1275 if self.storemap: 1276 self.stored_map = first_map 1277 self.dirtymap = False 1278 self.map_handle = orig_person.handle 1279 1280 for person_handle in second_map: 1281 if person_handle in first_map: 1282 com = [] 1283 #a common ancestor 1284 for rel1, fam1 in zip(first_map[person_handle][0], 1285 first_map[person_handle][1]): 1286 len1 = len(rel1) 1287 for rel2, fam2 in zip(second_map[person_handle][0], 1288 second_map[person_handle][1]): 1289 len2 = len(rel2) 1290 #collect paths to arrive at common ancestor 1291 com.append((len1+len2, person_handle, rel1, fam1, 1292 rel2, fam2)) 1293 #insert common ancestor in correct position, 1294 # if shorter links, check if not subset 1295 # if longer links, check if not superset 1296 pos = 0 1297 for (ranknew, handlenew, rel1new, fam1new, rel2new, 1298 fam2new) in com: 1299 insert = True 1300 for rank, handle, rel1, fam1, rel2, fam2 in common: 1301 if ranknew < rank: 1302 break 1303 elif ranknew >= rank: 1304 #check subset 1305 if rel1 == rel1new[:len(rel1)] and \ 1306 rel2 == rel2new[:len(rel2)]: 1307 #subset relation exists already 1308 insert = False 1309 break 1310 pos += 1 1311 if insert: 1312 if common: 1313 common.insert(pos, (ranknew, handlenew, rel1new, 1314 fam1new, rel2new, fam2new)) 1315 else: 1316 common = [(ranknew, handlenew, rel1new, fam1new, 1317 rel2new, fam2new)] 1318 #now check if superset must be deleted from common 1319 deletelist = [] 1320 index = pos+1 1321 for (rank, handle, rel1, fam1, rel2, 1322 fam2) in common[pos+1:]: 1323 if rel1new == rel1[:len(rel1new)] and \ 1324 rel2new == rel2[:len(rel2new)]: 1325 deletelist.append(index) 1326 index += 1 1327 deletelist.reverse() 1328 for index in deletelist: 1329 del common[index] 1330 #check for extra messages 1331 if self.__max_depth_reached: 1332 self.__msg += [_('Family Tree reaches back more than the maximum ' 1333 '%d generations searched.\nIt is possible that ' 1334 'relationships have been missed') % 1335 (self.__max_depth)] 1336 1337 if common and not self.__all_dist: 1338 rank = common[0][0] 1339 person_handle = common[0][1] 1340 first_rel = common[0][2] 1341 first_fam = common[0][3] 1342 second_rel = common[0][4] 1343 second_fam = common[0][5] 1344 return (rank, person_handle, first_rel, first_fam, second_rel, 1345 second_fam), self.__msg 1346 if common: 1347 #list with tuples (rank, handle person,rel_str_orig,rel_fam_orig, 1348 # rel_str_other,rel_fam_str) and messages 1349 return common, self.__msg 1350 if not self.__all_dist: 1351 return (-1, None, '', [], '', []), self.__msg 1352 else: 1353 return [(-1, None, '', [], '', [])], self.__msg 1354 1355 def __apply_filter(self, db, person, rel_str, rel_fam, pmap, 1356 depth=1, stoprecursemap=None): 1357 """ 1358 Typically this method is called recursively in two ways: 1359 First method is stoprecursemap= None 1360 In this case a recursemap is builded by storing all data. 1361 1362 Second method is with a stoprecursemap given 1363 In this case parents are recursively looked up. If present in 1364 stoprecursemap, a common ancestor is found, and the method can 1365 stop looking further. If however self.__crosslinks == True, the data 1366 of first contains loops, and parents 1367 will be looked up anyway an stored if common. At end the doubles 1368 are filtered out 1369 """ 1370 if person is None or not person.handle: 1371 return 1372 1373 if depth > self.__max_depth: 1374 self.__max_depth_reached = True 1375 #print('Maximum ancestor generations ('+str(depth)+') reached', \ 1376 # '(' + rel_str + ').',\ 1377 # 'Stopping relation algorithm.') 1378 return 1379 depth += 1 1380 1381 commonancestor = False 1382 store = True #normally we store all parents 1383 if stoprecursemap: 1384 store = False #but not if a stop map given 1385 if person.handle in stoprecursemap: 1386 commonancestor = True 1387 store = True 1388 1389 #add person to the map, take into account that person can be obtained 1390 #from different sides 1391 if person.handle in pmap: 1392 #person is already a grandparent in another branch, we already have 1393 # had lookup of all parents, we call that a crosslink 1394 if not stoprecursemap: 1395 self.__crosslinks = True 1396 pmap[person.handle][0] += [rel_str] 1397 pmap[person.handle][1] += [rel_fam] 1398 #check if there is no loop father son of his son, ... 1399 # loop means person is twice reached, same rel_str in begin 1400 for rel1 in pmap[person.handle][0]: 1401 for rel2 in pmap[person.handle][0]: 1402 if len(rel1) < len(rel2) and \ 1403 rel1 == rel2[:len(rel1)]: 1404 #loop, keep one message in storage! 1405 self.__loop_detected = True 1406 self.__msg += [_("Relationship loop detected:") + " " + 1407 _("Person %(person)s connects to himself via %(relation)s") % 1408 {'person' : person.get_primary_name().get_name(), 1409 'relation' : rel2[len(rel1):]}] 1410 return 1411 elif store: 1412 pmap[person.handle] = [[rel_str], [rel_fam]] 1413 1414 #having added person to the pmap, we only look up recursively to 1415 # parents if this person is not common relative 1416 # if however the first map has crosslinks, we need to continue reduced 1417 if commonancestor and not self.__crosslinks: 1418 #don't continue search, great speedup! 1419 return 1420 1421 family_handles = [] 1422 main = person.get_main_parents_family_handle() 1423 if main: 1424 family_handles = [main] 1425 if self.__all_families: 1426 family_handles = person.get_parent_family_handle_list() 1427 1428 try: 1429 parentstodo = {} 1430 fam = 0 1431 for family_handle in family_handles: 1432 rel_fam_new = rel_fam + [fam] 1433 family = db.get_family_from_handle(family_handle) 1434 if not family: 1435 continue 1436 #obtain childref for this person 1437 childrel = [(ref.get_mother_relation(), 1438 ref.get_father_relation()) 1439 for ref in family.get_child_ref_list() 1440 if ref.ref == person.handle] 1441 fhandle = family.father_handle 1442 mhandle = family.mother_handle 1443 for data in [(fhandle, self.REL_FATHER, 1444 self.REL_FATHER_NOTBIRTH, childrel[0][1]), 1445 (mhandle, self.REL_MOTHER, 1446 self.REL_MOTHER_NOTBIRTH, childrel[0][0])]: 1447 if data[0] and data[0] not in parentstodo: 1448 persontodo = db.get_person_from_handle(data[0]) 1449 if data[3] == ChildRefType.BIRTH: 1450 addstr = data[1] 1451 elif not self.__only_birth: 1452 addstr = data[2] 1453 else: 1454 addstr = '' 1455 if addstr: 1456 parentstodo[data[0]] = (persontodo, 1457 rel_str + addstr, 1458 rel_fam_new) 1459 elif data[0] and data[0] in parentstodo: 1460 #this person is already scheduled to research 1461 #update family list 1462 famlist = parentstodo[data[0]][2] 1463 if not isinstance(famlist[-1], list) and \ 1464 fam != famlist[-1]: 1465 famlist = famlist[:-1] + [[famlist[-1]]] 1466 if isinstance(famlist[-1], list) and \ 1467 fam not in famlist[-1]: 1468 famlist = famlist[:-1] + [famlist[-1] + [fam]] 1469 parentstodo[data[0]] = (parentstodo[data[0]][0], 1470 parentstodo[data[0]][1], 1471 famlist) 1472 if not fhandle and not mhandle and stoprecursemap is None: 1473 #family without parents, add brothers for orig person 1474 #other person has recusemap, and will stop when seeing 1475 #the brother. 1476 child_list = [ref.ref for ref in family.get_child_ref_list() 1477 if ref.ref != person.handle] 1478 addstr = self.REL_SIBLING 1479 for chandle in child_list: 1480 if chandle in pmap: 1481 pmap[chandle][0] += [rel_str + addstr] 1482 pmap[chandle][1] += [rel_fam_new] 1483 #person is already a grandparent in another branch 1484 else: 1485 pmap[chandle] = [[rel_str+addstr], [rel_fam_new]] 1486 fam += 1 1487 1488 for handle, data in parentstodo.items(): 1489 self.__apply_filter(db, data[0], 1490 data[1], data[2], 1491 pmap, depth, stoprecursemap) 1492 except: 1493 import traceback 1494 traceback.print_exc() 1495 return 1496 1497 def collapse_relations(self, relations): 1498 """ 1499 Internal method to condense the relationships as returned by 1500 get_relationship_distance_new. 1501 Common ancestors in the same family are collapsed to one entry, 1502 changing the person paths to family paths, eg 'mf' and 'mm' become 'ma' 1503 1504 relations : list of relations as returned by 1505 get_relationship_distance_new with all_dist = True 1506 1507 returns : the same data as relations, but collapsed, hence the 1508 handle entry is now a list of handles, and the 1509 path to common ancestors can now contain family 1510 identifiers (eg 'a', ...) 1511 In the case of sibling, this is replaced by family 1512 with common ancestor handles empty list []! 1513 """ 1514 if relations[0][0] == -1: 1515 return relations 1516 commonnew = [] 1517 existing_path = [] 1518 for relation in relations: 1519 relstrfirst = None 1520 commonhandle = [relation[1]] 1521 if relation[2]: 1522 relstrfirst = relation[2][:-1] 1523 relstrsec = None 1524 if relation[4]: 1525 relstrsec = relation[4][:-1] 1526 relfamfirst = relation[3][:] 1527 relfamsec = relation[5][:] 1528 #handle pure sibling: 1529 rela2 = relation[2] 1530 rela4 = relation[4] 1531 if relation[2] and relation[2][-1] == self.REL_SIBLING: 1532 #sibling will be the unique common ancestor, 1533 #change to a family with unknown handle for common ancestor 1534 rela2 = relation[2][:-1] + self.REL_FAM_BIRTH 1535 rela4 = relation[4] + self.REL_FAM_BIRTH 1536 relfamsec = relfamsec + [relfamfirst[-1]] 1537 relstrsec = relation[4][:-1] 1538 commonhandle = [] 1539 1540 # a unique path to family of common person: 1541 familypaths = [] 1542 if relfamfirst and isinstance(relfamfirst[-1], list): 1543 if relfamsec and isinstance(relfamsec[-1], list): 1544 for val1 in relfamfirst[-1]: 1545 for val2 in relfamsec[-1]: 1546 familypaths.append((relstrfirst, relstrsec, 1547 relfamfirst[:-1] + [val1], 1548 relfamsec[:-1] + [val2])) 1549 else: 1550 for val1 in relfamfirst[-1]: 1551 familypaths.append((relstrfirst, relstrsec, 1552 relfamfirst[:-1] + [val1], 1553 relfamsec)) 1554 elif relfamsec and isinstance(relfamsec[-1], list): 1555 for val2 in relfamsec[-1]: 1556 familypaths.append((relstrfirst, relstrsec, 1557 relfamfirst, 1558 relfamsec[:-1] + [val2])) 1559 else: 1560 familypaths.append((relstrfirst, relstrsec, 1561 relfamfirst, relfamsec)) 1562 for familypath in familypaths: 1563 #familypath = (relstrfirst, relstrsec, relfamfirst, relfamsec) 1564 try: 1565 posfam = existing_path.index(familypath) 1566 except ValueError: 1567 posfam = None 1568 #if relstr is '', the ancestor is unique, if posfam None, 1569 # first time we see this family path 1570 if (posfam is not None and relstrfirst is not None and 1571 relstrsec is not None): 1572 # We already have a common ancestor of this family, just 1573 # add the other, setting correct family relation. 1574 tmp = commonnew[posfam] 1575 frstcomstr = rela2[-1] 1576 scndcomstr = tmp[2][-1] 1577 newcomstra = self._famrel_from_persrel(frstcomstr, 1578 scndcomstr) 1579 frstcomstr = rela4[-1] 1580 scndcomstr = tmp[4][-1] 1581 newcomstrb = self._famrel_from_persrel(frstcomstr, 1582 scndcomstr) 1583 1584 commonnew[posfam] = (tmp[0], tmp[1]+commonhandle, 1585 rela2[:-1]+newcomstra, 1586 tmp[3], rela4[:-1]+newcomstrb, 1587 tmp[5]) 1588 else: 1589 existing_path.append(familypath) 1590 commonnew.append((relation[0], commonhandle, rela2, 1591 familypath[2], rela4, familypath[3])) 1592 #we now have multiple person handles, single families, now collapse 1593 # families again if all else equal 1594 collapsed = commonnew[:1] 1595 for rel in commonnew[1:]: 1596 found = False 1597 for newrel in collapsed: 1598 if newrel[0:3] == rel[0:3] and newrel[4] == rel[4]: 1599 #another familypath to arrive at same result, merge 1600 path1 = [] 1601 path2 = [] 1602 for a, b in zip(newrel[3], rel[3]): 1603 if a == b: 1604 path1.append(a) 1605 elif isinstance(a, list): 1606 path1.append(a.append(b)) 1607 else: 1608 path1.append([a, b]) 1609 for a, b in zip(newrel[5], rel[5]): 1610 if a == b: 1611 path2.append(a) 1612 elif isinstance(a, list): 1613 path2.append(a.append(b)) 1614 else: 1615 path2.append([a, b]) 1616 newrel[3][:] = path1[:] 1617 newrel[5][:] = path2[:] 1618 found = True 1619 break 1620 if not found: 1621 collapsed.append(rel) 1622 1623 return collapsed 1624 1625 def _famrel_from_persrel(self, persrela, persrelb): 1626 """ 1627 Conversion from eg 'f' and 'm' to 'a', so relation to the two 1628 persons of a common family is converted to a family relation 1629 """ 1630 if persrela == persrelb: 1631 #should not happen, procedure called in error, just return value 1632 return persrela 1633 if ((persrela == self.REL_MOTHER and persrelb == self.REL_FATHER) or 1634 (persrelb == self.REL_MOTHER and persrela == self.REL_FATHER)): 1635 return self.REL_FAM_BIRTH 1636 if ((persrela == self.REL_MOTHER and 1637 persrelb == self.REL_FATHER_NOTBIRTH) or 1638 (persrelb == self.REL_MOTHER and 1639 persrela == self.REL_FATHER_NOTBIRTH)): 1640 return self.REL_FAM_BIRTH_MOTH_ONLY 1641 if ((persrela == self.REL_FATHER and 1642 persrelb == self.REL_MOTHER_NOTBIRTH) or 1643 (persrelb == self.REL_FATHER and 1644 persrela == self.REL_MOTHER_NOTBIRTH)): 1645 return self.REL_FAM_BIRTH_FATH_ONLY 1646 #catch calling with family relations already, return val 1647 if (persrela == self.REL_FAM_BIRTH or 1648 persrela == self.REL_FAM_BIRTH_FATH_ONLY or 1649 persrela == self.REL_FAM_BIRTH_MOTH_ONLY or 1650 persrela == self.REL_FAM_NONBIRTH): 1651 return persrela 1652 if (persrelb == self.REL_FAM_BIRTH or 1653 persrelb == self.REL_FAM_BIRTH_FATH_ONLY or 1654 persrelb == self.REL_FAM_BIRTH_MOTH_ONLY or 1655 persrelb == self.REL_FAM_NONBIRTH): 1656 return persrelb 1657 return self.REL_FAM_NONBIRTH 1658 1659 def only_birth(self, path): 1660 """ 1661 Given a path to common ancestor. Return True if only birth 1662 relations, False otherwise 1663 """ 1664 for value in path: 1665 if value in [self.REL_FAM_NONBIRTH, self.REL_FATHER_NOTBIRTH, 1666 self.REL_MOTHER_NOTBIRTH]: 1667 return False 1668 return True 1669 1670 def get_one_relationship(self, db, orig_person, other_person, 1671 extra_info=False, olocale=glocale): 1672 """ 1673 Returns a string representing the most relevant relationship between 1674 the two people. If extra_info = True, extra information is returned: 1675 (relation_string, distance_common_orig, distance_common_other) 1676 1677 If olocale is passed in (a GrampsLocale) that language will be used. 1678 1679 :param olocale: allow selection of the relationship language 1680 :type olocale: a GrampsLocale instance 1681 """ 1682 self._locale = olocale 1683 stop = False 1684 if orig_person is None: 1685 rel_str = _("undefined") 1686 stop = True 1687 1688 if not stop and orig_person.get_handle() == other_person.get_handle(): 1689 rel_str = '' 1690 stop = True 1691 1692 if not stop: 1693 is_spouse = self.is_spouse(db, orig_person, other_person) 1694 if is_spouse: 1695 rel_str = is_spouse 1696 stop = True 1697 1698 if stop: 1699 if extra_info: 1700 return (rel_str, -1, -1) 1701 else: 1702 return rel_str 1703 1704 data, msg = self.get_relationship_distance_new( 1705 db, orig_person, other_person, all_dist=True, all_families=True, 1706 only_birth=False) 1707 if data[0][0] == -1: 1708 if extra_info: 1709 return ('', -1, -1) 1710 else: 1711 return '' 1712 1713 data = self.collapse_relations(data) 1714 1715 #most relevant relationship is a birth family relation of lowest rank 1716 databest = [data[0]] 1717 rankbest = data[0][0] 1718 for rel in data: 1719 #data is sorted on rank 1720 if rel[0] == rankbest: 1721 databest.append(rel) 1722 rel = databest[0] 1723 dist_orig = len(rel[2]) 1724 dist_other = len(rel[4]) 1725 if len(databest) == 1: 1726 birth = self.only_birth(rel[2]) and self.only_birth(rel[4]) 1727 if dist_orig == dist_other == 1: 1728 rel_str = self.get_sibling_relationship_string( 1729 self.get_sibling_type(db, orig_person, other_person), 1730 orig_person.get_gender(), 1731 other_person.get_gender()) 1732 else: 1733 rel_str = self.get_single_relationship_string( 1734 dist_orig, dist_other, 1735 orig_person.get_gender(), other_person.get_gender(), 1736 rel[2], rel[4], only_birth=birth, 1737 in_law_a=False, in_law_b=False) 1738 else: 1739 order = [self.REL_FAM_BIRTH, self.REL_FAM_BIRTH_MOTH_ONLY, 1740 self.REL_FAM_BIRTH_FATH_ONLY, self.REL_MOTHER, 1741 self.REL_FATHER, self.REL_SIBLING, self.REL_FAM_NONBIRTH, 1742 self.REL_MOTHER_NOTBIRTH, self.REL_FATHER_NOTBIRTH] 1743 orderbest = order.index(self.REL_MOTHER) 1744 for relother in databest: 1745 relbirth = self.only_birth(rel[2]) and self.only_birth(rel[4]) 1746 if relother[2] == '' or relother[4] == '': 1747 #direct relation, take that 1748 rel = relother 1749 break 1750 if not relbirth and self.only_birth(relother[2]) \ 1751 and self.only_birth(relother[4]): 1752 #birth takes precedence 1753 rel = relother 1754 continue 1755 if order.index(relother[2][-1]) < order.index(rel[2][-1]) and\ 1756 order.index(relother[2][-1]) < orderbest: 1757 rel = relother 1758 continue 1759 if order.index(relother[4][-1]) < order.index(rel[4][-1]) and\ 1760 order.index(relother[4][-1]) < orderbest: 1761 rel = relother 1762 continue 1763 if order.index(rel[2][-1]) < orderbest or \ 1764 order.index(rel[4][-1]) < orderbest: 1765 #keep the good one 1766 continue 1767 if order.index(relother[2][-1]) < order.index(rel[2][-1]): 1768 rel = relother 1769 continue 1770 if order.index(relother[2][-1]) == order.index(rel[2][-1]) and\ 1771 order.index(relother[4][-1]) < order.index(rel[4][-1]): 1772 rel = relother 1773 continue 1774 dist_orig = len(rel[2]) 1775 dist_other = len(rel[4]) 1776 birth = self.only_birth(rel[2]) and self.only_birth(rel[4]) 1777 if dist_orig == dist_other == 1: 1778 rel_str = self.get_sibling_relationship_string( 1779 self.get_sibling_type(db, orig_person, other_person), 1780 orig_person.get_gender(), 1781 other_person.get_gender()) 1782 else: 1783 rel_str = self.get_single_relationship_string( 1784 dist_orig, dist_other, 1785 orig_person.get_gender(), other_person.get_gender(), 1786 rel[2], rel[4], only_birth=birth, 1787 in_law_a=False, in_law_b=False) 1788 if extra_info: 1789 return (rel_str, dist_orig, dist_other) 1790 else: 1791 return rel_str 1792 1793 def get_all_relationships(self, db, orig_person, other_person): 1794 """ 1795 Return a tuple, of which the first entry is a list with all 1796 relationships in text, and the second a list of lists of all common 1797 ancestors that have that text as relationship 1798 """ 1799 relstrings = [] 1800 commons = {} 1801 if orig_person is None: 1802 return ([], []) 1803 1804 if orig_person.get_handle() == other_person.get_handle(): 1805 return ([], []) 1806 1807 is_spouse = self.is_spouse(db, orig_person, other_person) 1808 if is_spouse: 1809 relstrings.append(is_spouse) 1810 commons[is_spouse] = [] 1811 1812 data, msg = self.get_relationship_distance_new( 1813 db, orig_person, other_person, all_dist=True, all_families=True, 1814 only_birth=False) 1815 if data[0][0] != -1: 1816 data = self.collapse_relations(data) 1817 for rel in data: 1818 rel2 = rel[2] 1819 rel4 = rel[4] 1820 rel1 = rel[1] 1821 dist_orig = len(rel[2]) 1822 dist_other = len(rel[4]) 1823 if rel[2] and rel[2][-1] == self.REL_SIBLING: 1824 rel2 = rel2[:-1] + self.REL_FAM_BIRTH 1825 dist_other += 1 1826 rel4 = rel4 + self.REL_FAM_BIRTH 1827 rel1 = None 1828 birth = self.only_birth(rel2) and self.only_birth(rel4) 1829 if dist_orig == dist_other == 1: 1830 rel_str = self.get_sibling_relationship_string( 1831 self.get_sibling_type(db, orig_person, other_person), 1832 orig_person.get_gender(), other_person.get_gender()) 1833 else: 1834 rel_str = self.get_single_relationship_string( 1835 dist_orig, dist_other, 1836 orig_person.get_gender(), other_person.get_gender(), 1837 rel2, rel4, only_birth=birth, 1838 in_law_a=False, in_law_b=False) 1839 if rel_str not in relstrings: 1840 relstrings.append(rel_str) 1841 if rel1: 1842 commons[rel_str] = rel1 1843 else: 1844 #unknown parent eg 1845 commons[rel_str] = [] 1846 else: 1847 if rel1: 1848 commons[rel_str].extend(rel1) 1849 #construct the return tupply, relstrings is ordered on rank automatic 1850 common_list = [] 1851 for rel_str in relstrings: 1852 common_list.append(commons[rel_str]) 1853 return (relstrings, common_list) 1854 1855 def get_plural_relationship_string(self, Ga, Gb, 1856 reltocommon_a='', reltocommon_b='', 1857 only_birth=True, 1858 in_law_a=False, in_law_b=False): 1859 """ 1860 Provide a string that describes the relationsip between a person, and 1861 a group of people with the same relationship. E.g. "grandparents" or 1862 "children". 1863 1864 Ga and Gb can be used to mathematically calculate the relationship. 1865 1866 .. seealso:: 1867 http://en.wikipedia.org/wiki/Cousin#Mathematical_definitions 1868 1869 :param Ga: The number of generations between the main person and the 1870 common ancestor. 1871 :type Ga: int 1872 :param Gb: The number of generations between the group of people and the 1873 common ancestor 1874 :type Gb: int 1875 :param reltocommon_a: relation path to common ancestor or common 1876 Family for person a. 1877 Note that length = Ga 1878 :type reltocommon_a: str 1879 :param reltocommon_b: relation path to common ancestor or common 1880 Family for person b. 1881 Note that length = Gb 1882 :type reltocommon_b: str 1883 :param only_birth: True if relation between a and b is by birth only 1884 False otherwise 1885 :type only_birth: bool 1886 :param in_law_a: True if path to common ancestors is via the partner 1887 of person a 1888 :type in_law_a: bool 1889 :param in_law_b: True if path to common ancestors is via the partner 1890 of person b 1891 :type in_law_b: bool 1892 :returns: A string describing the relationship between the person and 1893 the group. 1894 :rtype: str 1895 """ 1896 rel_str = "distant relatives" 1897 if Ga == 0: 1898 # These are descendants 1899 if Gb < len(_CHILDREN_LEVEL): 1900 rel_str = _CHILDREN_LEVEL[Gb] 1901 else: 1902 rel_str = "distant descendants" 1903 elif Gb == 0: 1904 # These are parents/grand parents 1905 if Ga < len(_PARENTS_LEVEL): 1906 rel_str = _PARENTS_LEVEL[Ga] 1907 else: 1908 rel_str = "distant ancestors" 1909 elif Gb == 1: 1910 # These are siblings/aunts/uncles 1911 if Ga < len(_SIBLINGS_LEVEL): 1912 rel_str = _SIBLINGS_LEVEL[Ga] 1913 else: 1914 rel_str = "distant uncles/aunts" 1915 elif Ga == 1: 1916 # These are nieces/nephews 1917 if Gb < len(_NEPHEWS_NIECES_LEVEL): 1918 rel_str = _NEPHEWS_NIECES_LEVEL[Gb] 1919 else: 1920 rel_str = "distant nephews/nieces" 1921 elif Ga > 1 and Ga == Gb: 1922 # These are cousins in the same generation 1923 if Ga <= len(_LEVEL_NAME): 1924 rel_str = "%s cousins" % _LEVEL_NAME[Ga-1] 1925 else: 1926 rel_str = "distant cousins" 1927 elif Ga > 1 and Ga > Gb: 1928 # These are cousins in different generations with the second person 1929 # being in a higher generation from the common ancestor than the 1930 # first person. 1931 if Gb <= len(_LEVEL_NAME) and (Ga-Gb) < len(_REMOVED_LEVEL): 1932 rel_str = "%s cousins%s (up)" % (_LEVEL_NAME[Gb-1], 1933 _REMOVED_LEVEL[Ga-Gb]) 1934 else: 1935 rel_str = "distant cousins" 1936 elif Gb > 1 and Gb > Ga: 1937 # These are cousins in different generations with the second person 1938 # being in a lower generation from the common ancestor than the 1939 # first person. 1940 if Ga <= len(_LEVEL_NAME) and (Gb-Ga) < len(_REMOVED_LEVEL): 1941 rel_str = "%s cousins%s (down)" % (_LEVEL_NAME[Ga-1], 1942 _REMOVED_LEVEL[Gb-Ga]) 1943 else: 1944 rel_str = "distant cousins" 1945 1946 if in_law_b is True: 1947 rel_str = "spouses of %s" % rel_str 1948 1949 return rel_str 1950 1951 def get_single_relationship_string(self, Ga, Gb, gender_a, gender_b, 1952 reltocommon_a, reltocommon_b, 1953 only_birth=True, 1954 in_law_a=False, in_law_b=False): 1955 """ 1956 Provide a string that describes the relationsip between a person, and 1957 another person. E.g. "grandparent" or "child". 1958 1959 To be used as: 'person b is the grandparent of a', this will be in 1960 translation string: 'person b is the %(relation)s of a' 1961 1962 Note that languages with gender should add 'the' inside the 1963 translation, so eg in french: 'person b est %(relation)s de a' 1964 where relation will be here: le grandparent 1965 1966 Ga and Gb can be used to mathematically calculate the relationship. 1967 1968 .. seealso:: 1969 http://en.wikipedia.org/wiki/Cousin#Mathematical_definitions 1970 1971 Some languages need to know the specific path to the common ancestor. 1972 Those languages should use reltocommon_a and reltocommon_b which is 1973 a string like 'mfmf'. 1974 1975 The possible string codes are: 1976 1977 ======================= =========================================== 1978 Code Description 1979 ======================= =========================================== 1980 REL_MOTHER # going up to mother 1981 REL_FATHER # going up to father 1982 REL_MOTHER_NOTBIRTH # going up to mother, not birth relation 1983 REL_FATHER_NOTBIRTH # going up to father, not birth relation 1984 REL_FAM_BIRTH # going up to family (mother and father) 1985 REL_FAM_NONBIRTH # going up to family, not birth relation 1986 REL_FAM_BIRTH_MOTH_ONLY # going up to fam, only birth rel to mother 1987 REL_FAM_BIRTH_FATH_ONLY # going up to fam, only birth rel to father 1988 ======================= =========================================== 1989 1990 Prefix codes are stripped, so REL_FAM_INLAW_PREFIX is not present. 1991 If the relation starts with the inlaw of the person a, then 'in_law_a' 1992 is True, if it starts with the inlaw of person b, then 'in_law_b' is 1993 True. 1994 1995 Also REL_SIBLING (# going sideways to sibling (no parents)) is not 1996 passed to this routine. The collapse_relations changes this to a 1997 family relation. 1998 1999 Hence, calling routines should always strip REL_SIBLING and 2000 REL_FAM_INLAW_PREFIX before calling get_single_relationship_string() 2001 Note that only_birth=False, means that in the reltocommon one of the 2002 NOTBIRTH specifiers is present. 2003 2004 The REL_FAM identifiers mean that the relation is not via a common 2005 ancestor, but via a common family (note that that is not possible for 2006 direct descendants or direct ancestors!). If the relation to one of the 2007 parents in that common family is by birth, then 'only_birth' is not 2008 set to False. The only_birth() method is normally used for this. 2009 2010 :param Ga: The number of generations between the main person and the 2011 common ancestor. 2012 :type Ga: int 2013 :param Gb: The number of generations between the other person and the 2014 common ancestor. 2015 :type Gb: int 2016 :param gender_a: gender of person a 2017 :type gender_a: int gender 2018 :param gender_b: gender of person b 2019 :type gender_b: int gender 2020 :param reltocommon_a: relation path to common ancestor or common 2021 Family for person a. 2022 Note that length = Ga 2023 :type reltocommon_a: str 2024 :param reltocommon_b: relation path to common ancestor or common 2025 Family for person b. 2026 Note that length = Gb 2027 :type reltocommon_b: str 2028 :param in_law_a: True if path to common ancestors is via the partner 2029 of person a 2030 :type in_law_a: bool 2031 :param in_law_b: True if path to common ancestors is via the partner 2032 of person b 2033 :type in_law_b: bool 2034 :param only_birth: True if relation between a and b is by birth only 2035 False otherwise 2036 :type only_birth: bool 2037 :returns: A string describing the relationship between the two people 2038 :rtype: str 2039 2040 .. note:: 1. the self.REL_SIBLING should not be passed to this routine, 2041 so we should not check on it. All other self. 2042 2. for better determination of siblings, use if Ga=1=Gb 2043 get_sibling_relationship_string 2044 """ 2045 if only_birth: 2046 step = '' 2047 else: 2048 step = self.STEP 2049 2050 if in_law_a or in_law_b: 2051 inlaw = self.INLAW 2052 else: 2053 inlaw = '' 2054 2055 rel_str = "distant %srelative%s" % (step, inlaw) 2056 2057 if Ga == 0: 2058 # b is descendant of a 2059 if Gb == 0: 2060 rel_str = 'same person' 2061 elif gender_b == MALE: 2062 rel_str = self._get_son(Gb, step, inlaw) 2063 elif gender_b == FEMALE: 2064 rel_str = self._get_daughter(Gb, step, inlaw) 2065 else: 2066 rel_str = self._get_child_unknown(Gb, step, inlaw) 2067 elif Gb == 0: 2068 # b is parents/grand parent of a 2069 if gender_b == MALE: 2070 rel_str = self._get_father(Ga, step, inlaw) 2071 elif gender_b == FEMALE: 2072 rel_str = self._get_mother(Ga, step, inlaw) 2073 else: 2074 rel_str = self._get_parent_unknown(Ga, step, inlaw) 2075 elif Gb == 1: 2076 # b is sibling/aunt/uncle of a 2077 if gender_b == MALE: 2078 rel_str = self._get_uncle(Ga, step, inlaw) 2079 elif gender_b == FEMALE: 2080 rel_str = self._get_aunt(Ga, step, inlaw) 2081 else: 2082 rel_str = self._get_sibling(Ga, step, inlaw) 2083 elif Ga == 1: 2084 # b is niece/nephew of a 2085 if gender_b == MALE: 2086 rel_str = self._get_nephew(Gb-1, step, inlaw) 2087 elif gender_b == FEMALE: 2088 rel_str = self._get_niece(Gb-1, step, inlaw) 2089 elif Gb < len(_NIECE_LEVEL) and Gb < len(_NEPHEW_LEVEL): 2090 rel_str = "%s or %s" % (self._get_nephew(Gb-1, step, inlaw), 2091 self._get_niece(Gb-1, step, inlaw)) 2092 else: 2093 rel_str = "distant %snephews/nieces%s" % (step, inlaw) 2094 elif Ga == Gb: 2095 # a and b cousins in the same generation 2096 rel_str = self._get_cousin(Ga-1, 0, dir='', step=step, inlaw=inlaw) 2097 elif Ga > Gb: 2098 # These are cousins in different generations with the second person 2099 # being in a higher generation from the common ancestor than the 2100 # first person. 2101 rel_str = self._get_cousin(Gb-1, Ga-Gb, dir=' (up)', 2102 step=step, inlaw=inlaw) 2103 elif Gb > Ga: 2104 # These are cousins in different generations with the second person 2105 # being in a lower generation from the common ancestor than the 2106 # first person. 2107 rel_str = self._get_cousin(Ga-1, Gb-Ga, dir=' (down)', 2108 step=step, inlaw=inlaw) 2109 return rel_str 2110 2111 def get_sibling_relationship_string(self, sib_type, gender_a, gender_b, 2112 in_law_a=False, in_law_b=False): 2113 """ 2114 Determine the string giving the relation between two siblings of 2115 type sib_type. 2116 Eg: b is the brother of a 2117 Here 'brother' is the string we need to determine 2118 This method gives more details about siblings than 2119 get_single_relationship_string can do. 2120 2121 .. warning:: DON'T TRANSLATE THIS PROCEDURE IF LOGIC IS EQUAL IN YOUR 2122 LANGUAGE, AND SAME METHODS EXIST (get_uncle, get_aunt, 2123 get_sibling) 2124 """ 2125 if sib_type == self.NORM_SIB or sib_type == self.UNKNOWN_SIB: 2126 typestr = '' 2127 elif sib_type == self.HALF_SIB_MOTHER \ 2128 or sib_type == self.HALF_SIB_FATHER: 2129 typestr = self.HALF 2130 elif sib_type == self.STEP_SIB: 2131 typestr = self.STEP 2132 2133 if in_law_a or in_law_b: 2134 inlaw = self.INLAW 2135 else: 2136 inlaw = '' 2137 2138 if gender_b == MALE: 2139 rel_str = self._get_uncle(1, typestr, inlaw) 2140 elif gender_b == FEMALE: 2141 rel_str = self._get_aunt(1, typestr, inlaw) 2142 else: 2143 rel_str = self._get_sibling(1, typestr, inlaw) 2144 return rel_str 2145 2146 def get_partner_relationship_string(self, spouse_type, gender_a, gender_b): 2147 """ 2148 Determine the string giving the relation between two partners of 2149 type spouse_type. 2150 Eg: b is the spouse of a 2151 Here 'spouse' is the string we need to determine 2152 2153 .. warning:: DON'T TRANSLATE THIS PROCEDURE IF LOGIC IS EQUAL IN YOUR 2154 LANGUAGE, AS GETTEXT IS ALREADY USED ! 2155 """ 2156 #english only needs gender of b, we don't guess if unknown like in old 2157 # procedure as that is stupid in present day cases! 2158 gender = gender_b 2159 2160 if not spouse_type: 2161 return '' 2162 2163 trans_text = _ 2164 # trans_text is a defined keyword (see po/update_po.py, po/genpot.sh) 2165 if hasattr(self, '_locale') and self._locale != glocale: 2166 trans_text = self._locale.translation.sgettext 2167 2168 if spouse_type == self.PARTNER_MARRIED: 2169 if gender == MALE: 2170 return trans_text("husband") 2171 elif gender == FEMALE: 2172 return trans_text("wife") 2173 else: 2174 return trans_text("gender unknown|spouse") 2175 elif spouse_type == self.PARTNER_EX_MARRIED: 2176 if gender == MALE: 2177 return trans_text("ex-husband") 2178 elif gender == FEMALE: 2179 return trans_text("ex-wife") 2180 else: 2181 return trans_text("gender unknown|ex-spouse") 2182 elif spouse_type == self.PARTNER_UNMARRIED: 2183 if gender == MALE: 2184 return trans_text("male,unmarried|partner") 2185 elif gender == FEMALE: 2186 return trans_text("female,unmarried|partner") 2187 else: 2188 return trans_text("gender unknown,unmarried|partner") 2189 elif spouse_type == self.PARTNER_EX_UNMARRIED: 2190 if gender == MALE: 2191 return trans_text("male,unmarried|ex-partner") 2192 elif gender == FEMALE: 2193 return trans_text("female,unmarried|ex-partner") 2194 else: 2195 return trans_text("gender unknown,unmarried|ex-partner") 2196 elif spouse_type == self.PARTNER_CIVIL_UNION: 2197 if gender == MALE: 2198 return trans_text("male,civil union|partner") 2199 elif gender == FEMALE: 2200 return trans_text("female,civil union|partner") 2201 else: 2202 return trans_text("gender unknown,civil union|partner") 2203 elif spouse_type == self.PARTNER_EX_CIVIL_UNION: 2204 if gender == MALE: 2205 return trans_text("male,civil union|former partner") 2206 elif gender == FEMALE: 2207 return trans_text("female,civil union|former partner") 2208 else: 2209 return trans_text("gender unknown,civil union|former partner") 2210 elif spouse_type == self.PARTNER_UNKNOWN_REL: 2211 if gender == MALE: 2212 return trans_text("male,unknown relation|partner") 2213 elif gender == FEMALE: 2214 return trans_text("female,unknown relation|partner") 2215 else: 2216 return trans_text("gender unknown,unknown relation|partner") 2217 else: 2218 # here we have spouse_type == self.PARTNER_EX_UNKNOWN_REL 2219 # or other not catched types 2220 if gender == MALE: 2221 return trans_text("male,unknown relation|former partner") 2222 elif gender == FEMALE: 2223 return trans_text("female,unknown relation|former partner") 2224 else: 2225 return trans_text("gender unknown,unknown relation|former partner") 2226 2227 def connect_db_signals(self, dbstate): 2228 """ 2229 We can save work by storing a map, however, if database changes 2230 this map must be regenerated. 2231 Before close, the calling app must call disconnect_db_signals 2232 """ 2233 if self.__db_connected: 2234 return 2235 assert len(self.signal_keys) == 0 2236 self.state_signal_key = dbstate.connect('database-changed', 2237 self._dbchange_callback) 2238 self.__connect_db_signals(dbstate.db) 2239 2240 def __connect_db_signals(self, db): 2241 signals = ['person-add', 'person-update', 'person-delete', 2242 'person-rebuild', 'family-add', 'family-update', 2243 'family-delete', 'family-rebuild', 'database-changed'] 2244 for name in signals: 2245 self.signal_keys.append(db.connect(name, self._datachange_callback)) 2246 self.storemap = True 2247 self.__db_connected = True 2248 2249 def disconnect_db_signals(self, dbstate): 2250 """ 2251 Method to disconnect to all signals the relationship calculator is 2252 subscribed 2253 """ 2254 dbstate.disconnect(self.state_signal_key) 2255 list(map(dbstate.db.disconnect, self.signal_keys)) 2256 self.storemap = False 2257 self.stored_map = None 2258 2259 def _dbchange_callback(self, db): 2260 """ 2261 When database changes, the map can no longer be used. 2262 Connects must be remade 2263 """ 2264 self.dirtymap = True 2265 #signals are disconnected on close of old database, connect to new 2266 self.__connect_db_signals(db) 2267 2268 def _datachange_callback(self, handle_list=None): 2269 """ 2270 When data in database changes, the map can no longer be used. 2271 As the map might be in use or might be generated at the moment, 2272 this method sets a dirty flag. Before reusing the map, this flag 2273 will be checked 2274 """ 2275 self.dirtymap = True 2276 2277#------------------------------------------------------------------------- 2278# 2279# define the default relationshipcalculator 2280# 2281#------------------------------------------------------------------------- 2282 2283__RELCALC_CLASS = None 2284 2285def get_relationship_calculator(reinit=False, clocale=glocale): 2286 """ 2287 Return the relationship calculator for the current language. 2288 2289 If clocale is passed in (a GrampsLocale) then that language will be used. 2290 2291 :param clocale: allow selection of the relationship language 2292 :type clocale: a GrampsLocale instance 2293 2294 """ 2295 global __RELCALC_CLASS 2296 2297 if __RELCALC_CLASS is None or reinit: 2298 lang = clocale.language[0] 2299 __RELCALC_CLASS = RelationshipCalculator 2300 # If lang not set default to English relationship calulator 2301 # See if lang begins with en_, English_ or english_ 2302 # If so return standard relationship calculator. 2303 if lang.startswith("en") or lang == "C": 2304 return __RELCALC_CLASS() 2305 # set correct non English relationship calculator based on lang 2306 relation_translation_found = False 2307 for plugin in PluginRegister.get_instance().relcalc_plugins(): 2308 if lang in plugin.lang_list: 2309 pmgr = BasePluginManager.get_instance() 2310 # the loaded module is put in variable mod 2311 mod = pmgr.load_plugin(plugin) 2312 if mod: 2313 __RELCALC_CLASS = getattr(mod, plugin.relcalcclass) 2314 relation_translation_found = True 2315 break 2316 if not relation_translation_found and \ 2317 len(PluginRegister.get_instance().relcalc_plugins()): 2318 LOG.warning(_("Family relationship translator not available for " 2319 "language '%s'. Using 'english' instead."), lang) 2320 return __RELCALC_CLASS() 2321 2322#------------------------------------------------------------------------- 2323# 2324# Tests 2325# 2326#------------------------------------------------------------------------- 2327MAX = 30 2328FMT = '%+50s' 2329 2330def _test(rcalc, onlybirth, inlawa, inlawb, printrelstr, test_num=None): 2331 """ 2332 This is a generic test suite for the singular relationship 2333 TRANSLATORS: do NOT translate, use __main__ ! 2334 """ 2335 import sys 2336 import random 2337 random.seed() 2338 def _rand_f_m(): 2339 if random.randint(0, 1) == 0: 2340 return 'f' 2341 else: 2342 return 'm' 2343 2344 def _rand_relstr(length, endstr): 2345 if length == 0: 2346 return '' 2347 else: 2348 relstr = '' 2349 for i in range(length-1): 2350 relstr += _rand_f_m() 2351 return relstr + endstr 2352 2353 if test_num is None: 2354 print(""" 2355Select a test: 2356 0 - all tests 2357 1 - testing sons 2358 2 - testing daughters 2359 3 - testing unknown children 2360 4 - testing grandfathers 2361 5 - testing grandmothers 2362 6 - testing unknown parents 2363 7 - testing nieces 2364 8 - testing nephews 2365 9 - testing unknown nephews/nieces 2366 10 - testing uncles 2367 11 - testing aunts 2368 12 - testing unknown uncles/aunts 2369 13 - testing male cousins same generation 2370 14 - testing female cousins same generation 2371 15 - testing unknown cousins same generation 2372 16 - testing some cousins up 2373 17 - testing some cousins down 2374 2375Please enter a test number and press Enter for continue: 2376 """) 2377 test_num = sys.stdin.readline().strip() 2378 test_num = int(test_num) 2379 2380 if test_num == 0 or test_num == 1: 2381 print('\ntesting sons') 2382 #sys.stdin.readline() 2383 for i in range(MAX): 2384 relstr = _rand_relstr(i, 'f') 2385 rel = FMT % rcalc.get_single_relationship_string( 2386 0, i, MALE, MALE, '', relstr, only_birth=onlybirth, 2387 in_law_a=inlawa, in_law_b=inlawb) 2388 if printrelstr: 2389 print(rel + ' |info:', relstr) 2390 else: 2391 print(rel) 2392 if test_num == 0 or test_num == 2: 2393 print('\ntesting daughters\n') 2394 #sys.stdin.readline() 2395 for i in range(MAX): 2396 relstr = _rand_relstr(i, 'm') 2397 rel = FMT % rcalc.get_single_relationship_string( 2398 0, i, MALE, FEMALE, '', relstr, only_birth=onlybirth, 2399 in_law_a=inlawa, in_law_b=inlawb) 2400 if printrelstr: 2401 print(rel + ' |info:', relstr) 2402 else: 2403 print(rel) 2404 if test_num == 0 or test_num == 3: 2405 print('\ntesting unknown children\n') 2406 #sys.stdin.readline() 2407 for i in range(MAX): 2408 relstr = _rand_relstr(i, 'f') 2409 rel = FMT % rcalc.get_single_relationship_string( 2410 0, i, MALE, UNKNOWN, '', relstr, only_birth=onlybirth, 2411 in_law_a=inlawa, in_law_b=inlawb) 2412 if printrelstr: 2413 print(rel + ' |info:', relstr) 2414 else: 2415 print(rel) 2416 if test_num == 0 or test_num == 4: 2417 print('\ntesting grandfathers\n') 2418 #sys.stdin.readline() 2419 for i in range(MAX): 2420 relstr = _rand_relstr(i, 'f') 2421 rel = FMT % rcalc.get_single_relationship_string( 2422 i, 0, FEMALE, MALE, relstr, '', only_birth=onlybirth, 2423 in_law_a=inlawa, in_law_b=inlawb) 2424 if printrelstr: 2425 print(rel + ' |info:', relstr) 2426 else: 2427 print(rel) 2428 if test_num == 0 or test_num == 5: 2429 print('\ntesting grandmothers\n') 2430 #sys.stdin.readline() 2431 for i in range(MAX): 2432 relstr = _rand_relstr(i, 'm') 2433 rel = FMT % rcalc.get_single_relationship_string( 2434 i, 0, FEMALE, FEMALE, relstr, '', only_birth=onlybirth, 2435 in_law_a=inlawa, in_law_b=inlawb) 2436 if printrelstr: 2437 print(rel + ' |info:', relstr) 2438 else: 2439 print(rel) 2440 if test_num == 0 or test_num == 6: 2441 print('\ntesting unknown parents\n') 2442 #sys.stdin.readline() 2443 for i in range(MAX): 2444 relstr = _rand_relstr(i, 'f') 2445 rel = FMT % rcalc.get_single_relationship_string( 2446 i, 0, FEMALE, UNKNOWN, relstr, '', only_birth=onlybirth, 2447 in_law_a=inlawa, in_law_b=inlawb) 2448 if printrelstr: 2449 print(rel + ' |info:', relstr) 2450 else: 2451 print(rel) 2452 if test_num == 0 or test_num == 7: 2453 print('\ntesting nieces\n') 2454 #sys.stdin.readline() 2455 for i in range(1, MAX): 2456 relstr = _rand_relstr(i, 'm') 2457 rel = FMT % rcalc.get_single_relationship_string( 2458 1, i, FEMALE, FEMALE, 'm', relstr, only_birth=onlybirth, 2459 in_law_a=inlawa, in_law_b=inlawb) 2460 if printrelstr: 2461 print(rel + ' |info:', relstr) 2462 else: 2463 print(rel) 2464 if test_num == 0 or test_num == 8: 2465 print('\ntesting nephews\n') 2466 #sys.stdin.readline() 2467 for i in range(1, MAX): 2468 relstr = _rand_relstr(i, 'f') 2469 rel = FMT % rcalc.get_single_relationship_string( 2470 1, i, FEMALE, MALE, 'f', relstr, only_birth=onlybirth, 2471 in_law_a=inlawa, in_law_b=inlawb) 2472 if printrelstr: 2473 print(rel + ' |info:', relstr) 2474 else: 2475 print(rel) 2476 if test_num == 0 or test_num == 9: 2477 print('\ntesting unknown nephews/nieces\n') 2478 #sys.stdin.readline() 2479 for i in range(1, MAX): 2480 relstr = _rand_relstr(i, 'f') 2481 rel = FMT % rcalc.get_single_relationship_string( 2482 1, i, FEMALE, UNKNOWN, 'f', relstr, only_birth=onlybirth, 2483 in_law_a=inlawa, in_law_b=inlawb) 2484 if printrelstr: 2485 print(rel + ' |info:', relstr) 2486 else: 2487 print(rel) 2488 if test_num == 0 or test_num == 10: 2489 print('\ntesting uncles\n') 2490 #sys.stdin.readline() 2491 for i in range(1, MAX): 2492 relstr = _rand_relstr(i, 'f') 2493 rel = FMT % rcalc.get_single_relationship_string( 2494 i, 1, FEMALE, MALE, relstr, 'f', only_birth=onlybirth, 2495 in_law_a=inlawa, in_law_b=inlawb) 2496 if printrelstr: 2497 print(rel + ' |info:', relstr) 2498 else: 2499 print(rel) 2500 if test_num == 0 or test_num == 11: 2501 print('\ntesting aunts\n') 2502 #sys.stdin.readline() 2503 for i in range(1, MAX): 2504 relstr = _rand_relstr(i, 'f') 2505 rel = FMT % rcalc.get_single_relationship_string( 2506 i, 1, MALE, FEMALE, relstr, 'f', only_birth=onlybirth, 2507 in_law_a=inlawa, in_law_b=inlawb) 2508 if printrelstr: 2509 print(rel + ' |info:', relstr) 2510 else: 2511 print(rel) 2512 if test_num == 0 or test_num == 12: 2513 print('\ntesting unknown uncles/aunts\n') 2514 #sys.stdin.readline() 2515 for i in range(1, MAX): 2516 relstr = _rand_relstr(i, 'm') 2517 rel = FMT % rcalc.get_single_relationship_string( 2518 i, 1, MALE, UNKNOWN, relstr, 'm', only_birth=onlybirth, 2519 in_law_a=inlawa, in_law_b=inlawb) 2520 if printrelstr: 2521 print(rel + ' |info:', relstr) 2522 else: 2523 print(rel) 2524 if test_num == 0 or test_num == 13: 2525 print('\ntesting male cousins same generation\n') 2526 #sys.stdin.readline() 2527 for i in range(1, MAX): 2528 relstra = _rand_relstr(i, 'f') 2529 relstrb = _rand_relstr(i, 'f') 2530 rel = FMT % rcalc.get_single_relationship_string( 2531 i, i, MALE, MALE, relstra, relstrb, only_birth=onlybirth, 2532 in_law_a=inlawa, in_law_b=inlawb) 2533 if printrelstr: 2534 print(rel + ' |info:', relstra, relstrb) 2535 else: 2536 print(rel) 2537 if test_num == 0 or test_num == 14: 2538 print('\ntesting female cousins same generation\n') 2539 #sys.stdin.readline() 2540 for i in range(1, MAX): 2541 relstra = _rand_relstr(i, 'm') 2542 relstrb = _rand_relstr(i, 'm') 2543 rel = FMT % rcalc.get_single_relationship_string( 2544 i, i, MALE, FEMALE, relstra, relstrb, only_birth=onlybirth, 2545 in_law_a=inlawa, in_law_b=inlawb) 2546 if printrelstr: 2547 print(rel + ' |info:', relstra, relstrb) 2548 else: 2549 print(rel) 2550 if test_num == 0 or test_num == 15: 2551 print('\ntesting unknown cousins same generation\n') 2552 #sys.stdin.readline() 2553 for i in range(1, MAX): 2554 relstra = _rand_relstr(i, 'm') 2555 relstrb = _rand_relstr(i, 'm') 2556 rel = FMT % rcalc.get_single_relationship_string( 2557 i, i, MALE, UNKNOWN, relstra, relstrb, only_birth=onlybirth, 2558 in_law_a=inlawa, in_law_b=inlawb) 2559 if printrelstr: 2560 print(rel + ' |info:', relstra, relstrb) 2561 else: 2562 print(rel) 2563 if test_num == 0 or test_num == 16: 2564 print('\ntesting some cousins up\n') 2565 #sys.stdin.readline() 2566 random.seed() 2567 for i in range(1, MAX): 2568 for j in range(i, MAX): 2569 rnd = random.randint(0, 100) 2570 if rnd < 10: 2571 relstra = _rand_relstr(j, 'f') 2572 relstrb = _rand_relstr(i, 'f') 2573 if rnd < 5: 2574 rel = (FMT + ' |info: female, Ga=%2d, Gb=%2d') % ( 2575 rcalc.get_single_relationship_string( 2576 j, i, MALE, FEMALE, relstra, relstrb, 2577 only_birth=onlybirth, 2578 in_law_a=inlawa, in_law_b=inlawb), j, i) 2579 if printrelstr: 2580 print(rel + ' |info:', relstra, relstrb) 2581 else: 2582 print(rel) 2583 else: 2584 rel = (FMT + ' |info: male, Ga=%2d, Gb=%2d') % ( 2585 rcalc.get_single_relationship_string( 2586 j, i, MALE, MALE, relstra, relstrb, 2587 only_birth=onlybirth, 2588 in_law_a=inlawa, in_law_b=inlawb), j, i) 2589 if printrelstr: 2590 print(rel + ' |info:', relstra, relstrb) 2591 else: 2592 print(rel) 2593 if test_num == 0 or test_num == 17: 2594 print('\ntesting some cousins down\n') 2595 #sys.stdin.readline() 2596 for i in range(1, MAX): 2597 for j in range(i, MAX): 2598 rnd = random.randint(0, 100) 2599 if rnd < 10: 2600 relstra = _rand_relstr(i, 'f') 2601 relstrb = _rand_relstr(j, 'f') 2602 if rnd < 5: 2603 rel = (FMT + ' |info: female, Ga=%2d, Gb=%2d') % ( 2604 rcalc.get_single_relationship_string( 2605 i, j, MALE, FEMALE, relstra, relstrb, 2606 only_birth=onlybirth, 2607 in_law_a=inlawa, in_law_b=inlawb), i, j) 2608 if printrelstr: 2609 print(rel + ' |info:', relstra, relstrb) 2610 else: 2611 print(rel) 2612 else: 2613 rel = (FMT + ' |info: male, Ga=%2d, Gb=%2d') % ( 2614 rcalc.get_single_relationship_string( 2615 i, j, MALE, MALE, relstra, relstrb, 2616 only_birth=onlybirth, 2617 in_law_a=inlawa, in_law_b=inlawb), i, j) 2618 if printrelstr: 2619 print(rel + ' |info:', relstra, relstrb) 2620 else: 2621 print(rel) 2622 2623def _testsibling(rcalc): 2624 vals = [(rcalc.NORM_SIB, 'sibling'), 2625 (rcalc.HALF_SIB_MOTHER, 'half sib mother side'), 2626 (rcalc.HALF_SIB_FATHER, 'half sib father side'), 2627 (rcalc.STEP_SIB, 'step sib'), 2628 (rcalc.UNKNOWN_SIB, 'undetermined sib')] 2629 for gendr, strgen in [(MALE, 'male'), 2630 (FEMALE, 'female'), 2631 (UNKNOWN, 'unknown')]: 2632 for inlaw in [False, True]: 2633 for sibt, text in vals: 2634 print(FMT % rcalc.get_sibling_relationship_string( 2635 sibt, MALE, gendr, in_law_a=inlaw) + 2636 ' |info:', text, strgen) 2637 2638def _test_spouse(rcalc): 2639 vals = [(rcalc.PARTNER_MARRIED, 'married'), 2640 (rcalc.PARTNER_UNMARRIED, 'unmarried'), 2641 (rcalc.PARTNER_CIVIL_UNION, 'civil union'), 2642 (rcalc.PARTNER_UNKNOWN_REL, 'unknown rel'), 2643 (rcalc.PARTNER_EX_MARRIED, 'ex-married'), 2644 (rcalc.PARTNER_EX_UNMARRIED, 'ex-unmarried'), 2645 (rcalc.PARTNER_EX_CIVIL_UNION, 'ex civil union'), 2646 (rcalc.PARTNER_EX_UNKNOWN_REL, 'ex unknown rel')] 2647 2648 for gender, strgen in [(MALE, 'male'), 2649 (FEMALE, 'female'), 2650 (UNKNOWN, 'unknown')]: 2651 for spouse_type, text in vals: 2652 print(FMT % rcalc.get_partner_relationship_string( 2653 spouse_type, MALE, gender) + 2654 ' |info: gender='+strgen+', rel='+text) 2655 2656def test(rcalc, printrelstr): 2657 """ 2658 This is a generic test suite for the singular relationship 2659 TRANSLATORS: do NOT translate, call this from 2660 __main__ in the rel_xx.py module. 2661 """ 2662 import sys 2663 import argparse 2664 2665 parser = argparse.ArgumentParser(description='Test the Relationship Calculator') 2666 parser.add_argument('-r', type=int, help='type of the relations test') 2667 parser.add_argument('-s', type=int, help='type of the singular relationship test') 2668 2669 args = parser.parse_args() 2670 test_num = args.r 2671 2672 if test_num is None: 2673 print(""" 2674Select a test: 2675 0 - all tests 2676 1 - Test normal relations 2677 2 - Test step relations 2678 3 - Test in-law relations (first pers) 2679 4 - Test step and in-law relations 2680 5 - Test sibling types 2681 6 - Test partner types 2682 2683Letter 'f' means Father, 'm' means Mother 2684 2685Please enter a test number and press Enter for continue: 2686 """) 2687 test_num = sys.stdin.readline().strip() 2688 test_num = int(test_num) 2689 2690 if test_num == 0 or test_num == 1: 2691 print('\n\n=== Test normal relations ===') 2692 _test(rcalc, True, False, False, printrelstr, args.s) 2693 2694 if test_num == 0 or test_num == 2: 2695 print('\n\n=== Test step relations ===') 2696 _test(rcalc, False, False, False, printrelstr, args.s) 2697 2698 if test_num == 0 or test_num == 3: 2699 print('\n\n=== Test in-law relations (first pers) ===') 2700 _test(rcalc, True, True, False, printrelstr, args.s) 2701 2702 if test_num == 0 or test_num == 4: 2703 print('\n\n=== Test step and in-law relations ===') 2704 _test(rcalc, False, True, False, printrelstr, args.s) 2705 2706 if test_num == 0 or test_num == 5: 2707 print('\n\n=== Test sibling types ===') 2708 _testsibling(rcalc) 2709 2710 if test_num == 0 or test_num == 6: 2711 print('\n\n=== Test partner types ===') 2712 _test_spouse(rcalc) 2713 2714if __name__ == "__main__": 2715 """ 2716 TRANSLATORS, copy this if statement at the bottom of your 2717 rel_xx.py module, after adding: 'from Relationship import test' 2718 and test your work with: 2719 export PYTHONPATH=/path/to/gramps/src 2720 python src/plugins/rel_xx.py 2721 2722 See eg rel_fr.py at the bottom 2723 """ 2724 REL_CALC = RelationshipCalculator() 2725 test(REL_CALC, True) 2726