653 lines
24 KiB
OCaml
653 lines
24 KiB
OCaml
(* Copyright (c) 2001 Ludovic LEDIEU *)
|
|
|
|
open Def
|
|
open Gwdb
|
|
|
|
(*= TODO =====================================================================
|
|
- Improve the way not to check several time the same persons.
|
|
=========================================================================== *)
|
|
|
|
let in_file1 = ref ""
|
|
let in_file2 = ref ""
|
|
let html = ref false
|
|
let root = ref ""
|
|
let cr = ref ""
|
|
|
|
(** Messages are printed when there is a difference between a person
|
|
present in the two bases explored. *)
|
|
type messages =
|
|
| MsgBadChild of iper
|
|
| MsgBirthDate
|
|
| MsgBirthPlace
|
|
| MsgChildMissing of iper
|
|
| MsgChildren of iper
|
|
| MsgDeathDate
|
|
| MsgDeathPlace
|
|
| MsgDivorce
|
|
| MsgFirstName
|
|
| MsgOccupation
|
|
| MsgParentsMissing
|
|
| MsgMarriageDate
|
|
| MsgMarriagePlace
|
|
| MsgSex
|
|
| MsgSpouseMissing of iper
|
|
| MsgSpouses of iper
|
|
| MsgSurname
|
|
|
|
(** [person_string base iper]
|
|
Returns the string associated to the person with id `iper` in the base
|
|
`base`. *)
|
|
let person_string base iper =
|
|
let p = poi base iper in
|
|
let fn = sou base (get_first_name p) in
|
|
let sn = sou base (get_surname p) in
|
|
if sn = "?" || fn = "?" then fn ^ " " ^ sn ^ " (#" ^ string_of_iper iper ^ ")"
|
|
else fn ^ "." ^ string_of_int (get_occ p) ^ " " ^ sn
|
|
|
|
(** Returns the string associated to a person in HTML if the html option is set,
|
|
otherwise it has the same effect tja, `person_string`. *)
|
|
let person_link bname base iper target =
|
|
if !html then
|
|
Printf.sprintf "<A HREF=\"%s%s_w?i=%s\" TARGET=\"%s\">%s</A>" !root bname
|
|
(string_of_iper iper) target (person_string base iper)
|
|
else person_string base iper
|
|
|
|
(** Prints a message *)
|
|
let print_message base1 msg =
|
|
Printf.printf " ";
|
|
(match msg with
|
|
| MsgBadChild iper1 ->
|
|
Printf.printf "can not isolate one child match: %s"
|
|
(person_link !in_file1 base1 iper1 "base1")
|
|
| MsgBirthDate -> Printf.printf "birth date"
|
|
| MsgBirthPlace -> Printf.printf "birth place"
|
|
| MsgChildMissing iper1 ->
|
|
Printf.printf "child missing: %s"
|
|
(person_link !in_file1 base1 iper1 "base1")
|
|
| MsgChildren iper1 ->
|
|
Printf.printf "more than one child match: %s"
|
|
(person_link !in_file1 base1 iper1 "base1")
|
|
| MsgDeathDate -> Printf.printf "death (status or date)"
|
|
| MsgDeathPlace -> Printf.printf "death place"
|
|
| MsgDivorce -> Printf.printf "divorce"
|
|
| MsgFirstName -> Printf.printf "first name"
|
|
| MsgOccupation -> Printf.printf "occupation"
|
|
| MsgParentsMissing -> Printf.printf "parents missing"
|
|
| MsgMarriageDate -> Printf.printf "marriage date"
|
|
| MsgMarriagePlace -> Printf.printf "marriage place"
|
|
| MsgSex -> Printf.printf "sex"
|
|
| MsgSpouseMissing iper1 ->
|
|
Printf.printf "spouse missing: %s"
|
|
(person_link !in_file1 base1 iper1 "base1")
|
|
| MsgSpouses iper1 ->
|
|
Printf.printf "more than one spouse match: %s"
|
|
(person_link !in_file1 base1 iper1 "base1")
|
|
| MsgSurname -> Printf.printf "surname");
|
|
Printf.printf "%s" !cr
|
|
|
|
(** Prints messages associates to the two families identifiers in argument *)
|
|
let print_f_messages base1 base2 ifam1 ifam2 res =
|
|
let f1 = foi base1 ifam1 in
|
|
let f2 = foi base2 ifam2 in
|
|
Printf.printf "%s x %s%s/ %s x %s%s"
|
|
(person_link !in_file1 base1 (get_father f1) "base1")
|
|
(person_link !in_file1 base1 (get_mother f1) "base1")
|
|
!cr
|
|
(person_link !in_file2 base2 (get_father f2) "base2")
|
|
(person_link !in_file2 base2 (get_father f2) "base2")
|
|
!cr;
|
|
List.iter (print_message base1) res
|
|
|
|
(** Same, but for persons *)
|
|
let print_p_messages base1 base2 iper1 iper2 res =
|
|
Printf.printf "%s / %s%s"
|
|
(person_link !in_file1 base1 iper1 "base1")
|
|
(person_link !in_file2 base2 iper2 "base2")
|
|
!cr;
|
|
List.iter (print_message base1) res
|
|
|
|
(** [compatible_names src_name dest_name_list]
|
|
Returns true if `src_name` is in `dest_name_list` (case insensitive) *)
|
|
let compatible_names src_name dest_name_list =
|
|
let src_name = Name.lower src_name in
|
|
let dest_name_list = List.map Name.lower dest_name_list in
|
|
List.mem src_name dest_name_list
|
|
|
|
(** [compatible_str_field istr1 istr2]
|
|
Checks the compatibility of two string identifiers, i.e.
|
|
if istr1 is not the empty string identifier, then istr2
|
|
must not be. *)
|
|
let compatible_str_field istr1 istr2 =
|
|
is_empty_string istr1 || not (is_empty_string istr2)
|
|
|
|
(** Returns a list of intervals of SDN (SDN 1 is November 25, 4714 BC Gregorian
|
|
calendar) of the date in argument. An interval has the format (b, b'),
|
|
where b is an optional lower bound (None => no bound), and b' an optional
|
|
upper bound. *)
|
|
let dmy_to_sdn_range_l dmy =
|
|
let sdn_of_dmy dmy =
|
|
let sdn = Calendar.sdn_of_gregorian dmy in
|
|
let sdn = if dmy.month = 0 || dmy.day = 0 then sdn + 1 else sdn in
|
|
let sdn2 =
|
|
if dmy.delta != 0 then sdn + dmy.delta
|
|
else
|
|
let dmy2 =
|
|
{
|
|
year =
|
|
(if dmy.month = 0 || (dmy.month = 12 && dmy.day = 0) then
|
|
dmy.year + 1
|
|
else dmy.year);
|
|
month =
|
|
(if dmy.month = 0 then 1
|
|
else if dmy.day = 0 then
|
|
if dmy.month = 12 then 1 else dmy.month + 1
|
|
else dmy.month);
|
|
day = (if dmy.day = 0 then 1 else dmy.day);
|
|
prec = (if dmy.month = 0 || dmy.day = 0 then Before else Sure);
|
|
delta = dmy.delta;
|
|
}
|
|
in
|
|
let sdn2 = Calendar.sdn_of_gregorian dmy2 in
|
|
if dmy2.prec = Before then sdn2 - 1 else sdn2
|
|
in
|
|
(sdn, sdn2)
|
|
in
|
|
(* S: calls to sdn_of_dmy dmy can be factorized *)
|
|
match dmy.prec with
|
|
| Sure ->
|
|
let sdn1, sdn2 = sdn_of_dmy dmy in
|
|
[ (Some sdn1, Some sdn2) ]
|
|
| Maybe ->
|
|
let sdn1, sdn2 = sdn_of_dmy dmy in
|
|
[ (Some sdn1, Some sdn2); (None, None) ]
|
|
| About ->
|
|
let sdn1, sdn2 = sdn_of_dmy dmy in
|
|
let delta = (sdn2 - sdn1 + 1) * 5 in
|
|
[ (Some (sdn1 - delta), Some (sdn2 + delta)) ]
|
|
| Before ->
|
|
let _sdn1, sdn2 = sdn_of_dmy dmy in
|
|
[ (None, Some sdn2) ]
|
|
| After ->
|
|
let sdn1, _sdn2 = sdn_of_dmy dmy in
|
|
[ (Some sdn1, None) ]
|
|
| OrYear dmy2 ->
|
|
let sdn11, sdn12 = sdn_of_dmy dmy in
|
|
let sdn21, sdn22 = sdn_of_dmy (Date.dmy_of_dmy2 dmy2) in
|
|
[ (Some sdn11, Some sdn12); (Some sdn21, Some sdn22) ]
|
|
| YearInt dmy2 ->
|
|
let sdn11, _sdn12 = sdn_of_dmy dmy in
|
|
let _sdn21, sdn22 = sdn_of_dmy (Date.dmy_of_dmy2 dmy2) in
|
|
[ (Some sdn11, Some sdn22) ]
|
|
|
|
(** [compatible_sdn i1 i2]
|
|
Checks if two intervals `i1` and `i2` (as described for `dmy_to_sdn_range_l`)
|
|
are compatible, i.e. if i2 is a sub interval of i1. *)
|
|
let compatible_sdn (sdn11, sdn12) (sdn21, sdn22) =
|
|
if (sdn21, sdn22) = (None, None) then true
|
|
else
|
|
(* S: Add unit argument to bool2 to make good use of OCaml laziness *)
|
|
let bool1 =
|
|
match (sdn11, sdn21) with
|
|
| Some sdn1, Some sdn2 -> sdn1 <= sdn2
|
|
| None, _ -> true
|
|
| Some _, None -> false
|
|
in
|
|
let bool2 =
|
|
match (sdn12, sdn22) with
|
|
| Some sdn1, Some sdn2 -> sdn1 >= sdn2
|
|
| None, _ -> true
|
|
| Some _, None -> false
|
|
in
|
|
bool1 && bool2
|
|
|
|
(** [compatible_sdn_l l i]
|
|
Checks if there exists an interval in `l` that is compatible with `i` *)
|
|
let compatible_sdn_l sdn1_l sdn2 =
|
|
(* S: replace by List.exists *)
|
|
List.fold_left (fun r sdn1 -> r || compatible_sdn sdn1 sdn2) false sdn1_l
|
|
|
|
(** [compatible_sdn_l l1 l2]
|
|
Checks if for all intervals `i2` in `l2`, there exists an interval `i1` in
|
|
`l1` such that `i1` is compatible with `i2` *)
|
|
let compatible_sdn_ll sdn1_l sdn2_l =
|
|
List.fold_left (fun r sdn2 -> r && compatible_sdn_l sdn1_l sdn2) true sdn2_l
|
|
|
|
(** [compatible_dmys d1 d2]
|
|
Checks if `d1` is compatible with `d2`, i.e. if despite a potential lack
|
|
of precision in the dates, d2 is more precise than d1. *)
|
|
let compatible_dmys dmy1 dmy2 =
|
|
compatible_sdn_ll (dmy_to_sdn_range_l dmy1) (dmy_to_sdn_range_l dmy2)
|
|
|
|
(** [compatible_dates date1 date2]
|
|
Same than before, but also checks the kind of date (Dgreg or Dtext)
|
|
and, in the first case, if calendars are compatible. *)
|
|
let compatible_dates date1 date2 =
|
|
let compatible_cals cal1 cal2 =
|
|
match (cal1, cal2) with
|
|
| Dgregorian, Djulian | Dgregorian, Dfrench -> true
|
|
| _ -> cal1 = cal2
|
|
in
|
|
if date1 = date2 then true
|
|
else
|
|
match (date1, date2) with
|
|
| Dgreg (dmy1, cal1), Dgreg (dmy2, cal2) ->
|
|
compatible_dmys dmy1 dmy2 && compatible_cals cal1 cal2
|
|
| Dgreg (_, _), Dtext _ -> false
|
|
| Dtext _, _ -> true
|
|
|
|
(** Same than before, but for Adef.ctype. *)
|
|
let compatible_cdates cdate1 cdate2 =
|
|
let od1 = Date.od_of_cdate cdate1 in
|
|
let od2 = Date.od_of_cdate cdate2 in
|
|
match (od1, od2) with
|
|
| Some date1, Some date2 -> compatible_dates date1 date2
|
|
| Some _, None -> false
|
|
| None, _ -> true
|
|
|
|
(** Checks if birth between two persons are compatible, i.e. if their birth date
|
|
(baptism date if birth date not provided) and place are compatible, and
|
|
returns a list of messages.
|
|
If birth is not provided, checks bathism date instead.
|
|
If birth/bathism date are not compatible, the returned list will have MsgBirthDate
|
|
If birth place are not compatible, the returned list will have MsgBirthPlace *)
|
|
let compatible_birth p1 p2 =
|
|
let get_birth person =
|
|
if person.birth = Date.cdate_None then person.baptism else person.birth
|
|
in
|
|
let birth1 = get_birth p1 in
|
|
let birth2 = get_birth p2 in
|
|
let res1 = if compatible_cdates birth1 birth2 then [] else [ MsgBirthDate ] in
|
|
let res2 =
|
|
if compatible_str_field p1.birth_place p2.birth_place then []
|
|
else [ MsgBirthPlace ]
|
|
in
|
|
res1 @ res2
|
|
|
|
(** Same than before, but for death. Messages returned are
|
|
MsgDeathDate and MsgDeathPlace *)
|
|
let compatible_death p1 p2 =
|
|
let bool1 =
|
|
p1.death = p2.death
|
|
||
|
|
match (p1.death, p2.death) with
|
|
| Death (_, cdate1), Death (_, cdate2) ->
|
|
let date1 = Date.date_of_cdate cdate1 in
|
|
let date2 = Date.date_of_cdate cdate2 in
|
|
compatible_dates date1 date2
|
|
| NotDead, _
|
|
| DeadYoung, Death (_, _)
|
|
| DeadDontKnowWhen, (Death (_, _) | DeadYoung | DeadDontKnowWhen)
|
|
| DontKnowIfDead, _ ->
|
|
true
|
|
| _ -> (* S: avoid non-exhaustive pattern matching *) false
|
|
in
|
|
let res1 = if bool1 then [] else [ MsgDeathDate ] in
|
|
let res2 =
|
|
if compatible_str_field p1.death_place p2.death_place then []
|
|
else [ MsgDeathPlace ]
|
|
in
|
|
res1 @ res2
|
|
|
|
(** [compatible_sexes p1 p2]
|
|
Returns [] if `p1` and `p2` have the same sex, [MsgSex] otherwise. *)
|
|
let compatible_sexes p1 p2 = if p1.sex = p2.sex then [] else [ MsgSex ]
|
|
|
|
(** [compatible_occupations p1 p2]
|
|
Returns [] if `p1` and `p2` have compatible occupations, [MsgOccupation] otherwise. *)
|
|
let compatible_occupations p1 p2 =
|
|
if compatible_str_field p1.occupation p2.occupation then []
|
|
else [ MsgOccupation ]
|
|
|
|
(** Checks if two persons' names are compatible wrt. their eventual aliases and returns a
|
|
list of messages.
|
|
If first names are not compatible, the returned list will have MsgFirstName.
|
|
If surnames are not compatible, the returned list will have MsgSurname. *)
|
|
let compatible_persons_ligth base1 base2 p1 p2 =
|
|
let fn1 = sou base1 p1.first_name in
|
|
let fn2 = sou base2 p2.first_name in
|
|
let afn2 = fn2 :: List.map (sou base2) p2.first_names_aliases in
|
|
let sn1 = sou base1 p1.surname in
|
|
let sn2 = sou base2 p2.surname in
|
|
let asn2 = sn2 :: List.map (sou base2) p2.surnames_aliases in
|
|
let res1 = if compatible_names fn1 afn2 then [] else [ MsgFirstName ] in
|
|
let res2 = if compatible_names sn1 asn2 then [] else [ MsgSurname ] in
|
|
res1 @ res2
|
|
|
|
(** Checks if two persons are compatible and returns all the messages associated
|
|
to the compatiblity of their name, sex, birth, death and occupation. *)
|
|
let compatible_persons base1 base2 p1 p2 =
|
|
compatible_persons_ligth base1 base2 p1 p2
|
|
@ compatible_sexes p1 p2 @ compatible_birth p1 p2 @ compatible_death p1 p2
|
|
@ compatible_occupations p1 p2
|
|
|
|
(** [find_compatible_persons_ligth base1 base2 iper1 iper2_list]
|
|
Returns the sublist of persons of `iper2_list` that are compatible with
|
|
`iper1` (only checking names). *)
|
|
let rec find_compatible_persons_ligth base1 base2 iper1 iper2_list =
|
|
(* S: not tail recursive, could be *)
|
|
match iper2_list with
|
|
| [] -> []
|
|
| head :: rest ->
|
|
let p1 = gen_person_of_person (poi base1 iper1) in
|
|
let p2 = gen_person_of_person (poi base2 head) in
|
|
let c_rest = find_compatible_persons_ligth base1 base2 iper1 rest in
|
|
if compatible_persons_ligth base1 base2 p1 p2 = [] then head :: c_rest
|
|
else c_rest
|
|
|
|
(** Same than before, but with full compatibility ( name, sex, birth, death and
|
|
occupation) *)
|
|
let rec find_compatible_persons base1 base2 iper1 iper2_list =
|
|
match iper2_list with
|
|
| [] -> []
|
|
| head :: rest ->
|
|
let p1 = gen_person_of_person (poi base1 iper1) in
|
|
let p2 = gen_person_of_person (poi base2 head) in
|
|
let c_rest = find_compatible_persons base1 base2 iper1 rest in
|
|
if compatible_persons base1 base2 p1 p2 = [] then head :: c_rest
|
|
else c_rest
|
|
|
|
(** Checks if the spouse of the persons (whose id are in argument) are
|
|
compatible (only checking names) and returns the associated messages list. *)
|
|
let compatible_unions base1 base2 iper1 iper2 ifam1 ifam2 =
|
|
let get_spouse base iper ifam =
|
|
let f = foi base ifam in
|
|
if iper = get_father f then poi base (get_mother f)
|
|
else poi base (get_father f)
|
|
in
|
|
let spouse1 = gen_person_of_person (get_spouse base1 iper1 ifam1) in
|
|
let spouse2 = gen_person_of_person (get_spouse base2 iper2 ifam2) in
|
|
compatible_persons_ligth base1 base2 spouse1 spouse2
|
|
|
|
(** [find_compatible_unions base1 base2 iper1 iper2_list ifam1 ifam2_list]
|
|
Returns the sublist of families of `ifam2_list` whose union is compatible
|
|
(in the sense of `compatible_unions`). *)
|
|
let rec find_compatible_unions base1 base2 iper1 iper2 ifam1 ifam2_list =
|
|
match ifam2_list with
|
|
| [] -> []
|
|
| head :: rest ->
|
|
let c_rest = find_compatible_unions base1 base2 iper1 iper2 ifam1 rest in
|
|
if compatible_unions base1 base2 iper1 iper2 ifam1 head = [] then
|
|
head :: c_rest
|
|
else c_rest
|
|
|
|
(** [compatible_divorces d1 d2]
|
|
Returns true if divorces are compatible, i.e. if both divorced, then
|
|
checking date compatibility, if d1 is a divorce and d2 is not returns
|
|
false, otherwise returns true. *)
|
|
let compatible_divorces d1 d2 =
|
|
match (d1, d2) with
|
|
| Divorced cdate1, Divorced cdate2 -> compatible_cdates cdate1 cdate2
|
|
| Divorced _, _ -> false
|
|
| _ -> true
|
|
|
|
(** Checks the compatibility of marriages (mariage date, divorce
|
|
and mariage place), then print the list of messages calculated. *)
|
|
let compatible_marriages base1 base2 ifam1 ifam2 =
|
|
let f1 = gen_family_of_family (foi base1 ifam1) in
|
|
let f2 = gen_family_of_family (foi base2 ifam2) in
|
|
let res1 =
|
|
if compatible_cdates f1.marriage f2.marriage then []
|
|
else [ MsgMarriageDate ]
|
|
in
|
|
let res2 =
|
|
if compatible_divorces f1.divorce f2.divorce then [] else [ MsgDivorce ]
|
|
in
|
|
let res3 =
|
|
if compatible_str_field f1.marriage_place f2.marriage_place then []
|
|
else [ MsgMarriagePlace ]
|
|
in
|
|
let res = res1 @ res2 @ res3 in
|
|
if res = [] then () else print_f_messages base1 base2 ifam1 ifam2 res
|
|
|
|
(** Calculates the compatibility of two persons and prints the associated
|
|
messages *)
|
|
let pdiff base1 base2 iper1 iper2 =
|
|
let p1 = gen_person_of_person (poi base1 iper1) in
|
|
let p2 = gen_person_of_person (poi base2 iper2) in
|
|
let res = compatible_persons base1 base2 p1 p2 in
|
|
if res = [] then () else print_p_messages base1 base2 iper1 iper2 res
|
|
|
|
(** Calculates the compatibility of two persons' families and prints the
|
|
associated messages. *)
|
|
let compatible_parents base1 base2 iper1 iper2 =
|
|
let a1 = get_parents (poi base1 iper1) in
|
|
let a2 = get_parents (poi base2 iper2) in
|
|
match (a1, a2) with
|
|
| Some ifam1, Some ifam2 ->
|
|
let f1 = foi base1 ifam1 in
|
|
let f2 = foi base2 ifam2 in
|
|
let _ = pdiff base1 base2 (get_father f1) (get_father f2) in
|
|
let _ = pdiff base1 base2 (get_mother f1) (get_mother f2) in
|
|
compatible_marriages base1 base2 ifam1 ifam2
|
|
| None, _ -> ()
|
|
| Some _, None ->
|
|
print_p_messages base1 base2 iper1 iper2 [ MsgParentsMissing ]
|
|
|
|
(** Checks che compatibility of two persons and their families, and prints it.
|
|
This is performed recursively through their descendants *)
|
|
let rec ddiff base1 base2 iper1 iper2 d_tab =
|
|
(* S: Simplify with statement:
|
|
let ddiff iper1 iper2 = ddiff base1 base2 iper1 iper2 d_tab *)
|
|
let d_check = Gwdb.Marker.get d_tab iper1 in
|
|
if List.mem iper2 d_check then ()
|
|
else
|
|
let _ = Gwdb.Marker.set d_tab iper1 (iper2 :: d_check) in
|
|
let spouse f iper =
|
|
if iper = get_father f then get_mother f else get_father f
|
|
in
|
|
let udiff base1 base2 iper1 iper2 ifam1 ifam2 =
|
|
let fd b1 b2 ip2_list ip1 =
|
|
match find_compatible_persons_ligth b1 b2 ip1 ip2_list with
|
|
| [ ip2 ] -> ddiff base1 base2 ip1 ip2 d_tab
|
|
| [] -> print_p_messages base1 base2 iper1 iper2 [ MsgChildMissing ip1 ]
|
|
| rest_list -> (
|
|
match find_compatible_persons b1 b2 ip1 rest_list with
|
|
| [ best_ip2 ] -> ddiff base1 base2 ip1 best_ip2 d_tab
|
|
| [] -> print_p_messages base1 base2 iper1 iper2 [ MsgBadChild ip1 ]
|
|
| _ -> print_p_messages base1 base2 iper1 iper2 [ MsgChildren ip1 ])
|
|
in
|
|
let f1 = foi base1 ifam1 in
|
|
let f2 = foi base2 ifam2 in
|
|
let p1 = spouse f1 iper1 in
|
|
let p2 = spouse f2 iper2 in
|
|
let d1 = Array.to_list (get_children (foi base1 ifam1)) in
|
|
let d2 = Array.to_list (get_children (foi base2 ifam2)) in
|
|
pdiff base1 base2 p1 p2;
|
|
List.iter (fd base1 base2 d2) d1
|
|
in
|
|
let fu b1 b2 ifam2_list ifam1 =
|
|
match find_compatible_unions b1 b2 iper1 iper2 ifam1 ifam2_list with
|
|
| [ ifam2 ] ->
|
|
compatible_marriages b1 b2 ifam1 ifam2;
|
|
compatible_parents b1 b2
|
|
(spouse (foi base1 ifam1) iper1)
|
|
(spouse (foi base2 ifam2) iper2);
|
|
udiff b1 b2 iper1 iper2 ifam1 ifam2
|
|
| [] ->
|
|
print_p_messages base1 base2 iper1 iper2
|
|
[ MsgSpouseMissing (spouse (foi base1 ifam1) iper1) ]
|
|
| _ ->
|
|
print_p_messages base1 base2 iper1 iper2
|
|
[ MsgSpouses (spouse (foi base1 ifam1) iper1) ]
|
|
in
|
|
let u1 = Array.to_list (get_family (poi base1 iper1)) in
|
|
let u2 = Array.to_list (get_family (poi base2 iper2)) in
|
|
pdiff base1 base2 iper1 iper2;
|
|
List.iter (fu base1 base2 u2) u1
|
|
|
|
(** Returns the eldest persons on the base starting from the persons in argument. *)
|
|
let rec find_top base1 base2 iper1 iper2 =
|
|
let p1 = gen_person_of_person (poi base1 iper1) in
|
|
let p2 = gen_person_of_person (poi base2 iper2) in
|
|
if compatible_persons_ligth base1 base2 p1 p2 = [] then
|
|
let a1 = get_parents (poi base1 iper1) in
|
|
let a2 = get_parents (poi base2 iper2) in
|
|
match (a1, a2) with
|
|
| Some ifam1, Some ifam2 ->
|
|
let f1 = foi base1 ifam1 in
|
|
let f2 = foi base2 ifam2 in
|
|
let f_top_list = find_top base1 base2 (get_father f1) (get_father f2) in
|
|
let m_top_list = find_top base1 base2 (get_mother f1) (get_mother f2) in
|
|
f_top_list @ m_top_list
|
|
| _ -> [ (iper1, iper2) ]
|
|
else (
|
|
Printf.printf " Warning: %s doesn't match %s%s"
|
|
(person_link !in_file1 base1 iper1 "base1")
|
|
(person_link !in_file2 base2 iper2 "base2")
|
|
!cr;
|
|
[])
|
|
|
|
(** Same than ddiff, but starting from the eldest ancestors from the persons in argument *)
|
|
let addiff base1 base2 iper1 iper2 d_tab =
|
|
let topdiff (iper1, iper2) =
|
|
Printf.printf "==> %s / %s%s"
|
|
(person_link !in_file1 base1 iper1 "base1")
|
|
(person_link !in_file2 base2 iper2 "base2")
|
|
!cr;
|
|
ddiff base1 base2 iper1 iper2 d_tab
|
|
in
|
|
Printf.printf "Building top list...%s" !cr;
|
|
let top_list = find_top base1 base2 iper1 iper2 in
|
|
Printf.printf "Top list built.%s" !cr;
|
|
List.iter topdiff top_list
|
|
|
|
(* Main *)
|
|
|
|
let gwdiff base1 base2 iper1 iper2 d_mode ad_mode =
|
|
let desc_tab = Gwdb.iper_marker (Gwdb.ipers base1) [] in
|
|
match (d_mode, ad_mode) with
|
|
| true, _ | false, false -> ddiff base1 base2 iper1 iper2 desc_tab
|
|
| false, true -> addiff base1 base2 iper1 iper2 desc_tab
|
|
|
|
let p1_fn = ref ""
|
|
let p1_occ = ref 0
|
|
let p1_sn = ref ""
|
|
let p2_fn = ref ""
|
|
let p2_occ = ref 0
|
|
let p2_sn = ref ""
|
|
|
|
type arg_state = ASnone | ASwaitP1occ | ASwaitP1sn | ASwaitP2occ | ASwaitP2sn
|
|
|
|
let arg_state = ref ASnone
|
|
let mem = ref false
|
|
let d_mode = ref false
|
|
let ad_mode = ref false
|
|
|
|
let speclist =
|
|
[
|
|
( "-1",
|
|
Arg.String
|
|
(fun s ->
|
|
p1_fn := s;
|
|
arg_state := ASwaitP1occ),
|
|
"<fn> <occ> <sn> : (mandatory) defines starting person in base1" );
|
|
( "-2",
|
|
Arg.String
|
|
(fun s ->
|
|
p2_fn := s;
|
|
arg_state := ASwaitP2occ),
|
|
"<fn> <occ> <sn> : (mandatory) defines starting person in base2" );
|
|
("-ad", Arg.Set ad_mode, ": checks descendants of all ascendants ");
|
|
("-d", Arg.Set d_mode, ": checks descendants (default)");
|
|
( "-html",
|
|
Arg.String
|
|
(fun s ->
|
|
html := true;
|
|
root := s),
|
|
"<root>: HTML format used for report" );
|
|
("-mem", Arg.Set mem, ": save memory space, but slower");
|
|
]
|
|
|
|
let anonfun s =
|
|
match !arg_state with
|
|
| ASnone ->
|
|
if !in_file1 = "" then in_file1 := s
|
|
else if !in_file2 = "" then in_file2 := s
|
|
else raise (Arg.Bad "Too much arguments")
|
|
| ASwaitP1occ -> (
|
|
try
|
|
p1_occ := int_of_string s;
|
|
arg_state := ASwaitP1sn
|
|
with Failure _ -> raise (Arg.Bad "Numeric value for occ (-1)!"))
|
|
| ASwaitP1sn ->
|
|
p1_sn := s;
|
|
arg_state := ASnone
|
|
| ASwaitP2occ -> (
|
|
try
|
|
p2_occ := int_of_string s;
|
|
arg_state := ASwaitP2sn
|
|
with Failure _ -> raise (Arg.Bad "Numeric value for occ (-2)!"))
|
|
| ASwaitP2sn ->
|
|
p2_sn := s;
|
|
arg_state := ASnone
|
|
|
|
let errmsg = "Usage: " ^ Sys.argv.(0) ^ " [options] base1 base2\nOptions are: "
|
|
|
|
let check_args () =
|
|
Arg.parse speclist anonfun errmsg;
|
|
if !in_file1 = "" then (
|
|
Printf.printf "Missing reference data base\n";
|
|
Printf.printf "Use option -help for usage\n";
|
|
flush stdout;
|
|
exit 2);
|
|
if !in_file2 = "" then (
|
|
Printf.printf "Missing destination data base\n";
|
|
Printf.printf "Use option -help for usage\n";
|
|
flush stdout;
|
|
exit 2);
|
|
if !p1_fn = "" then (
|
|
Printf.printf "-1 parameter is mandatory\n";
|
|
Printf.printf "Use option -help for usage\n";
|
|
flush stdout;
|
|
exit 2);
|
|
if !p1_sn = "" then (
|
|
Printf.printf "Incomplete -1 parameter\n";
|
|
Printf.printf "Use option -help for usage\n";
|
|
flush stdout;
|
|
exit 2);
|
|
if !p2_fn = "" then (
|
|
Printf.printf "-2 parameter is mandatory\n";
|
|
Printf.printf "Use option -help for usage\n";
|
|
flush stdout;
|
|
exit 2);
|
|
if !p2_sn = "" then (
|
|
Printf.printf "Incomplete -2 parameter\n";
|
|
Printf.printf "Use option -help for usage\n";
|
|
flush stdout;
|
|
exit 2)
|
|
|
|
let main () =
|
|
let _ = check_args () in
|
|
let _ = if not !html then cr := "\n" else cr := "<BR>\n" in
|
|
let load_base file =
|
|
let base = open_base file in
|
|
let () = load_ascends_array base in
|
|
let () = load_strings_array base in
|
|
let () =
|
|
if not !mem then
|
|
let () = load_unions_array base in
|
|
let () = load_couples_array base in
|
|
let () = load_descends_array base in
|
|
()
|
|
in
|
|
base
|
|
in
|
|
(* Reference base *)
|
|
let base1 = load_base !in_file1 in
|
|
(* Destination base *)
|
|
let base2 = if !in_file1 != !in_file2 then load_base !in_file2 else base1 in
|
|
let iper1 = person_of_key base1 !p1_fn !p1_sn !p1_occ in
|
|
let iper2 = person_of_key base2 !p2_fn !p2_sn !p2_occ in
|
|
if !html then Printf.printf "<BODY>\n";
|
|
(match (iper1, iper2) with
|
|
| None, _ ->
|
|
Printf.printf "Cannot find person %s.%d %s in reference base" !p1_fn
|
|
!p1_occ !p1_sn
|
|
| _, None ->
|
|
Printf.printf "Cannot find person %s.%d %s in destination base" !p2_fn
|
|
!p2_occ !p2_sn
|
|
| Some iper1, Some iper2 -> gwdiff base1 base2 iper1 iper2 !d_mode !ad_mode);
|
|
if !html then Printf.printf "</BODY>\n"
|
|
|
|
let _ = Printexc.print main ()
|