(* $Id: gwdiff.ml,v 1.4 2002/02/03 19:02:48 ddr Exp $ *)
(* Copyright (c) 2001 Ludovic LEDIEU *)
open Def;
open Gutil;
(*= TODO =====================================================================
- Improve the way not to check several time the same persons.
=========================================================================== *)
value in_file1 = ref "";
value in_file2 = ref "";
value html = ref False;
value root = ref "";
value cr = ref "";
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 ]
;
value person_string base iper =
let p = poi base iper in
let fn = sou base p.first_name in
let sn = sou base p.surname in
if sn = "?" || fn = "?" then
fn ^ " " ^ sn ^ " (#" ^ string_of_int (Adef.int_of_iper iper) ^ ")"
else fn ^ "." ^ (string_of_int p.occ) ^ " " ^ sn
;
value person_link bname base iper target =
if html.val then
Printf.sprintf "%s"
root.val bname (Adef.int_of_iper iper) target (person_string base iper)
else
person_string base iper
;
value print_message base1 base2 msg =
do {
Printf.printf " ";
match msg with
[ MsgBadChild iper1 ->
Printf.printf "can not isolate one child match: %s"
(person_link in_file1.val base1 iper1 "base1")
| MsgBirthDate ->
Printf.printf "birth date"
| MsgBirthPlace ->
Printf.printf "birth place"
| MsgChildMissing iper1 ->
Printf.printf "child missing: %s"
(person_link in_file1.val base1 iper1 "base1")
| MsgChildren iper1 ->
Printf.printf "more than one child match: %s"
(person_link in_file1.val 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.val base1 iper1 "base1")
| MsgSpouses iper1 ->
Printf.printf "more than one spouse match: %s"
(person_link in_file1.val base1 iper1 "base1")
| MsgSurname ->
Printf.printf "surname" ];
Printf.printf "%s" cr.val
}
;
value print_f_messages base1 base2 ifam1 ifam2 res =
let c1 = coi base1 ifam1 in
let c2 = coi base2 ifam2 in
do {
Printf.printf "%s x %s%s/ %s x %s%s"
(person_link in_file1.val base1 c1.father "base1")
(person_link in_file1.val base1 c1.mother "base1")
cr.val
(person_link in_file2.val base2 c2.father "base2")
(person_link in_file2.val base2 c2.mother "base2")
cr.val;
List.iter (print_message base1 base2) res
}
;
value print_p_messages base1 base2 iper1 iper2 res =
do {
Printf.printf "%s / %s%s"
(person_link in_file1.val base1 iper1 "base1")
(person_link in_file2.val base2 iper2 "base2")
cr.val;
List.iter (print_message base1 base2) res
}
;
value 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
;
value compatible_str_field istr1 istr2 =
(Adef.int_of_istr istr1 = 0) || (Adef.int_of_istr istr2 != 0)
;
value 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
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 y ->
let dmy2 =
{ year = y + 1;
month = 0;
day = 0;
prec = Sure;
delta = 0 }
in
let (sdn11, sdn12) = sdn_of_dmy dmy in
let (sdn21, sdn22) = sdn_of_dmy dmy2 in
[ (Some sdn11, Some sdn12) ; (Some sdn21, Some sdn22) ]
| YearInt y ->
let dmy2 =
{ year = y + 1;
month = 0;
day = 0;
prec = Sure;
delta = 0 }
in
let (sdn11, sdn12) = sdn_of_dmy dmy in
let (sdn21, sdn22) = sdn_of_dmy dmy2 in
[ (Some sdn11, Some sdn22) ] ]
;
value compatible_sdn (sdn11, sdn12) (sdn21, sdn22) =
if (sdn21, sdn22) = (None, None) then True
else
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
;
value compatible_sdn_l sdn1_l sdn2 =
List.fold_left (fun r sdn1 -> r || (compatible_sdn sdn1 sdn2))
False sdn1_l
;
value compatible_sdn_ll sdn1_l sdn2_l =
List.fold_left (fun r sdn2 -> r && (compatible_sdn_l sdn1_l sdn2))
True sdn2_l
;
value compatible_dmys dmy1 dmy2 =
compatible_sdn_ll (dmy_to_sdn_range_l dmy1) (dmy_to_sdn_range_l dmy2)
;
value 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 ]
;
value compatible_codates codate1 codate2 =
let od1 = Adef.od_of_codate codate1 in
let od2 = Adef.od_of_codate codate2 in
match (od1, od2) with
[ (Some date1, Some date2) -> compatible_dates date1 date2
| (Some _, None) -> False
| (None, _) -> True ]
;
value compatible_birth base1 base2 p1 p2 =
let get_birth person =
if person.birth = Adef.codate_None then person.baptism
else person.birth
in
let birth1 = get_birth p1 in
let birth2 = get_birth p2 in
let res1 =
if compatible_codates birth1 birth2 then []
else [ MsgBirthDate ]
in
let res2 =
if compatible_str_field p1.birth_place p2.birth_place then []
else [ MsgBirthPlace ]
in
res1 @ res2
;
value compatible_death base1 base2 p1 p2 =
let bool1 =
p1.death = p2.death ||
match (p1.death, p2.death) with
[ (Death _ cdate1, Death _ cdate2) ->
let date1 = Adef.date_of_cdate cdate1 in
let date2 = Adef.date_of_cdate cdate2 in
compatible_dates date1 date2
| (NotDead, _)
| (DeadYoung, Death _ _)
| (DeadDontKnowWhen, Death _ _ | DeadYoung | DeadDontKnowWhen)
| (DontKnowIfDead, _) -> True
| _ -> 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
;
value compatible_sexes base1 base2 p1 p2 =
if p1.sex = p2.sex then []
else [ MsgSex ]
;
value compatible_occupations base1 base2 p1 p2 =
if compatible_str_field p1.occupation p2.occupation then []
else [ MsgOccupation ]
;
value 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
;
value compatible_persons base1 base2 p1 p2 =
compatible_persons_ligth base1 base2 p1 p2
@ compatible_sexes base1 base2 p1 p2
@ compatible_birth base1 base2 p1 p2
@ compatible_death base1 base2 p1 p2
@ compatible_occupations base1 base2 p1 p2
;
value rec find_compatible_persons_ligth base1 base2 iper1 iper2_list =
match iper2_list with
[ [] -> []
| [ head :: rest ] ->
let p1 = poi base1 iper1 in
let p2 = 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 ]
;
value rec find_compatible_persons base1 base2 iper1 iper2_list =
match iper2_list with
[ [] -> []
| [ head :: rest ] ->
let p1 = poi base1 iper1 in
let p2 = 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 ]
;
value compatible_unions base1 base2 iper1 iper2 ifam1 ifam2 =
let get_spouse base iper ifam =
let c = coi base ifam in
if iper = c.father then poi base c.mother
else poi base c.father
in
let spouse1 = get_spouse base1 iper1 ifam1 in
let spouse2 = get_spouse base2 iper2 ifam2 in
compatible_persons_ligth base1 base2 spouse1 spouse2
;
value 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 ]
;
value compatible_divorces d1 d2 =
match (d1, d2) with
[ (Divorced codate1, Divorced codate2) ->
compatible_codates codate1 codate2
| (Divorced _, _) -> False
| _ -> True ]
;
value compatible_marriages base1 base2 ifam1 ifam2 =
let f1 = foi base1 ifam1 in
let f2 = foi base2 ifam2 in
let res1 =
if compatible_codates 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
;
value pdiff base1 base2 iper1 iper2 =
let p1 = poi base1 iper1 in
let p2 = 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
;
value compatible_parents base1 base2 iper1 iper2 =
let a1 = (aoi base1 iper1).parents in
let a2 = (aoi base2 iper2).parents in
match (a1, a2) with
[ (Some ifam1, Some ifam2) ->
let c1 = coi base1 ifam1 in
let c2 = coi base2 ifam2 in
let _ = pdiff base1 base2 c1.father c2.father in
let _ = pdiff base1 base2 c1.mother c2.mother in
compatible_marriages base1 base2 ifam1 ifam2
| (None, _) -> ()
| (Some _, None) ->
print_p_messages base1 base2 iper1 iper2 [ MsgParentsMissing ] ]
;
value rec ddiff base1 base2 iper1 iper2 d_tab =
let d_check = d_tab.(Adef.int_of_iper iper1) in
if List.mem iper2 d_check then ()
else
let _ = d_tab.(Adef.int_of_iper iper1) := [iper2 :: d_check ] in
let spouse c iper =
if iper = c.father then c.mother
else c.father
in
let rec udiff base1 base2 iper1 iper2 r 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 c1 = coi base1 ifam1 in
let c2 = coi base2 ifam2 in
let p1 = spouse c1 iper1 in
let p2 = spouse c2 iper2 in
let d1 = Array.to_list (doi base1 ifam1).children in
let d2 = Array.to_list (doi base2 ifam2).children in
do {
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] ->
do {
compatible_marriages b1 b2 ifam1 ifam2;
compatible_parents b1 b2 (spouse (coi base1 ifam1) iper1)
(spouse (coi base2 ifam2) iper2);
udiff b1 b2 iper1 iper2 True ifam1 ifam2
}
| [] ->
print_p_messages base1 base2 iper1 iper2
[ MsgSpouseMissing (spouse (coi base1 ifam1) iper1) ]
| _ ->
print_p_messages base1 base2 iper1 iper2
[ MsgSpouses (spouse (coi base1 ifam1) iper1) ] ]
in
let u1 = Array.to_list (uoi base1 iper1).family in
let u2 = Array.to_list (uoi base2 iper2).family in
do {
pdiff base1 base2 iper1 iper2;
List.iter (fu base1 base2 u2) u1
}
;
value rec find_top base1 base2 iper1 iper2 =
let p1 = poi base1 iper1 in
let p2 = poi base2 iper2 in
if compatible_persons_ligth base1 base2 p1 p2 = [] then
let a1 = (aoi base1 iper1).parents in
let a2 = (aoi base2 iper2).parents in
match (a1, a2) with
[ (Some ifam1, Some ifam2) ->
let c1 = coi base1 ifam1 in
let c2 = coi base2 ifam2 in
let f_top_list = find_top base1 base2 c1.father c2.father in
let m_top_list = find_top base1 base2 c1.mother c2.mother in
f_top_list @ m_top_list
| _ -> [(iper1, iper2)] ]
else do {
Printf.printf " Warning: %s doesn't match %s%s"
(person_link in_file1.val base1 iper1 "base1")
(person_link in_file2.val base2 iper2 "base2")
cr.val;
[]
}
;
value addiff base1 base2 iper1 iper2 d_tab =
let topdiff (iper1, iper2) =
do {
Printf.printf "==> %s / %s%s"
(person_link in_file1.val base1 iper1 "base1")
(person_link in_file2.val base2 iper2 "base2")
cr.val;
ddiff base1 base2 iper1 iper2 d_tab
}
in
do {
Printf.printf "Building top list...%s" cr.val;
let top_list = find_top base1 base2 iper1 iper2 in
Printf.printf "Top list built.%s" cr.val;
List.iter topdiff top_list
}
;
(* Main *)
value gwdiff base1 base2 iper1 iper2 d_mode ad_mode =
let desc_tab = Array.create base1.data.persons.len [] 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 ]
;
value p1_fn = ref "";
value p1_occ = ref 0;
value p1_sn = ref "";
value p2_fn = ref "";
value p2_occ = ref 0;
value p2_sn = ref "";
type arg_state =
[ ASnone
| ASwaitP1occ
| ASwaitP1sn
| ASwaitP2occ
| ASwaitP2sn ]
;
value arg_state = ref ASnone;
value mem = ref False;
value d_mode = ref False;
value ad_mode = ref False;
value speclist =
[("-1", Arg.String (fun s -> do { p1_fn.val := s; arg_state.val := ASwaitP1occ }),
" : (mandatory) defines starting person in base1");
("-2", Arg.String (fun s -> do { p2_fn.val := s; arg_state.val := ASwaitP2occ }),
" : (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 -> do { html.val := True; root.val := s }),
": HTML format used for report");
("-mem", Arg.Set mem, ": save memory space, but slower") ]
;
value anonfun s =
match arg_state.val with
[ ASnone ->
if in_file1.val = "" then in_file1.val := s
else if in_file2.val = "" then in_file2.val := s
else raise (Arg.Bad "Too much arguments")
| ASwaitP1occ ->
try
do {
p1_occ.val := int_of_string s;
arg_state.val := ASwaitP1sn
}
with
[ Failure _ -> raise (Arg.Bad "Numeric value for occ (-1)!") ]
| ASwaitP1sn -> do { p1_sn.val := s; arg_state.val := ASnone }
| ASwaitP2occ ->
try
do {
p2_occ.val := int_of_string s;
arg_state.val := ASwaitP2sn
}
with
[ Failure _ -> raise (Arg.Bad "Numeric value for occ (-2)!") ]
| ASwaitP2sn -> do { p2_sn.val := s; arg_state.val := ASnone }]
;
value errmsg =
"Usage: " ^ Sys.argv.(0) ^ " \
[options] base1 base2
Options are: "
;
value check_args () =
do {
Argl.parse speclist anonfun errmsg;
if in_file1.val = "" then
do {
Printf.printf "Missing reference data base\n";
Printf.printf "Use option -help for usage\n";
flush stdout;
exit 2
}
else ();
if in_file2.val = "" then
do {
Printf.printf "Missing destination data base\n";
Printf.printf "Use option -help for usage\n";
flush stdout;
exit 2
}
else ();
if p1_fn.val = "" then
do {
Printf.printf "-1 parameter is mandatory\n";
Printf.printf "Use option -help for usage\n";
flush stdout;
exit 2
}
else ();
if p1_sn.val = "" then
do {
Printf.printf "Incomplete -1 parameter\n";
Printf.printf "Use option -help for usage\n";
flush stdout;
exit 2
}
else ();
if p2_fn.val = "" then
do {
Printf.printf "-2 parameter is mandatory\n";
Printf.printf "Use option -help for usage\n";
flush stdout;
exit 2
}
else ();
if p2_sn.val = "" then
do {
Printf.printf "Incomplete -2 parameter\n";
Printf.printf "Use option -help for usage\n";
flush stdout;
exit 2
}
else ()
}
;
value main () =
let _ = check_args () in
let _ =
if not html.val then cr.val := "\n"
else cr.val := "
\n"
in
(* Reference base *)
let base1 = Iobase.input in_file1.val in
let _ = base1.data.ascends.array () in
let _ = base1.data.strings.array () in
let _ =
if not mem.val then
let _ = base1.data.persons.array () in
let _ = base1.data.families.array () in
let _ = base1.data.couples.array () in
let _ = base1.data.unions.array () in
let _ = base1.data.descends.array () in
()
else ()
in
(* Destination base *)
let base2 =
if in_file1.val != in_file2.val then
let base2 = Iobase.input in_file2.val in
let _ = base2.data.ascends.array () in
let _ = base2.data.strings.array () in
let _ =
if not mem.val then
let _ = base2.data.persons.array () in
let _ = base2.data.families.array () in
let _ = base2.data.couples.array () in
let _ = base2.data.unions.array () in
let _ = base2.data.descends.array () in
()
else ()
in
base2
else
(* Reference = Destination *)
base1
in
let iper1 = person_ht_find_unique base1 p1_fn.val p1_sn.val p1_occ.val in
let iper2 = person_ht_find_unique base2 p2_fn.val p2_sn.val p2_occ.val in
do {
if html.val then
Printf.printf "\n"
else ();
gwdiff base1 base2 iper1 iper2 d_mode.val ad_mode.val;
if html.val then
Printf.printf "\n"
else ()
}
;
Printexc.catch main ();