Initial comit - Clone
This commit is contained in:
178
lib/GWPARAM.ml
Normal file
178
lib/GWPARAM.ml
Normal file
@@ -0,0 +1,178 @@
|
||||
(** This module allows plugins to modify geneweb configuration.
|
||||
|
||||
This approch is preffered to Functors or library variants
|
||||
for simple functions if it does not come with a performance cost.
|
||||
*)
|
||||
|
||||
let nb_errors = ref 0
|
||||
let errors_undef = ref []
|
||||
let errors_other = ref []
|
||||
let set_vars = ref []
|
||||
let gwd_cmd = ref ""
|
||||
|
||||
type syslog_level =
|
||||
[ `LOG_ALERT
|
||||
| `LOG_CRIT
|
||||
| `LOG_DEBUG
|
||||
| `LOG_EMERG
|
||||
| `LOG_ERR
|
||||
| `LOG_INFO
|
||||
| `LOG_NOTICE
|
||||
| `LOG_WARNING ]
|
||||
|
||||
module Default = struct
|
||||
let init () = Secure.add_assets Filename.current_dir_name
|
||||
|
||||
let base_path pref bname =
|
||||
List.fold_right Filename.concat (Secure.base_dir () :: pref) bname
|
||||
|
||||
let bpath bname = Filename.concat (Secure.base_dir ()) bname
|
||||
|
||||
(** [output_error ?headers ?content conf code]
|
||||
Send the http status [code], [headers] and
|
||||
[content] if provided, or default content otherwise.
|
||||
*)
|
||||
let output_error =
|
||||
let output_file conf fn =
|
||||
let ic = open_in fn in
|
||||
try
|
||||
in_channel_length ic |> really_input_string ic
|
||||
|> Output.print_sstring conf;
|
||||
close_in ic
|
||||
with _ -> ( try close_in ic with _ -> ())
|
||||
in
|
||||
fun ?(headers = []) ?(content : Adef.safe_string option) conf code ->
|
||||
Output.status conf code;
|
||||
List.iter (Output.header conf "%s") headers;
|
||||
Output.print_string conf (Adef.encoded "<h1>Incorrect request</h1>");
|
||||
match content with
|
||||
| Some content -> Output.print_string conf content
|
||||
| None -> (
|
||||
let code =
|
||||
match code with
|
||||
| Def.Bad_Request -> "400"
|
||||
| Unauthorized -> "401"
|
||||
| Forbidden -> "403"
|
||||
| Not_Found -> "404"
|
||||
| Conflict -> "409"
|
||||
| Internal_Server_Error -> "500"
|
||||
| Service_Unavailable -> "503"
|
||||
| OK | Moved_Temporarily -> assert false
|
||||
in
|
||||
let fname lang =
|
||||
code ^ "-" ^ lang ^ ".html"
|
||||
|> Filename.concat "etc" |> Mutil.search_asset_opt
|
||||
in
|
||||
match fname conf.lang with
|
||||
| Some fn -> output_file conf fn
|
||||
| None -> (
|
||||
match fname "en" with
|
||||
| Some fn -> output_file conf fn
|
||||
| None -> Output.print_sstring conf ""))
|
||||
|
||||
(** Calcul les droits de visualisation d'une personne en
|
||||
fonction de son age.
|
||||
Renvoie (dans l'ordre des tests) :
|
||||
- Vrai si : magicien ou ami ou la personne est public
|
||||
- Vrai si : la personne est en si_titre, si elle a au moins un
|
||||
titre et que public_if_title = yes dans le fichier gwf
|
||||
- Faux si : la personne n'est pas décédée et private_years > 0
|
||||
- Vrai si : la personne est plus agée (en fonction de la date de
|
||||
naissance ou de la date de baptème) que privates_years
|
||||
- Faux si : la personne est plus jeune (en fonction de la date de
|
||||
naissance ou de la date de baptème) que privates_years
|
||||
- Vrai si : la personne est décédée depuis plus de privates_years
|
||||
- Faux si : la personne est décédée depuis moins de privates_years
|
||||
- Vrai si : la personne a entre 80 et 120 ans et qu'elle n'est pas
|
||||
privée et public_if_no_date = yes
|
||||
- Vrai si : la personne s'est mariée depuis plus de private_years
|
||||
- Faux dans tous les autres cas *)
|
||||
let p_auth conf base p =
|
||||
conf.Config.wizard || conf.friend
|
||||
|| Gwdb.get_access p = Public
|
||||
|| conf.public_if_titles
|
||||
&& Gwdb.get_access p = IfTitles
|
||||
&& Gwdb.nobtitles base conf.allowed_titles conf.denied_titles p <> []
|
||||
||
|
||||
let death = Gwdb.get_death p in
|
||||
if death = NotDead then conf.private_years < 1
|
||||
else
|
||||
let check_date d lim none =
|
||||
match d with
|
||||
| None -> none ()
|
||||
| Some d ->
|
||||
let a = Date.time_elapsed d conf.today in
|
||||
if a.Def.year > lim then true
|
||||
else if a.year < conf.private_years then false
|
||||
else a.month > 0 || a.day > 0
|
||||
in
|
||||
check_date (Gwdb.get_birth p |> Date.cdate_to_dmy_opt) conf.private_years
|
||||
@@ fun () ->
|
||||
check_date
|
||||
(Gwdb.get_baptism p |> Date.cdate_to_dmy_opt)
|
||||
conf.private_years
|
||||
@@ fun () ->
|
||||
check_date
|
||||
(Gwdb.get_death p |> Date.dmy_of_death)
|
||||
conf.private_years_death
|
||||
@@ fun () ->
|
||||
(Gwdb.get_access p <> Def.Private && conf.public_if_no_date)
|
||||
||
|
||||
let families = Gwdb.get_family p in
|
||||
let len = Array.length families in
|
||||
let rec loop i =
|
||||
i < len
|
||||
&& check_date
|
||||
(Array.get families i |> Gwdb.foi base |> Gwdb.get_marriage
|
||||
|> Date.cdate_to_dmy_opt)
|
||||
conf.private_years_marriage
|
||||
(fun () -> loop (i + 1))
|
||||
in
|
||||
loop 0
|
||||
|
||||
let syslog (level : syslog_level) msg =
|
||||
let tm = Unix.(time () |> localtime) in
|
||||
let level =
|
||||
match level with
|
||||
| `LOG_EMERG -> "EMERGENCY"
|
||||
| `LOG_ALERT -> "ALERT"
|
||||
| `LOG_CRIT -> "CRITICAL"
|
||||
| `LOG_ERR -> "ERROR"
|
||||
| `LOG_WARNING -> "WARNING"
|
||||
| `LOG_NOTICE -> "NOTICE"
|
||||
| `LOG_INFO -> "INFO"
|
||||
| `LOG_DEBUG -> "DEBUG"
|
||||
in
|
||||
Printf.eprintf "[%s]: %s %s\n"
|
||||
(Mutil.sprintf_date tm : Adef.safe_string :> string)
|
||||
level msg
|
||||
|
||||
let wrap_output (conf : Config.config) (title : Adef.safe_string)
|
||||
(content : unit -> unit) =
|
||||
Output.print_sstring conf {|<!DOCTYPE html><head><title>|};
|
||||
Output.print_string conf title;
|
||||
Output.print_sstring conf {|</title>|};
|
||||
Output.print_sstring conf {|<meta name="robots" content="none">|};
|
||||
Output.print_sstring conf {|<meta charset="|};
|
||||
Output.print_sstring conf conf.charset;
|
||||
Output.print_sstring conf {|">|};
|
||||
Output.print_sstring conf
|
||||
{|<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">|};
|
||||
Output.print_sstring conf {|</head>|};
|
||||
Output.print_sstring conf "<body>";
|
||||
content ();
|
||||
Output.print_sstring conf {|</body></html>|}
|
||||
end
|
||||
|
||||
let init = ref Default.init
|
||||
let base_path = ref Default.base_path
|
||||
let bpath = ref Default.bpath
|
||||
let output_error = ref Default.output_error
|
||||
let p_auth = ref Default.p_auth
|
||||
let syslog = ref Default.syslog
|
||||
|
||||
(** [wrap_output conf title content]
|
||||
Plugins defining a page content but not a complete UI
|
||||
may want to wrap their page using [wrap_output].
|
||||
*)
|
||||
let wrap_output = ref Default.wrap_output
|
||||
110
lib/GWPARAM.mli
Normal file
110
lib/GWPARAM.mli
Normal file
@@ -0,0 +1,110 @@
|
||||
val nb_errors : int ref
|
||||
val errors_undef : string list ref
|
||||
val errors_other : string list ref
|
||||
val set_vars : string list ref
|
||||
val gwd_cmd : string ref
|
||||
|
||||
type syslog_level =
|
||||
[ `LOG_EMERG (** A panic condition. *)
|
||||
| `LOG_ALERT
|
||||
(** A condition that should be corrected immediately,
|
||||
such as a corrupted system database. *)
|
||||
| `LOG_CRIT (** Critical conditions, such as hard device errors. *)
|
||||
| `LOG_ERR (** Errors. *)
|
||||
| `LOG_WARNING (** Warning messages. *)
|
||||
| `LOG_DEBUG
|
||||
(** Conditions that are not error conditions,
|
||||
but that may require special handling. *)
|
||||
| `LOG_INFO (** Informational messages. *)
|
||||
| `LOG_NOTICE
|
||||
(** Messages that contain information
|
||||
normally of use only when debugging a program. *)
|
||||
]
|
||||
(** The level of log gravity. See SYSLOG(3) *)
|
||||
|
||||
(* S: Move it to gwd_lib? *)
|
||||
|
||||
val init : (unit -> unit) ref
|
||||
(** Function called before gwd starts
|
||||
e.g. inititialise assets folders in Secure module. *)
|
||||
|
||||
val base_path : (string list -> string -> string) ref
|
||||
(** [!base_path pref fname] function that returns a path to a file identified by [pref] [fname]
|
||||
related to bases. [pref] is like a category for file [fname].
|
||||
|
||||
See {!val:GWPARAM.Default.base_path} for a concrete example.
|
||||
*)
|
||||
|
||||
val bpath : (string -> string) ref
|
||||
(** Same as {!val:base_path}, but without the prefix (avoid unecessary empty list). *)
|
||||
|
||||
val output_error :
|
||||
(?headers:string list ->
|
||||
?content:Adef.safe_string ->
|
||||
Config.config ->
|
||||
Def.httpStatus ->
|
||||
unit)
|
||||
ref
|
||||
(** [!output_error ?headers ?content conf status] default function that send the http status [status].
|
||||
Also send [headers] and use [content] (typically a HTML string describing the error) if provided.
|
||||
*)
|
||||
|
||||
val p_auth : (Config.config -> Gwdb.base -> Gwdb.person -> bool) ref
|
||||
(** Check if a person should be displayed or not *)
|
||||
|
||||
val syslog : (syslog_level -> string -> unit) ref
|
||||
(** [!syslog level log] log message [log] with gravity level [level] on stderr. *)
|
||||
|
||||
val wrap_output :
|
||||
(Config.config -> Adef.safe_string -> (unit -> unit) -> unit) ref
|
||||
(** [wrap_output conf title content]
|
||||
Wrap the display of [title] and [content] in a defined template.
|
||||
*)
|
||||
|
||||
module Default : sig
|
||||
val init : unit -> unit
|
||||
(** Inititialise assets directories for gwd server:
|
||||
* current directory
|
||||
*)
|
||||
|
||||
val base_path : string list -> string -> string
|
||||
(** Use concatenation of [Secure.base_dir ()], [pref] and [fname] *)
|
||||
|
||||
val bpath : string -> string
|
||||
(** [Filename.concat (Secure.base_dir ())] *)
|
||||
|
||||
val output_error :
|
||||
?headers:string list ->
|
||||
?content:Adef.safe_string ->
|
||||
Config.config ->
|
||||
Def.httpStatus ->
|
||||
unit
|
||||
(** If [?content] is not set, sends page content from {/etc/<status-code>-<lang>.html}.
|
||||
If the current lang is not available, use `en` *)
|
||||
|
||||
val p_auth : Config.config -> Gwdb.base -> Gwdb.person -> bool
|
||||
(** Calculate the access rights to the person's information in
|
||||
according to his age.
|
||||
Returns (in the order of the tests) :
|
||||
- `true` if requester is wizard or friend or person is public
|
||||
- `true` if person has at least one title and {i public_if_title}
|
||||
is set to {i yes} in gwf config file
|
||||
- `false` if person is alive and {i private_years} > 0
|
||||
- `true` if person is older (depending on the date of
|
||||
birth or baptism date) then {i privates_years}
|
||||
- `false` if person is younger (depending on the date of
|
||||
birth or baptism date) then {i privates_years}
|
||||
- `true` if person has been deceased for more than {i privates_years}
|
||||
- `false` if person has been deceased for less than {i privates_years}
|
||||
- `true` if person is between 80 and 120 years old and he is not beeing
|
||||
private and {i public_if_no_date} is set to {i yes} in gwf config file
|
||||
- `true` if person has been married for more than {i private_years}
|
||||
- `false` otherwise
|
||||
*)
|
||||
|
||||
val syslog : syslog_level -> string -> unit
|
||||
(** Prints on stderr using `"[date]: level message"` format. *)
|
||||
|
||||
val wrap_output : Config.config -> Adef.safe_string -> (unit -> unit) -> unit
|
||||
(** Display in a very basic HTML doc, with no CSS or JavaScript. *)
|
||||
end
|
||||
43
lib/GWPARAM_ITL.ml
Normal file
43
lib/GWPARAM_ITL.ml
Normal file
@@ -0,0 +1,43 @@
|
||||
module Default = struct
|
||||
let init_cache _conf _base _ip _nb_asc _from_gen_desc _nb_desc = ()
|
||||
let max_ancestor_level _conf _base _ip _bname _max start = start
|
||||
let max_descendant_level _conf _base _ip _max = 0
|
||||
let tree_generation_list _conf _base _bname _p = (None, None)
|
||||
let get_father _conf _base _bsrc _ip = None
|
||||
let get_mother _conf _base _bsrc _ip = None
|
||||
let get_person _conf _base _bsrc _ip = None
|
||||
let get_father' _conf _base _ip = None
|
||||
let get_mother' _conf _base _ip = None
|
||||
let get_family _conf _base _base_prefix _p _ifam = assert false
|
||||
let get_families _conf _base _ip = []
|
||||
let get_children_of_parents _base _baseprefix _ifam _ifath _imoth = []
|
||||
let get_children _base _baseprefix _ifam _ifath _imoth = []
|
||||
let get_children' _base _baseprefix _ifam _ifath _imoth = []
|
||||
let has_children _conf _base _p _fam = false
|
||||
let has_family_correspondance _baseprefix _ip = false
|
||||
let has_parents_link _baseprefix _ip = false
|
||||
let has_siblings _baseprefix _ip = false
|
||||
let nb_children _baseprefix _ifam = 0
|
||||
let nb_families _baseprefix _ip = 0
|
||||
end
|
||||
|
||||
let init_cache = ref Default.init_cache
|
||||
let max_ancestor_level = ref Default.max_ancestor_level
|
||||
let max_descendant_level = ref Default.max_descendant_level
|
||||
let tree_generation_list = ref Default.tree_generation_list
|
||||
let get_father = ref Default.get_father
|
||||
let get_mother = ref Default.get_mother
|
||||
let get_person = ref Default.get_person
|
||||
let get_father' = ref Default.get_father'
|
||||
let get_mother' = ref Default.get_mother'
|
||||
let get_family = ref Default.get_family
|
||||
let get_families = ref Default.get_families
|
||||
let get_children_of_parents = ref Default.get_children_of_parents
|
||||
let has_children = ref Default.has_children
|
||||
let get_children = ref Default.get_children
|
||||
let get_children' = ref Default.get_children'
|
||||
let has_family_correspondance = ref Default.has_family_correspondance
|
||||
let has_parents_link = ref Default.has_parents_link
|
||||
let has_siblings = ref Default.has_siblings
|
||||
let nb_children = ref Default.nb_children
|
||||
let nb_families = ref Default.nb_families
|
||||
126
lib/GWPARAM_ITL.mli
Normal file
126
lib/GWPARAM_ITL.mli
Normal file
@@ -0,0 +1,126 @@
|
||||
val init_cache :
|
||||
(Config.config -> Gwdb.base -> Gwdb.iper -> int -> int -> int -> unit) ref
|
||||
(** [init_cache conf base ip nb_asc from_gen_desc nb_desc] *)
|
||||
|
||||
val max_ancestor_level :
|
||||
(Config.config -> Gwdb.base -> Gwdb.iper -> string -> int -> int -> int) ref
|
||||
|
||||
val max_descendant_level :
|
||||
(Config.config -> Gwdb.base -> Gwdb.iper -> int -> int) ref
|
||||
|
||||
val tree_generation_list :
|
||||
(Config.config ->
|
||||
Gwdb.base ->
|
||||
string ->
|
||||
Gwdb.person ->
|
||||
(Gwdb.person * Gwdb.ifam * string) option
|
||||
* (Gwdb.person * Gwdb.ifam * string) option)
|
||||
ref
|
||||
|
||||
val get_father :
|
||||
(Config.config ->
|
||||
Gwdb.base ->
|
||||
string ->
|
||||
Gwdb.iper ->
|
||||
((Gwdb.person * bool) * string) option)
|
||||
ref
|
||||
|
||||
val get_mother :
|
||||
(Config.config ->
|
||||
Gwdb.base ->
|
||||
string ->
|
||||
Gwdb.iper ->
|
||||
((Gwdb.person * bool) * string) option)
|
||||
ref
|
||||
|
||||
val get_person :
|
||||
(Config.config ->
|
||||
Gwdb.base ->
|
||||
string ->
|
||||
Gwdb.iper ->
|
||||
((Gwdb.person * bool) * string) option)
|
||||
ref
|
||||
|
||||
val get_father' :
|
||||
(Config.config ->
|
||||
Gwdb.base ->
|
||||
Gwdb.iper ->
|
||||
(string
|
||||
* (Gwdb.person * bool)
|
||||
* Gwdb.ifam
|
||||
* Gwdb.family
|
||||
* (Gwdb.iper * Gwdb.iper * Gwdb.iper))
|
||||
option)
|
||||
ref
|
||||
|
||||
val get_mother' :
|
||||
(Config.config ->
|
||||
Gwdb.base ->
|
||||
Gwdb.iper ->
|
||||
(string
|
||||
* (Gwdb.person * bool)
|
||||
* Gwdb.ifam
|
||||
* Gwdb.family
|
||||
* (Gwdb.iper * Gwdb.iper * Gwdb.iper))
|
||||
option)
|
||||
ref
|
||||
|
||||
val get_family :
|
||||
(Config.config ->
|
||||
Gwdb.base ->
|
||||
string ->
|
||||
Gwdb.person ->
|
||||
Gwdb.ifam ->
|
||||
(Gwdb.family * (Gwdb.iper * Gwdb.iper * Gwdb.iper) * bool) option)
|
||||
ref
|
||||
|
||||
val get_families :
|
||||
(Config.config ->
|
||||
Gwdb.base ->
|
||||
Gwdb.person ->
|
||||
(Gwdb.ifam
|
||||
* Gwdb.family
|
||||
* (Gwdb.iper * Gwdb.iper * Gwdb.person)
|
||||
* string
|
||||
* bool)
|
||||
list)
|
||||
ref
|
||||
|
||||
val get_children_of_parents :
|
||||
(Gwdb.base ->
|
||||
string ->
|
||||
Gwdb.ifam ->
|
||||
Gwdb.iper ->
|
||||
Gwdb.iper ->
|
||||
(Gwdb.person * string) list)
|
||||
ref
|
||||
|
||||
val get_children :
|
||||
(Gwdb.base ->
|
||||
string ->
|
||||
Gwdb.ifam ->
|
||||
Gwdb.iper ->
|
||||
Gwdb.iper ->
|
||||
((Gwdb.person * bool) * string) list)
|
||||
ref
|
||||
|
||||
val get_children' :
|
||||
(Config.config ->
|
||||
Gwdb.base ->
|
||||
Gwdb.iper ->
|
||||
Gwdb.family ->
|
||||
Gwdb.iper ->
|
||||
(string
|
||||
* (Gwdb.iper * Gwdb.iper * Gwdb.iper)
|
||||
* ((Gwdb.person * bool) * string * bool) list)
|
||||
list)
|
||||
ref
|
||||
|
||||
val has_children :
|
||||
(Config.config -> Gwdb.base -> Gwdb.person -> Gwdb.family -> bool) ref
|
||||
|
||||
val has_family_correspondance : (string -> Gwdb.iper -> bool) ref
|
||||
val has_parents_link : (string -> Gwdb.iper -> bool) ref
|
||||
val has_siblings : (string -> Gwdb.iper -> bool) ref
|
||||
val nb_children : (string -> Gwdb.ifam -> int) ref
|
||||
val nb_families : (string -> Gwdb.iper -> int) ref
|
||||
620
lib/advSearchOk.ml
Normal file
620
lib/advSearchOk.ml
Normal file
@@ -0,0 +1,620 @@
|
||||
(* Copyright (c) 1998-2007 INRIA *)
|
||||
|
||||
open Config
|
||||
open Def
|
||||
open Gwdb
|
||||
open Util
|
||||
|
||||
let get_number var key env = p_getint env (var ^ "_" ^ key)
|
||||
|
||||
let reconstitute_date_dmy conf var =
|
||||
match get_number var "yyyy" conf.env with
|
||||
| Some y -> (
|
||||
match get_number var "mm" conf.env with
|
||||
| Some m -> (
|
||||
match get_number var "dd" conf.env with
|
||||
| Some d ->
|
||||
if d >= 1 && d <= 31 && m >= 1 && m <= 12 then
|
||||
Some { day = d; month = m; year = y; prec = Sure; delta = 0 }
|
||||
else None
|
||||
| None ->
|
||||
if m >= 1 && m <= 12 then
|
||||
Some { day = 0; month = m; year = y; prec = Sure; delta = 0 }
|
||||
else None)
|
||||
| None -> Some { day = 0; month = 0; year = y; prec = Sure; delta = 0 })
|
||||
| None -> None
|
||||
|
||||
let reconstitute_date conf var =
|
||||
match reconstitute_date_dmy conf var with
|
||||
| Some d -> Some (Dgreg (d, Dgregorian))
|
||||
| None -> None
|
||||
|
||||
let rec skip_spaces x i =
|
||||
if i = String.length x then i
|
||||
else if String.unsafe_get x i = ' ' then skip_spaces x (i + 1)
|
||||
else i
|
||||
|
||||
let rec skip_no_spaces x i =
|
||||
if i = String.length x then i
|
||||
else if String.unsafe_get x i != ' ' then skip_no_spaces x (i + 1)
|
||||
else i
|
||||
|
||||
let string_incl x y =
|
||||
let rec loop j_ini =
|
||||
if j_ini = String.length y then false
|
||||
else
|
||||
let rec loop1 i j =
|
||||
if i = String.length x then
|
||||
if j = String.length y then true
|
||||
else String.unsafe_get y j = ' ' || String.unsafe_get y (j - 1) = ' '
|
||||
else if
|
||||
j < String.length y && String.unsafe_get x i = String.unsafe_get y j
|
||||
then loop1 (i + 1) (j + 1)
|
||||
else loop (skip_spaces y (skip_no_spaces y j_ini))
|
||||
in
|
||||
loop1 0 j_ini
|
||||
in
|
||||
loop 0
|
||||
|
||||
let abbrev_lower x = Name.abbrev (Name.lower x)
|
||||
|
||||
(* Get the field name of an event criteria depending of the search type. *)
|
||||
let get_event_field_name gets event_criteria event_name search_type =
|
||||
if search_type <> "OR" then event_name ^ "_" ^ event_criteria
|
||||
else if "on" = gets ("event_" ^ event_name) then event_criteria
|
||||
else ""
|
||||
|
||||
(*
|
||||
Search for other persons in the base matching with the provided infos.
|
||||
|
||||
On search semantic:
|
||||
|
||||
Search can be set to be exact on the first name and/or the surname,
|
||||
if no first name or surname is provided then the search ignores the
|
||||
parameter in both the exact and the loose case.
|
||||
|
||||
- When search is loose it is only necessary for each name atom (name atoms
|
||||
for "Jean-Pierre" are: [Jean] [Pierre]) to be found at least once in another
|
||||
person's name atoms in the base.
|
||||
|
||||
- When search is exact, it is necessary for each atom to be found exactly the
|
||||
number of times it occurs in the given name but order is not considered for
|
||||
a person from the base to match. (ie. "Pierre-Jean de Bourbon de Vallois" matches
|
||||
with "Jean Pierre de Vallois de Bourbon" but not with "Jean de Bourbon")
|
||||
*)
|
||||
let advanced_search conf base max_answers =
|
||||
let hs = Hashtbl.create 73 in
|
||||
let hss = Hashtbl.create 73 in
|
||||
let hd = Hashtbl.create 73 in
|
||||
let getd x =
|
||||
try Hashtbl.find hd x
|
||||
with Not_found ->
|
||||
let v =
|
||||
(reconstitute_date conf (x ^ "1"), reconstitute_date conf (x ^ "2"))
|
||||
in
|
||||
Hashtbl.add hd x v;
|
||||
v
|
||||
in
|
||||
let gets x =
|
||||
try Hashtbl.find hs x
|
||||
with Not_found ->
|
||||
let v = match p_getenv conf.env x with Some v -> v | None -> "" in
|
||||
Hashtbl.add hs x v;
|
||||
v
|
||||
in
|
||||
let getss x =
|
||||
let y = gets x in
|
||||
if y <> "" then [ y ]
|
||||
else
|
||||
match Hashtbl.find_opt hss @@ x with
|
||||
| Some v -> v
|
||||
| None ->
|
||||
let rec loop acc i =
|
||||
let k = x ^ "_" ^ string_of_int i in
|
||||
match p_getenv conf.env k with
|
||||
| Some v -> loop (if v <> "" then v :: acc else acc) (i + 1)
|
||||
| None -> acc
|
||||
in
|
||||
let v = loop [] 1 in
|
||||
Hashtbl.add hss x v;
|
||||
v
|
||||
in
|
||||
let fn_list =
|
||||
List.map
|
||||
(fun s -> List.map Name.lower @@ Name.split_fname s)
|
||||
(getss "first_name")
|
||||
in
|
||||
let sn_list =
|
||||
List.map
|
||||
(fun s -> List.map Name.lower @@ Name.split_sname s)
|
||||
(getss "surname")
|
||||
in
|
||||
(* Search type can be AND or OR. *)
|
||||
let search_type = gets "search_type" in
|
||||
(* Return empty_field_value if the field is empty. Apply function cmp to the field value. Also check the authorization. *)
|
||||
let apply_to_field_value_raw p x cmp empty_default_value =
|
||||
let y = gets x in
|
||||
if y = "" then empty_default_value
|
||||
else if authorized_age conf base p then cmp y
|
||||
else false
|
||||
in
|
||||
let apply_to_field_value p x get cmp empty_default_value =
|
||||
let y = gets x in
|
||||
if y = "" then empty_default_value
|
||||
else if authorized_age conf base p then
|
||||
cmp (abbrev_lower y) (abbrev_lower @@ sou base @@ get p)
|
||||
else false
|
||||
in
|
||||
let do_compare p y get cmp =
|
||||
let s = abbrev_lower @@ get p in
|
||||
List.exists (fun s' -> cmp (abbrev_lower s') s) y
|
||||
in
|
||||
let apply_to_field_values_raw p x get cmp empty_default_value =
|
||||
let y = getss x in
|
||||
if y = [] then empty_default_value
|
||||
else if authorized_age conf base p then do_compare p y get cmp
|
||||
else false
|
||||
in
|
||||
let apply_to_field_values p x get cmp empty_default_value =
|
||||
let get p = sou base @@ get p in
|
||||
apply_to_field_values_raw p x get cmp empty_default_value
|
||||
in
|
||||
(* Check if the date matches with the person event. *)
|
||||
let match_date p x df empty_default_value =
|
||||
let d1, d2 = getd x in
|
||||
authorized_age conf base p
|
||||
&&
|
||||
match (d1, d2) with
|
||||
| Some (Dgreg (d1, _)), Some (Dgreg (d2, _)) -> (
|
||||
match df () with
|
||||
| Some (Dgreg (d, _)) ->
|
||||
Date.compare_dmy d d1 >= 0 && Date.compare_dmy d d2 <= 0
|
||||
| _ -> false)
|
||||
| Some (Dgreg (d1, _)), _ -> (
|
||||
match df () with
|
||||
| Some (Dgreg (d, _)) -> Date.compare_dmy d d1 >= 0
|
||||
| _ -> false)
|
||||
| _, Some (Dgreg (d2, _)) -> (
|
||||
match df () with
|
||||
| Some (Dgreg (d, _)) -> Date.compare_dmy d d2 <= 0
|
||||
| _ -> false)
|
||||
| _ -> empty_default_value
|
||||
in
|
||||
let match_sex p empty_default_value =
|
||||
apply_to_field_value_raw p "sex"
|
||||
(function
|
||||
| "M" -> get_sex p = Male | "F" -> get_sex p = Female | _ -> true)
|
||||
empty_default_value
|
||||
in
|
||||
let bapt_date_field_name =
|
||||
get_event_field_name gets "date" "bapt" search_type
|
||||
in
|
||||
let birth_date_field_name =
|
||||
get_event_field_name gets "date" "birth" search_type
|
||||
in
|
||||
let death_date_field_name =
|
||||
get_event_field_name gets "date" "death" search_type
|
||||
in
|
||||
let burial_date_field_name =
|
||||
get_event_field_name gets "date" "burial" search_type
|
||||
in
|
||||
let marriage_date_field_name =
|
||||
get_event_field_name gets "date" "marriage" search_type
|
||||
in
|
||||
let bapt_place_field_name =
|
||||
get_event_field_name gets "place" "bapt" search_type
|
||||
in
|
||||
let birth_place_field_name =
|
||||
get_event_field_name gets "place" "birth" search_type
|
||||
in
|
||||
let death_place_field_name =
|
||||
get_event_field_name gets "place" "death" search_type
|
||||
in
|
||||
let burial_place_field_name =
|
||||
get_event_field_name gets "place" "burial" search_type
|
||||
in
|
||||
let marriage_place_field_name =
|
||||
get_event_field_name gets "place" "marriage" search_type
|
||||
in
|
||||
let match_baptism_date p empty_default_value =
|
||||
match_date p bapt_date_field_name
|
||||
(fun () -> Date.od_of_cdate (get_baptism p))
|
||||
empty_default_value
|
||||
in
|
||||
let match_birth_date p empty_default_value =
|
||||
match_date p birth_date_field_name
|
||||
(fun () -> Date.od_of_cdate (get_birth p))
|
||||
empty_default_value
|
||||
in
|
||||
let match_death_date p empty_default_value =
|
||||
match_date p death_date_field_name
|
||||
(fun () -> Date.date_of_death (get_death p))
|
||||
empty_default_value
|
||||
in
|
||||
let match_burial_date p empty_default_value =
|
||||
match_date p burial_date_field_name
|
||||
(fun () ->
|
||||
(* TODO Date.cdate_of_burial *)
|
||||
match get_burial p with
|
||||
| Buried cod | Cremated cod -> Date.od_of_cdate cod
|
||||
| UnknownBurial -> None)
|
||||
empty_default_value
|
||||
in
|
||||
let cmp_place = if "on" = gets "exact_place" then ( = ) else string_incl in
|
||||
let match_baptism_place p empty_default_value =
|
||||
apply_to_field_values p bapt_place_field_name get_baptism_place cmp_place
|
||||
empty_default_value
|
||||
in
|
||||
let match_birth_place p empty_default_value =
|
||||
apply_to_field_values p birth_place_field_name get_birth_place cmp_place
|
||||
empty_default_value
|
||||
in
|
||||
let match_death_place p empty_default_value =
|
||||
apply_to_field_values p death_place_field_name get_death_place cmp_place
|
||||
empty_default_value
|
||||
in
|
||||
let match_burial_place p empty_default_value =
|
||||
apply_to_field_values p burial_place_field_name get_burial_place cmp_place
|
||||
empty_default_value
|
||||
in
|
||||
let match_occupation p empty_default_value =
|
||||
apply_to_field_value p "occu" get_occupation string_incl empty_default_value
|
||||
in
|
||||
let match_name search_list exact : string list -> bool =
|
||||
let eq : string list -> string list -> bool =
|
||||
if exact then fun x search ->
|
||||
List.sort compare search = List.sort compare x
|
||||
else fun x search -> List.for_all (fun s -> List.mem s x) search
|
||||
in
|
||||
fun x -> List.exists (eq x) search_list
|
||||
in
|
||||
let match_first_name =
|
||||
if fn_list = [] then fun _ -> true
|
||||
else
|
||||
let eq = match_name fn_list (gets "exact_first_name" = "on") in
|
||||
fun p ->
|
||||
eq
|
||||
(List.map Name.lower @@ Name.split_fname @@ sou base
|
||||
@@ get_first_name p)
|
||||
in
|
||||
let match_surname =
|
||||
if sn_list = [] then fun _ -> true
|
||||
else
|
||||
let eq = match_name sn_list (gets "exact_surname" = "on") in
|
||||
fun p ->
|
||||
eq (List.map Name.lower @@ Name.split_sname @@ sou base @@ get_surname p)
|
||||
in
|
||||
let match_married p empty_default_value =
|
||||
apply_to_field_value_raw p "married"
|
||||
(function
|
||||
| "Y" -> get_family p <> [||] | "N" -> get_family p = [||] | _ -> true)
|
||||
empty_default_value
|
||||
in
|
||||
let match_marriage p x y empty_default_value =
|
||||
let d1, d2 = getd x in
|
||||
let y = getss y in
|
||||
let test_date_place df =
|
||||
Array.exists
|
||||
(fun ifam ->
|
||||
let fam = foi base ifam in
|
||||
let sp = poi base @@ Gutil.spouse (get_iper p) fam in
|
||||
if authorized_age conf base sp then
|
||||
df fam
|
||||
&& (y = []
|
||||
|| do_compare fam y
|
||||
(fun f -> sou base @@ get_marriage_place f)
|
||||
cmp_place)
|
||||
else false)
|
||||
(get_family p)
|
||||
in
|
||||
match (d1, d2) with
|
||||
| Some d1, Some d2 ->
|
||||
test_date_place (fun fam ->
|
||||
match Date.od_of_cdate (get_marriage fam) with
|
||||
| Some (Dgreg (_, _) as d) ->
|
||||
if Date.compare_date d d1 < 0 then false
|
||||
else if Date.compare_date d2 d < 0 then false
|
||||
else true
|
||||
| _ -> false)
|
||||
| Some d1, _ ->
|
||||
test_date_place (fun fam ->
|
||||
match Date.od_of_cdate (get_marriage fam) with
|
||||
| Some (Dgreg (_, _) as d) when authorized_age conf base p ->
|
||||
if Date.compare_date d d1 < 0 then false else true
|
||||
| _ -> false)
|
||||
| _, Some d2 ->
|
||||
test_date_place (fun fam ->
|
||||
match Date.od_of_cdate (get_marriage fam) with
|
||||
| Some (Dgreg (_, _) as d) when authorized_age conf base p ->
|
||||
if Date.compare_date d d2 > 0 then false else true
|
||||
| _ -> false)
|
||||
| _ ->
|
||||
if y = [] then empty_default_value else test_date_place (fun _ -> true)
|
||||
in
|
||||
(* Check the civil status. The test is the same for an AND or a OR search request. *)
|
||||
let match_civil_status ~skip_fname ~skip_sname p =
|
||||
match_sex p true
|
||||
&& (skip_fname || match_first_name p)
|
||||
&& (skip_sname || match_surname p)
|
||||
&& match_married p true && match_occupation p true
|
||||
in
|
||||
let match_person ?(skip_fname = false) ?(skip_sname = false)
|
||||
((list, len) as acc) p search_type =
|
||||
if search_type <> "OR" then
|
||||
if
|
||||
match_civil_status ~skip_fname ~skip_sname p
|
||||
&& match_baptism_date p true && match_baptism_place p true
|
||||
&& match_birth_date p true && match_birth_place p true
|
||||
&& match_burial_date p true && match_burial_place p true
|
||||
&& match_death_date p true && match_death_place p true
|
||||
&& match_marriage p marriage_date_field_name marriage_place_field_name
|
||||
true
|
||||
then (p :: list, len + 1)
|
||||
else acc
|
||||
else if
|
||||
match_civil_status ~skip_fname ~skip_sname p
|
||||
&& (getss "place" = []
|
||||
&& gets "date2_yyyy" = ""
|
||||
&& gets "date1_yyyy" = ""
|
||||
|| (match_baptism_date p false || match_baptism_place p false)
|
||||
&& match_baptism_date p true && match_baptism_place p true
|
||||
|| (match_birth_date p false || match_birth_place p false)
|
||||
&& match_birth_date p true && match_birth_place p true
|
||||
|| (match_burial_date p false || match_burial_place p false)
|
||||
&& match_burial_date p true && match_burial_place p true
|
||||
|| (match_death_date p false || match_death_place p false)
|
||||
&& match_death_date p true && match_death_place p true
|
||||
|| match_marriage p marriage_date_field_name marriage_place_field_name
|
||||
false)
|
||||
then (p :: list, len + 1)
|
||||
else acc
|
||||
in
|
||||
let list, len =
|
||||
if "on" = gets "sosa_filter" then
|
||||
match Util.find_sosa_ref conf base with
|
||||
| Some sosa_ref ->
|
||||
let rec loop p (set, acc) =
|
||||
if not (IperSet.mem (get_iper p) set) then
|
||||
let set = IperSet.add (get_iper p) set in
|
||||
let acc = match_person acc p search_type in
|
||||
match get_parents p with
|
||||
| Some ifam ->
|
||||
let fam = foi base ifam in
|
||||
let set, acc =
|
||||
loop (pget conf base @@ get_mother fam) (set, acc)
|
||||
in
|
||||
loop (pget conf base @@ get_father fam) (set, acc)
|
||||
| None -> (set, acc)
|
||||
else (set, acc)
|
||||
in
|
||||
loop (pget conf base @@ get_iper sosa_ref) (IperSet.empty, ([], 0))
|
||||
|> snd
|
||||
| None -> ([], 0)
|
||||
else if fn_list <> [] || sn_list <> [] then
|
||||
let list_aux strings_of persons_of split n_list exact =
|
||||
List.map
|
||||
(List.map (fun x ->
|
||||
let eq = match_name n_list exact in
|
||||
let istrs = strings_of base x in
|
||||
List.fold_left
|
||||
(fun acc istr ->
|
||||
let str = Mutil.nominative (sou base istr) in
|
||||
if eq (List.map Name.lower @@ split str) then istr :: acc
|
||||
else acc)
|
||||
[] istrs))
|
||||
n_list
|
||||
|> List.flatten |> List.flatten |> List.sort_uniq compare
|
||||
|> List.map (spi_find @@ persons_of base)
|
||||
|> List.flatten |> List.sort_uniq compare
|
||||
in
|
||||
let skip_fname, skip_sname, list =
|
||||
if sn_list <> [] then
|
||||
( false,
|
||||
true,
|
||||
list_aux Gwdb.base_strings_of_surname Gwdb.persons_of_surname
|
||||
Name.split_sname sn_list
|
||||
(gets "exact_surname" = "on") )
|
||||
else
|
||||
( true,
|
||||
false,
|
||||
list_aux Gwdb.base_strings_of_first_name Gwdb.persons_of_first_name
|
||||
Name.split_fname fn_list
|
||||
(gets "exact_first_name" = "on") )
|
||||
in
|
||||
let rec loop ((_, len) as acc) = function
|
||||
| [] -> acc
|
||||
| _ when len > max_answers -> acc
|
||||
| ip :: l ->
|
||||
loop
|
||||
(match_person ~skip_fname ~skip_sname acc (pget conf base ip)
|
||||
search_type)
|
||||
l
|
||||
in
|
||||
loop ([], 0) list
|
||||
else
|
||||
Gwdb.Collection.fold_until
|
||||
(fun (_, len) -> len <= max_answers)
|
||||
(fun acc i -> match_person acc (pget conf base i) search_type)
|
||||
([], 0) (Gwdb.ipers base)
|
||||
in
|
||||
(List.rev list, len)
|
||||
|
||||
let searching_fields conf base =
|
||||
let test_date x =
|
||||
reconstitute_date conf (x ^ "1") <> None
|
||||
|| reconstitute_date conf (x ^ "2") <> None
|
||||
in
|
||||
let gets x =
|
||||
match p_getenv conf.env x with
|
||||
| Some v when v <> "" -> v
|
||||
| _ ->
|
||||
let rec loop acc i =
|
||||
let k = x ^ "_" ^ string_of_int i in
|
||||
match p_getenv conf.env k with
|
||||
| Some v ->
|
||||
loop
|
||||
(if acc = "" then v
|
||||
else if v = "" then acc
|
||||
else acc ^ " / " ^ v)
|
||||
(i + 1)
|
||||
| None -> acc
|
||||
in
|
||||
loop "" 1
|
||||
in
|
||||
let test_string x = gets x <> "" in
|
||||
let getd x =
|
||||
(reconstitute_date conf (x ^ "1"), reconstitute_date conf (x ^ "2"))
|
||||
in
|
||||
let sex = match gets "sex" with "M" -> 0 | "F" -> 1 | _ -> 2 in
|
||||
(* Fonction pour tester un simple champ texte (e.g: first_name). *)
|
||||
let string_field x (search : Adef.safe_string) =
|
||||
if test_string x then
|
||||
search ^^^ " " ^<^ (escape_html (gets x) :> Adef.safe_string)
|
||||
else search
|
||||
in
|
||||
(* Returns the place and date request. (e.g.: ...in Paris between 1800 and 1900) *)
|
||||
let get_place_date_request place_prefix_field_name date_prefix_field_name
|
||||
search =
|
||||
let search =
|
||||
match getd date_prefix_field_name with
|
||||
| Some d1, Some d2 ->
|
||||
search ^^^ " "
|
||||
^<^ transl conf "between (date)"
|
||||
^<^ DateDisplay.string_of_date conf d1
|
||||
^^^ " " ^<^ transl conf "and" ^<^ " "
|
||||
^<^ DateDisplay.string_of_date conf d2
|
||||
| Some d1, _ ->
|
||||
search ^^^ " " ^<^ transl conf "after (date)" ^<^ " "
|
||||
^<^ DateDisplay.string_of_date conf d1
|
||||
| _, Some d2 ->
|
||||
search ^^^ " "
|
||||
^<^ transl conf "before (date)"
|
||||
^<^ " "
|
||||
^<^ DateDisplay.string_of_date conf d2
|
||||
| _ -> search
|
||||
in
|
||||
if test_string place_prefix_field_name then
|
||||
search ^^^ " " ^<^ transl conf "in (place)" ^<^ " "
|
||||
^<^ (escape_html (gets place_prefix_field_name) :> Adef.safe_string)
|
||||
else search
|
||||
in
|
||||
(* Returns the event request. (e.g.: born in...) *)
|
||||
let get_event_field_request place_prefix_field_name date_prefix_field_name
|
||||
event_name search search_type =
|
||||
(* Separator character depends on search type operator, a comma for AND search, a slash for OR search. *)
|
||||
let sep : Adef.safe_string =
|
||||
if (search : Adef.safe_string :> string) <> "" then
|
||||
if search_type <> "OR" then Adef.safe ", " else Adef.safe " / "
|
||||
else Adef.safe ""
|
||||
in
|
||||
let search =
|
||||
if test_string place_prefix_field_name || test_date date_prefix_field_name
|
||||
then search ^^^ sep ^>^ transl_nth conf event_name sex
|
||||
else search
|
||||
in
|
||||
(* The place and date have to be shown after each event only for the AND request. *)
|
||||
if search_type <> "OR" then
|
||||
get_place_date_request place_prefix_field_name date_prefix_field_name
|
||||
search
|
||||
else search
|
||||
in
|
||||
let sosa_field search =
|
||||
if gets "sosa_filter" <> "" then
|
||||
match Util.find_sosa_ref conf base with
|
||||
| Some p ->
|
||||
let s =
|
||||
Adef.safe
|
||||
@@ Printf.sprintf
|
||||
(ftransl conf "direct ancestor of %s")
|
||||
(Util.gen_person_text conf base p : Adef.safe_string :> string)
|
||||
in
|
||||
if (search : Adef.safe_string :> string) = "" then s
|
||||
else if (s :> string) = "" then search
|
||||
else search ^^^ ", " ^<^ s
|
||||
| None -> search
|
||||
else search
|
||||
in
|
||||
(* Search type can be AND or OR. *)
|
||||
let search_type = gets "search_type" in
|
||||
let bapt_date_field_name =
|
||||
get_event_field_name gets "date" "bapt" search_type
|
||||
in
|
||||
let birth_date_field_name =
|
||||
get_event_field_name gets "date" "birth" search_type
|
||||
in
|
||||
let death_date_field_name =
|
||||
get_event_field_name gets "date" "death" search_type
|
||||
in
|
||||
let burial_date_field_name =
|
||||
get_event_field_name gets "date" "burial" search_type
|
||||
in
|
||||
let marriage_date_field_name =
|
||||
get_event_field_name gets "date" "marriage" search_type
|
||||
in
|
||||
let bapt_place_field_name =
|
||||
get_event_field_name gets "place" "bapt" search_type
|
||||
in
|
||||
let birth_place_field_name =
|
||||
get_event_field_name gets "place" "birth" search_type
|
||||
in
|
||||
let death_place_field_name =
|
||||
get_event_field_name gets "place" "death" search_type
|
||||
in
|
||||
let burial_place_field_name =
|
||||
get_event_field_name gets "place" "burial" search_type
|
||||
in
|
||||
let marriage_place_field_name =
|
||||
get_event_field_name gets "place" "marriage" search_type
|
||||
in
|
||||
let search = Adef.safe "" in
|
||||
let search = string_field "first_name" search in
|
||||
let search = string_field "surname" search in
|
||||
let search = sosa_field search in
|
||||
let event_search = Adef.safe "" in
|
||||
let event_search =
|
||||
get_event_field_request birth_place_field_name birth_date_field_name "born"
|
||||
event_search search_type
|
||||
in
|
||||
let event_search =
|
||||
get_event_field_request bapt_place_field_name bapt_date_field_name
|
||||
"baptized" event_search search_type
|
||||
in
|
||||
let event_search =
|
||||
get_event_field_request marriage_place_field_name marriage_date_field_name
|
||||
"married" event_search search_type
|
||||
in
|
||||
let event_search =
|
||||
get_event_field_request death_place_field_name death_date_field_name "died"
|
||||
event_search search_type
|
||||
in
|
||||
let event_search =
|
||||
get_event_field_request burial_place_field_name burial_date_field_name
|
||||
"buried" event_search search_type
|
||||
in
|
||||
let search =
|
||||
if (search :> string) = "" then event_search
|
||||
else if (event_search :> string) = "" then search
|
||||
else search ^^^ ", " ^<^ event_search
|
||||
in
|
||||
(* Adding the place and date at the end for the OR request. *)
|
||||
let search =
|
||||
if
|
||||
search_type = "OR"
|
||||
&& (gets "place" != ""
|
||||
|| gets "date2_yyyy" != ""
|
||||
|| gets "date1_yyyy" != "")
|
||||
then get_place_date_request "place" "date" search
|
||||
else search
|
||||
in
|
||||
let search =
|
||||
if not (test_string marriage_place_field_name || test_date "marriage") then
|
||||
let sep = if (search :> string) <> "" then ", " else "" in
|
||||
if gets "married" = "Y" then
|
||||
search ^>^ sep ^ transl conf "having a family"
|
||||
else if gets "married" = "N" then
|
||||
search ^>^ sep ^ transl conf "having no family"
|
||||
else search
|
||||
else search
|
||||
in
|
||||
let sep = Adef.safe (if (search :> string) <> "" then "," else "") in
|
||||
string_field "occu" (search ^^^ sep)
|
||||
9
lib/advSearchOk.mli
Normal file
9
lib/advSearchOk.mli
Normal file
@@ -0,0 +1,9 @@
|
||||
val advanced_search :
|
||||
Config.config -> Gwdb.base -> int -> Gwdb.person list * int
|
||||
(** [advanced_search conf base max_answers] extracts advaced request fields from environement [conf.env] and
|
||||
returns at most [max_answers] persons from the [base] that match conditions described by those fields. Seond result
|
||||
represents real number of matches (if less then [max_answers]). *)
|
||||
|
||||
val searching_fields : Config.config -> Gwdb.base -> Adef.safe_string
|
||||
(** Returns a description string for the current advanced search results in the correct language.
|
||||
e.g. "Search all Pierre, born in Paris, died in Paris" *)
|
||||
46
lib/advSearchOkDisplay.ml
Normal file
46
lib/advSearchOkDisplay.ml
Normal file
@@ -0,0 +1,46 @@
|
||||
(* Copyright (c) 1998-2007 INRIA *)
|
||||
|
||||
open Config
|
||||
open Util
|
||||
|
||||
let print_result conf base max_answers (list, len) =
|
||||
let list =
|
||||
if len > max_answers then Util.reduce_list max_answers list else list
|
||||
in
|
||||
if len = 0 then (
|
||||
Output.print_sstring conf (Utf8.capitalize_fst (transl conf "no match"));
|
||||
Output.print_sstring conf " ")
|
||||
else
|
||||
let () = SosaCache.build_sosa_ht conf base in
|
||||
Output.print_sstring conf "<ul>\n";
|
||||
List.iter
|
||||
(fun p ->
|
||||
Output.print_sstring conf "<li>";
|
||||
SosaCache.print_sosa conf base p true;
|
||||
Output.print_sstring conf " ";
|
||||
Output.print_string conf (referenced_person_text conf base p);
|
||||
Output.print_string conf (DateDisplay.short_dates_text conf base p);
|
||||
Output.print_sstring conf "<em>";
|
||||
specify_homonymous conf base p false;
|
||||
Output.print_sstring conf "</em>")
|
||||
list;
|
||||
if len > max_answers then Output.print_sstring conf "<li>...</li>";
|
||||
Output.print_sstring conf "</ul>"
|
||||
|
||||
let print conf base =
|
||||
let title _ =
|
||||
transl_nth conf "advanced request" 0
|
||||
|> Utf8.capitalize_fst |> Output.print_sstring conf
|
||||
in
|
||||
let max_answers =
|
||||
match p_getint conf.env "max" with Some n -> n | None -> 100
|
||||
in
|
||||
Hutil.header conf title;
|
||||
Output.print_sstring conf "<p>";
|
||||
Output.print_sstring conf (Utf8.capitalize_fst (transl conf "searching all"));
|
||||
Output.print_sstring conf " ";
|
||||
Output.print_string conf (AdvSearchOk.searching_fields conf base);
|
||||
Output.print_sstring conf ".</p>";
|
||||
let list = AdvSearchOk.advanced_search conf base max_answers in
|
||||
print_result conf base max_answers list;
|
||||
Hutil.trailer conf
|
||||
2
lib/advSearchOkDisplay.mli
Normal file
2
lib/advSearchOkDisplay.mli
Normal file
@@ -0,0 +1,2 @@
|
||||
val print : Config.config -> Gwdb.base -> unit
|
||||
(** Displays the results of an advanced search *)
|
||||
153
lib/alln.ml
Normal file
153
lib/alln.ml
Normal file
@@ -0,0 +1,153 @@
|
||||
(* Copyright (c) 1998-2007 INRIA *)
|
||||
|
||||
open Config
|
||||
open Gwdb
|
||||
open Util
|
||||
|
||||
let default_max_cnt = 2000
|
||||
|
||||
(* selection *)
|
||||
|
||||
type t = Result of (string * string * int) list | Specify of string list
|
||||
|
||||
let first_letters base is_surnames =
|
||||
let iii =
|
||||
if is_surnames then persons_of_surname base else persons_of_first_name base
|
||||
in
|
||||
try
|
||||
let rec loop istr list =
|
||||
let s = Translate.eval (Mutil.nominative (sou base istr)) in
|
||||
let k = Util.name_key base s in
|
||||
let c = Utf8.sub k 0 1 in
|
||||
let list =
|
||||
match list with
|
||||
| hd :: _ -> if hd = c then list else c :: list
|
||||
| [] -> [ c ]
|
||||
in
|
||||
match spi_next iii istr with
|
||||
| istr -> loop istr list
|
||||
| exception Not_found -> list
|
||||
in
|
||||
loop (spi_first iii "") []
|
||||
with Not_found -> []
|
||||
|
||||
let select_names conf base is_surnames ini limit =
|
||||
let inilen = Utf8.length ini + 1 in
|
||||
let cut k = Utf8.sub k 0 (min (Utf8.length k) inilen) in
|
||||
let iii =
|
||||
if is_surnames then persons_of_surname base else persons_of_first_name base
|
||||
in
|
||||
let list, len =
|
||||
let start_k = Mutil.tr '_' ' ' ini in
|
||||
try
|
||||
let istr = spi_first iii start_k in
|
||||
let rec loop istr len list =
|
||||
let s = Translate.eval (Mutil.nominative (sou base istr)) in
|
||||
let k = Util.name_key base s in
|
||||
if Mutil.start_with_wildcard ini 0 k then
|
||||
let list, len =
|
||||
if s <> "?" then
|
||||
let ips = spi_find iii istr in
|
||||
let cnt =
|
||||
(* Optimization:
|
||||
* In the case of [Specify _]:
|
||||
* [cnt] is not used except for zero equality test
|
||||
* so we can use List.exists in order to avoid useless operations *)
|
||||
match list with
|
||||
| Specify _ ->
|
||||
if ips = [] then 0
|
||||
else if conf.use_restrict then
|
||||
if
|
||||
List.exists
|
||||
(fun i -> not @@ is_restricted conf base i)
|
||||
ips
|
||||
then 1
|
||||
else 0
|
||||
else if conf.hide_names then
|
||||
if
|
||||
List.exists
|
||||
(fun i -> Util.authorized_age conf base (poi base i))
|
||||
ips
|
||||
then 1
|
||||
else 0
|
||||
else 1
|
||||
| Result _ ->
|
||||
if conf.use_restrict then
|
||||
List.fold_left
|
||||
(fun acc i ->
|
||||
if is_restricted conf base i then acc else acc + 1)
|
||||
0 ips
|
||||
else if conf.hide_names then
|
||||
List.fold_left
|
||||
(fun acc i ->
|
||||
if Util.authorized_age conf base (poi base i) then
|
||||
acc + 1
|
||||
else acc)
|
||||
0 ips
|
||||
else List.length ips
|
||||
in
|
||||
if cnt = 0 then (list, len)
|
||||
else
|
||||
match list with
|
||||
| Result ((k1, s1, cnt1) :: tl) when k = k1 ->
|
||||
(Result ((k1, s1, cnt1 + cnt) :: tl), len)
|
||||
| Result acc ->
|
||||
if len >= limit then
|
||||
let k = cut k in
|
||||
match
|
||||
List.sort_uniq
|
||||
(fun a b -> compare b a)
|
||||
(List.map (fun (k, _, _) -> cut k) acc)
|
||||
with
|
||||
| hd :: _ as acc when hd = k -> (Specify acc, len + 1)
|
||||
| acc -> (Specify (k :: acc), len + 1)
|
||||
else (Result ((k, s, cnt) :: acc), len + 1)
|
||||
| Specify (k1 :: tl) ->
|
||||
let k = cut k in
|
||||
((if k = k1 then list else Specify (k :: k1 :: tl)), len + 1)
|
||||
| Specify [] -> (Specify [ cut k ], 1)
|
||||
else (list, len)
|
||||
in
|
||||
match spi_next iii istr with
|
||||
| istr -> loop istr len list
|
||||
| exception Not_found -> (list, len)
|
||||
else (list, len)
|
||||
in
|
||||
loop istr 0 (Result [])
|
||||
with Not_found -> (Result [], 0)
|
||||
in
|
||||
let list, len =
|
||||
match list with
|
||||
| Specify _ -> (list, len)
|
||||
| Result acc -> (
|
||||
match p_getint conf.env "atleast" with
|
||||
| None -> (list, len)
|
||||
| Some min ->
|
||||
let acc, len =
|
||||
List.fold_left
|
||||
(fun (list, len) (k, s, cnt) ->
|
||||
if cnt >= min then ((k, s, cnt) :: list, len)
|
||||
else (list, len - 1))
|
||||
([], len) acc
|
||||
in
|
||||
(Result acc, len))
|
||||
in
|
||||
(list, len)
|
||||
|
||||
let ini len k =
|
||||
let ini_k = if Utf8.length k <= len then k else Utf8.sub k 0 len in
|
||||
Mutil.unsafe_tr ' ' '_' ini_k
|
||||
|
||||
let groupby_ini len list =
|
||||
list
|
||||
|> Mutil.groupby
|
||||
~key:(fun (k, _, _) -> ini len k)
|
||||
~value:(fun (_, s, c) -> (s, c))
|
||||
|> List.sort (fun (a, _) (b, _) -> Gutil.alphabetic_order a b)
|
||||
|
||||
let groupby_count = function
|
||||
| Specify _ -> assert false
|
||||
| Result list ->
|
||||
list
|
||||
|> Mutil.groupby ~key:(fun (_, _, c) -> c) ~value:(fun (_, s, _) -> s)
|
||||
|> List.sort (fun (a, _) (b, _) -> compare b a)
|
||||
38
lib/alln.mli
Normal file
38
lib/alln.mli
Normal file
@@ -0,0 +1,38 @@
|
||||
val default_max_cnt : int
|
||||
(** Default number of names that could be printed simultaneously on the page *)
|
||||
|
||||
(** Type that represents result of name selection *)
|
||||
type t =
|
||||
| Result of (string * string * int) list
|
||||
(** Exhaustive result with the list of names
|
||||
(key and printable name) with number of
|
||||
persons that have giving name*)
|
||||
| Specify of string list
|
||||
(** Not exhaustive result that specifies all existing names
|
||||
prefixes (their length depends on initial searched prefix) *)
|
||||
|
||||
val first_letters : Gwdb.base -> bool -> string list
|
||||
(** Returns list of all first name's first letter present in the base (UTF8 encoded).
|
||||
Used for fast access for base's names *)
|
||||
|
||||
val select_names :
|
||||
Config.config -> Gwdb.base -> bool -> string -> int -> t * int
|
||||
(** [select_names conf base is_surnames ini limit]
|
||||
Select up to [limit] first names/surnames starting with [ini].
|
||||
If more values are available, return [Specify] with different
|
||||
possible prefixes with the length at most equal to the length of [ini]+1
|
||||
(for empty [ini] specifies all first letters of existing names).
|
||||
Otherwise, return the list of values [Result] with all first names
|
||||
and number of persons that have giving name. *)
|
||||
|
||||
val ini : int -> string -> string
|
||||
(** Returns prefix of length [len] of UTF8 encoded name *)
|
||||
|
||||
val groupby_ini :
|
||||
int -> (string * 'a * 'b) list -> (string * ('a * 'b) list) list
|
||||
(** [groupby_ini len results] returns alphabeticaly ordered list of grouped by name prefix (with length [len])
|
||||
results. *)
|
||||
|
||||
val groupby_count : t -> (int * string list) list
|
||||
(** Returns ordered (from bigest to smallest) list of grouped by name frequency (number of persons
|
||||
having the name) results. Shouldn't be used when results are represented with [Specify]. *)
|
||||
322
lib/allnDisplay.ml
Normal file
322
lib/allnDisplay.ml
Normal file
@@ -0,0 +1,322 @@
|
||||
open Def
|
||||
open Config
|
||||
open Gwdb
|
||||
open Util
|
||||
|
||||
let default_max_cnt = Alln.default_max_cnt
|
||||
|
||||
(* tools *)
|
||||
|
||||
let particle_at_the_end base is_surnames s =
|
||||
if is_surnames then surname_without_particle base s ^ surname_particle base s
|
||||
else s
|
||||
|
||||
let compare_particle_at_the_end base is_surnames a b =
|
||||
Gutil.alphabetic_order
|
||||
(particle_at_the_end base is_surnames a)
|
||||
(particle_at_the_end base is_surnames b)
|
||||
|
||||
(* print *)
|
||||
|
||||
let print_title conf base is_surnames ini len =
|
||||
if len >= 2 then
|
||||
if is_surnames then
|
||||
Printf.sprintf (fcapitale (ftransl conf "the %d surnames")) len
|
||||
|> Output.print_sstring conf
|
||||
else
|
||||
Printf.sprintf (fcapitale (ftransl conf "the %d first names")) len
|
||||
|> Output.print_sstring conf
|
||||
else if is_surnames then
|
||||
transl_nth conf "surname/surnames" 0
|
||||
|> Utf8.capitalize_fst |> Output.print_sstring conf
|
||||
else
|
||||
transl_nth conf "first name/first names" 0
|
||||
|> Utf8.capitalize_fst |> Output.print_sstring conf;
|
||||
if ini <> "" then (
|
||||
Output.print_sstring conf " ";
|
||||
Output.print_sstring conf (transl conf "starting with");
|
||||
Output.print_sstring conf " ";
|
||||
Output.print_string conf (Util.escape_html ini))
|
||||
else (
|
||||
Output.print_sstring conf " (";
|
||||
Output.print_sstring conf (string_of_int @@ Gwdb.nb_of_real_persons base);
|
||||
Output.print_sstring conf " ";
|
||||
Output.print_sstring conf
|
||||
(Util.translate_eval ("@(c)" ^ transl_nth conf "person/persons" 1));
|
||||
Output.print_sstring conf ")")
|
||||
|
||||
let tr c1 s2 s =
|
||||
let rec loop i len =
|
||||
if i = String.length s then Buff.get len
|
||||
else if String.unsafe_get s i = c1 then loop (i + 1) (Buff.mstore len s2)
|
||||
else loop (i + 1) (Buff.store len (String.unsafe_get s i))
|
||||
in
|
||||
loop 0 0
|
||||
|
||||
let print_alphabetic_big conf base is_surnames ini list len too_big =
|
||||
let title _ = print_title conf base is_surnames ini len in
|
||||
let mode = if is_surnames then Adef.encoded "N" else Adef.encoded "P" in
|
||||
Hutil.header conf title;
|
||||
Output.print_sstring conf {|<p class="search_name">|};
|
||||
List.iter
|
||||
(fun ini_k ->
|
||||
if ini_k = ini then (
|
||||
Output.print_sstring conf {|<a href="|};
|
||||
Output.print_string conf (commd conf);
|
||||
Output.print_sstring conf "m=";
|
||||
Output.print_string conf mode;
|
||||
Output.print_sstring conf "&tri=A&v=";
|
||||
Output.print_string conf (Mutil.encode ini_k);
|
||||
Output.print_sstring conf {|">|})
|
||||
else (
|
||||
Output.print_sstring conf {|<a href="|};
|
||||
Output.print_string conf (commd conf);
|
||||
Output.print_sstring conf "m=";
|
||||
Output.print_string conf mode;
|
||||
Output.print_sstring conf "&tri=A&k=";
|
||||
Output.print_string conf (Mutil.encode ini_k);
|
||||
Output.print_sstring conf {|">|});
|
||||
Output.print_string conf (tr '_' " " ini_k |> Util.escape_html);
|
||||
Output.print_sstring conf "</a>\n")
|
||||
list;
|
||||
if not too_big then (
|
||||
Output.print_sstring conf "</p><p>";
|
||||
transl conf "the whole list"
|
||||
|> Utf8.capitalize_fst |> Output.print_sstring conf;
|
||||
Output.print_sstring conf (transl conf ":");
|
||||
Output.print_sstring conf "</p><ul><li>";
|
||||
Output.print_sstring conf {|<a href="|};
|
||||
Output.print_string conf (commd conf);
|
||||
Output.print_sstring conf "m=";
|
||||
Output.print_string conf mode;
|
||||
Output.print_sstring conf "&tri=A&o=A&k=";
|
||||
Output.print_string conf (Mutil.encode ini);
|
||||
Output.print_sstring conf {|">|};
|
||||
Output.print_sstring conf (transl conf "long display");
|
||||
Output.print_sstring conf "</a></li><li>";
|
||||
Output.print_sstring conf {|<a href="|};
|
||||
Output.print_string conf (commd conf);
|
||||
Output.print_sstring conf "m=";
|
||||
Output.print_string conf mode;
|
||||
Output.print_sstring conf "&tri=S&o=A&k=";
|
||||
Output.print_string conf (Mutil.encode ini);
|
||||
Output.print_sstring conf {|">|};
|
||||
Output.print_sstring conf (transl conf "short display");
|
||||
Output.print_sstring conf "</a></li><li>";
|
||||
Output.print_sstring conf {|<a href="|};
|
||||
Output.print_string conf (commd conf);
|
||||
Output.print_sstring conf "m=";
|
||||
Output.print_string conf mode;
|
||||
Output.print_sstring conf "&tri=S&o=A&cgl=on&k=";
|
||||
Output.print_string conf (Mutil.encode ini);
|
||||
Output.print_sstring conf {|">|};
|
||||
Output.print_sstring conf (transl conf "short display");
|
||||
Output.print_sstring conf " + ";
|
||||
Output.print_sstring conf (transl conf "cancel GeneWeb links");
|
||||
Output.print_sstring conf "</a></li></ul>");
|
||||
Hutil.trailer conf
|
||||
|
||||
let print_alphabetic_all conf base is_surnames ini list len =
|
||||
let title _ = print_title conf base is_surnames ini len in
|
||||
let mode = Adef.encoded (if is_surnames then "N" else "P") in
|
||||
Hutil.header conf title;
|
||||
Output.print_sstring conf {|<p class="search_name">|};
|
||||
List.iter
|
||||
(fun (ini_k, _) ->
|
||||
Output.print_sstring conf "<a href=\"#a";
|
||||
Output.print_string conf (Mutil.encode ini_k);
|
||||
Output.print_sstring conf "\">";
|
||||
Output.print_string conf (Mutil.tr '_' ' ' ini_k |> Adef.safe);
|
||||
Output.print_sstring conf "</a>\n")
|
||||
list;
|
||||
Output.print_sstring conf "</p><ul>";
|
||||
List.iter
|
||||
(fun (ini_k, l) ->
|
||||
Output.print_sstring conf "<li><a id=\"a";
|
||||
Output.print_string conf (Mutil.encode ini_k);
|
||||
Output.print_sstring conf "\">";
|
||||
Output.print_string conf (Mutil.tr '_' ' ' ini_k |> Adef.safe);
|
||||
Output.print_sstring conf "</a><ul>";
|
||||
List.iter
|
||||
(fun (s, cnt) ->
|
||||
Output.print_sstring conf "<li>";
|
||||
let href = "m=" ^<^ mode ^^^ "&v=" ^<^ Mutil.encode s ^>^ "&t=A" in
|
||||
wprint_geneweb_link conf
|
||||
(href :> Adef.escaped_string)
|
||||
(particle_at_the_end base is_surnames s |> Util.escape_html
|
||||
:> Adef.safe_string);
|
||||
Output.print_sstring conf " (";
|
||||
Output.print_sstring conf (string_of_int cnt);
|
||||
Output.print_sstring conf ")</li>")
|
||||
(List.sort
|
||||
(fun (a, _) (b, _) ->
|
||||
compare_particle_at_the_end base is_surnames a b)
|
||||
l);
|
||||
Output.print_sstring conf "</ul></li>")
|
||||
list;
|
||||
Output.print_sstring conf "</ul>";
|
||||
Hutil.trailer conf
|
||||
|
||||
let print_alphabetic_small conf base is_surnames ini list len =
|
||||
let title _ = print_title conf base is_surnames ini len in
|
||||
let mode = Adef.encoded (if is_surnames then "N" else "P") in
|
||||
Hutil.header conf title;
|
||||
if list <> [] then (
|
||||
Output.print_sstring conf "<ul>";
|
||||
List.iter
|
||||
(fun (_, s, cnt) ->
|
||||
Output.print_sstring conf "<li>";
|
||||
Output.print_sstring conf "<a href=\"";
|
||||
Output.print_string conf (commd conf);
|
||||
Output.print_sstring conf "m=";
|
||||
Output.print_string conf mode;
|
||||
Output.print_sstring conf "&v=";
|
||||
Output.print_string conf (Mutil.encode s);
|
||||
Output.print_sstring conf "&t=A\">";
|
||||
Output.print_string conf
|
||||
(particle_at_the_end base is_surnames s |> Util.escape_html);
|
||||
Output.print_sstring conf "</a> (";
|
||||
Output.print_sstring conf (string_of_int cnt);
|
||||
Output.print_sstring conf ")</li>")
|
||||
(List.sort
|
||||
(fun (_, a, _) (_, b, _) ->
|
||||
compare_particle_at_the_end base is_surnames a b)
|
||||
list);
|
||||
Output.print_sstring conf "</ul>");
|
||||
Hutil.trailer conf
|
||||
|
||||
let print_frequency_any conf base is_surnames list len =
|
||||
let title _ = print_title conf base is_surnames "" len in
|
||||
let mode = Adef.encoded (if is_surnames then "N" else "P") in
|
||||
let n = ref 0 in
|
||||
Hutil.header conf title;
|
||||
Output.print_sstring conf "<ul>";
|
||||
List.iter
|
||||
(fun (cnt, l) ->
|
||||
if !n <= default_max_cnt then (
|
||||
Output.print_sstring conf "<li>";
|
||||
Output.print_sstring conf (string_of_int cnt);
|
||||
Output.print_sstring conf "<ul>";
|
||||
List.iter
|
||||
(fun s ->
|
||||
Output.print_sstring conf "<li><a href=\"";
|
||||
Output.print_string conf (commd conf);
|
||||
Output.print_sstring conf "m=";
|
||||
Output.print_string conf mode;
|
||||
Output.print_sstring conf "&v=";
|
||||
Output.print_string conf (Mutil.encode (Name.lower s));
|
||||
Output.print_sstring conf "\">";
|
||||
Output.print_string conf
|
||||
(particle_at_the_end base is_surnames s |> Util.escape_html);
|
||||
Output.print_sstring conf "</a></li>";
|
||||
incr n)
|
||||
l;
|
||||
Output.print_sstring conf "</ul>";
|
||||
Output.print_sstring conf "</li>"))
|
||||
list;
|
||||
Output.print_sstring conf "</ul>";
|
||||
Hutil.trailer conf
|
||||
|
||||
let print_frequency conf base is_surnames =
|
||||
let () = load_strings_array base in
|
||||
let list, len = Alln.select_names conf base is_surnames "" max_int in
|
||||
let list = Alln.groupby_count list in
|
||||
print_frequency_any conf base is_surnames list len
|
||||
|
||||
let print_alphabetic conf base is_surnames =
|
||||
let ini = match p_getenv conf.env "k" with Some k -> k | _ -> "" in
|
||||
if List.assoc_opt "fast_alphabetic" conf.base_env = Some "yes" && ini = ""
|
||||
then (
|
||||
load_strings_array base;
|
||||
let list = Alln.first_letters base is_surnames in
|
||||
let list = List.sort Gutil.alphabetic_order list in
|
||||
print_alphabetic_big conf base is_surnames ini list 1 true)
|
||||
else
|
||||
let all =
|
||||
match p_getenv conf.env "o" with Some "A" -> true | _ -> false
|
||||
in
|
||||
if String.length ini < 2 then load_strings_array base;
|
||||
let list, len =
|
||||
Alln.select_names conf base is_surnames ini (if all then max_int else 50)
|
||||
in
|
||||
match list with
|
||||
| Alln.Specify keys ->
|
||||
let keys = List.sort Gutil.alphabetic_order keys in
|
||||
let too_big = (not all) && List.length keys > Alln.default_max_cnt in
|
||||
print_alphabetic_big conf base is_surnames ini keys len too_big
|
||||
| Alln.Result list ->
|
||||
if len >= 50 || ini = "" then
|
||||
let list = Alln.groupby_ini (Utf8.length ini + 1) list in
|
||||
print_alphabetic_all conf base is_surnames ini list len
|
||||
else print_alphabetic_small conf base is_surnames ini list len
|
||||
|
||||
(* short print *)
|
||||
|
||||
let print_alphabetic_short conf base is_surnames ini list len =
|
||||
let title _ = print_title conf base is_surnames ini len in
|
||||
let mode = Adef.encoded (if is_surnames then "N" else "P") in
|
||||
let need_ref = len >= 250 in
|
||||
Hutil.header conf title;
|
||||
if need_ref then (
|
||||
Output.print_sstring conf "<p>";
|
||||
List.iter
|
||||
(fun (ini_k, _) ->
|
||||
Output.print_sstring conf "<a href=\"#a";
|
||||
Output.print_string conf (Mutil.encode ini_k);
|
||||
Output.print_sstring conf "\">";
|
||||
Output.print_string conf (Mutil.tr '_' ' ' ini_k |> Util.escape_html);
|
||||
Output.print_sstring conf "</a>\n")
|
||||
list;
|
||||
Output.print_sstring conf "</p>");
|
||||
List.iter
|
||||
(fun (ini_k, l) ->
|
||||
Output.print_sstring conf "<p>";
|
||||
Mutil.list_iter_first
|
||||
(fun first (s, cnt) ->
|
||||
let href =
|
||||
" href=\"" ^<^ commd conf
|
||||
^^^ ("m=" ^<^ mode ^^^ "&v=" ^<^ Mutil.encode s ^>^ "&t=A\""
|
||||
:> Adef.escaped_string)
|
||||
in
|
||||
let name =
|
||||
Adef.encoded
|
||||
(if first && need_ref then " id=\"a" ^ ini_k ^ "\"" else "")
|
||||
in
|
||||
if not first then Output.print_sstring conf ",";
|
||||
Output.print_sstring conf "\n<a";
|
||||
Output.print_string conf href;
|
||||
Output.print_string conf name;
|
||||
Output.print_sstring conf ">";
|
||||
Output.print_string conf
|
||||
(particle_at_the_end base is_surnames s |> Util.escape_html);
|
||||
Output.print_sstring conf "</a>";
|
||||
Output.print_sstring conf " (";
|
||||
Output.print_sstring conf (string_of_int cnt);
|
||||
Output.print_sstring conf ")")
|
||||
(List.sort (fun (a, _) (b, _) -> Gutil.alphabetic_order a b) l);
|
||||
Output.print_sstring conf "</p>")
|
||||
list;
|
||||
Hutil.trailer conf
|
||||
|
||||
let print_short conf base is_surnames =
|
||||
let ini = match p_getenv conf.env "k" with Some k -> k | _ -> "" in
|
||||
let _ = if String.length ini < 2 then load_strings_array base in
|
||||
match Alln.select_names conf base is_surnames ini max_int with
|
||||
| Alln.Specify _, _ -> Hutil.incorrect_request conf
|
||||
| Alln.Result list, len ->
|
||||
let list = Alln.groupby_ini (Utf8.length ini + 1) list in
|
||||
print_alphabetic_short conf base is_surnames ini list len
|
||||
|
||||
(* main *)
|
||||
|
||||
let print_surnames conf base =
|
||||
match p_getenv conf.env "tri" with
|
||||
| Some "F" -> print_frequency conf base true
|
||||
| Some "S" -> print_short conf base true
|
||||
| _ -> print_alphabetic conf base true
|
||||
|
||||
let print_first_names conf base =
|
||||
match p_getenv conf.env "tri" with
|
||||
| Some "F" -> print_frequency conf base false
|
||||
| Some "S" -> print_short conf base false
|
||||
| _ -> print_alphabetic conf base false
|
||||
13
lib/allnDisplay.mli
Normal file
13
lib/allnDisplay.mli
Normal file
@@ -0,0 +1,13 @@
|
||||
val print_surnames : Config.config -> Gwdb.base -> unit
|
||||
(** Displays all persons surnames present in the base. Display could be different depending
|
||||
on environement [conf.env]. These variables affect the display:
|
||||
|
||||
- tri : "F" to display surnames by frequency, "S" to display
|
||||
surnames regrouped by first letter (depends on variable "k")
|
||||
otherwsise display surnames just ordered alphabeticaly
|
||||
- k : Defines common prefix for surnames (empty for all)
|
||||
- o : "A" to print all surnames (if less then [Alln.default_max_cnt])
|
||||
otherwise prints links to access different type of displaying *)
|
||||
|
||||
val print_first_names : Config.config -> Gwdb.base -> unit
|
||||
(** Same as [print_surnames] but dealing with first names. *)
|
||||
283
lib/ansel.ml
Normal file
283
lib/ansel.ml
Normal file
@@ -0,0 +1,283 @@
|
||||
(* $Id: ansel.ml,v 5.3 2007-01-19 01:53:16 ddr Exp $ *)
|
||||
(* Copyright (c) 1998-2007 INRIA *)
|
||||
|
||||
let iso_8859_1_unknown = '\129'
|
||||
let ansel_unknown = 129
|
||||
|
||||
let no_accent = function
|
||||
| '\224' .. '\229' -> 'a'
|
||||
| '\162' | '\231' -> 'c'
|
||||
| '\232' .. '\235' -> 'e'
|
||||
| '\236' .. '\239' -> 'i'
|
||||
| '\241' -> 'n'
|
||||
| '\242' .. '\246' -> 'o'
|
||||
| '\249' .. '\252' -> 'u'
|
||||
| '\253' | '\255' -> 'y'
|
||||
| '\192' .. '\197' -> 'A'
|
||||
| '\199' -> 'C'
|
||||
| '\200' .. '\203' -> 'E'
|
||||
| '\204' .. '\207' -> 'I'
|
||||
| '\209' -> 'N'
|
||||
| '\210' .. '\214' -> 'O'
|
||||
| '\217' .. '\220' -> 'U'
|
||||
| '\221' -> 'Y'
|
||||
| '\168' | '\176' | '\180' | '\184' | '\186' -> ' '
|
||||
| '\171' -> '<'
|
||||
| '\187' -> '>'
|
||||
| c -> c
|
||||
|
||||
let accent_code = (* and 1-to-1 conversions *)
|
||||
function
|
||||
| '\192' | '\200' | '\204' | '\210' | '\217' | '\224' | '\232' | '\236'
|
||||
| '\242' | '\249' ->
|
||||
225
|
||||
| '\193' | '\201' | '\205' | '\211' | '\218' | '\221' | '\180' | '\225'
|
||||
| '\233' | '\237' | '\243' | '\250' | '\253' ->
|
||||
226
|
||||
| '\194' | '\202' | '\206' | '\212' | '\219' | '\226' | '\234' | '\238'
|
||||
| '\244' | '\251' ->
|
||||
227
|
||||
| '\195' | '\209' | '\213' | '\227' | '\241' | '\245' -> 228
|
||||
| '\196' | '\203' | '\207' | '\214' | '\220' | '\168' | '\228' | '\235'
|
||||
| '\239' | '\246' | '\252' | '\255' ->
|
||||
232
|
||||
| '\197' | '\229' | '\176' | '\186' -> 234
|
||||
| '\199' | '\231' | '\184' -> 240
|
||||
| '\161' -> 198
|
||||
| '\162' -> 252
|
||||
| '\163' -> 185
|
||||
| '\164' -> 0x6f
|
||||
| '\165' -> 0x59
|
||||
| '\166' -> 0x7c
|
||||
| '\169' -> 195
|
||||
| '\170' -> 0x61
|
||||
| '\171' -> 0x3c
|
||||
| '\173' -> 0x2d
|
||||
| '\174' -> 170
|
||||
| '\177' -> 171
|
||||
| '\178' -> 0x32
|
||||
| '\179' -> 0x33
|
||||
| '\183' -> 168
|
||||
| '\185' -> 0x31
|
||||
| '\187' -> 0x3e
|
||||
| '\191' -> 197
|
||||
| '\198' -> 165
|
||||
| '\230' -> 181
|
||||
| '\208' -> 163
|
||||
| '\240' -> 179
|
||||
| '\216' -> 162
|
||||
| '\248' -> 178
|
||||
| '\222' -> 164
|
||||
| '\254' -> 180
|
||||
| '\223' -> 207
|
||||
| c when c >= '\161' -> ansel_unknown
|
||||
| _ -> 0
|
||||
|
||||
let of_iso_8859_1 s =
|
||||
let len, identical =
|
||||
let rec loop i len identical =
|
||||
if i = String.length s then (len, identical)
|
||||
else
|
||||
let a = accent_code s.[i] in
|
||||
if a = 0 then loop (i + 1) (len + 1) identical
|
||||
else
|
||||
let n = no_accent s.[i] in
|
||||
if n = s.[i] then loop (i + 1) (len + 1) false
|
||||
else loop (i + 1) (len + 2) false
|
||||
in
|
||||
loop 0 0 true
|
||||
in
|
||||
if identical then s
|
||||
else
|
||||
let s' = Bytes.create len in
|
||||
let rec loop i i' =
|
||||
if i = String.length s then Bytes.unsafe_to_string s'
|
||||
else
|
||||
let i' =
|
||||
let a = accent_code s.[i] in
|
||||
if a > 0 then (
|
||||
Bytes.set s' i' (Char.chr a);
|
||||
let n = no_accent s.[i] in
|
||||
if n = s.[i] then i'
|
||||
else (
|
||||
Bytes.set s' (i' + 1) n;
|
||||
i' + 1))
|
||||
else (
|
||||
Bytes.set s' i' s.[i];
|
||||
i')
|
||||
in
|
||||
loop (i + 1) (i' + 1)
|
||||
in
|
||||
loop 0 0
|
||||
|
||||
let grave = function
|
||||
| 'a' -> '\224'
|
||||
| 'e' -> '\232'
|
||||
| 'i' -> '\236'
|
||||
| 'o' -> '\242'
|
||||
| 'u' -> '\249'
|
||||
| 'A' -> '\192'
|
||||
| 'E' -> '\200'
|
||||
| 'I' -> '\204'
|
||||
| 'O' -> '\210'
|
||||
| 'U' -> '\217'
|
||||
| ' ' -> '`'
|
||||
| x -> x
|
||||
|
||||
let acute = function
|
||||
| 'a' -> '\225'
|
||||
| 'e' -> '\233'
|
||||
| 'i' -> '\237'
|
||||
| 'o' -> '\243'
|
||||
| 'u' -> '\250'
|
||||
| 'y' -> '\253'
|
||||
| 'A' -> '\193'
|
||||
| 'E' -> '\201'
|
||||
| 'I' -> '\205'
|
||||
| 'O' -> '\211'
|
||||
| 'U' -> '\218'
|
||||
| 'Y' -> '\221'
|
||||
| ' ' -> '\180'
|
||||
| x -> x
|
||||
|
||||
let circum = function
|
||||
| 'a' -> '\226'
|
||||
| 'e' -> '\234'
|
||||
| 'i' -> '\238'
|
||||
| 'o' -> '\244'
|
||||
| 'u' -> '\251'
|
||||
| 'A' -> '\194'
|
||||
| 'E' -> '\202'
|
||||
| 'I' -> '\206'
|
||||
| 'O' -> '\212'
|
||||
| 'U' -> '\219'
|
||||
| ' ' -> '^'
|
||||
| x -> x
|
||||
|
||||
let uml = function
|
||||
| 'a' -> '\228'
|
||||
| 'e' -> '\235'
|
||||
| 'i' -> '\239'
|
||||
| 'o' -> '\246'
|
||||
| 'u' -> '\252'
|
||||
| 'y' -> '\255'
|
||||
| 'A' -> '\196'
|
||||
| 'E' -> '\203'
|
||||
| 'I' -> '\207'
|
||||
| 'O' -> '\214'
|
||||
| 'U' -> '\220'
|
||||
| ' ' -> '\168'
|
||||
| x -> x
|
||||
|
||||
let circle = function 'a' -> '\229' | 'A' -> '\197' | ' ' -> '\176' | x -> x
|
||||
|
||||
let tilde = function
|
||||
| 'a' -> '\227'
|
||||
| 'n' -> '\241'
|
||||
| 'o' -> '\245'
|
||||
| 'A' -> '\195'
|
||||
| 'N' -> '\209'
|
||||
| 'O' -> '\213'
|
||||
| ' ' -> '~'
|
||||
| x -> x
|
||||
|
||||
let cedil = function 'c' -> '\231' | 'C' -> '\199' | ' ' -> '\184' | x -> x
|
||||
|
||||
let slash = function
|
||||
| 'C' | 'c' -> '\162'
|
||||
| 'O' -> '\216'
|
||||
| 'o' -> '\248'
|
||||
| ' ' -> '/'
|
||||
| x -> x
|
||||
|
||||
let to_iso_8859_1 s =
|
||||
let len, identical =
|
||||
let rec loop i len identical =
|
||||
if i = String.length s then (len, identical)
|
||||
else if i = String.length s - 1 then (len + 1, identical)
|
||||
else
|
||||
match Char.code s.[i] with
|
||||
| 166 | 172 | 173 | 182 | 188 | 189 -> loop (i + 1) (len + 2) false
|
||||
| c when c >= 224 -> loop (i + 2) (len + 1) false
|
||||
| c when c >= 161 -> loop (i + 1) (len + 1) false
|
||||
| _ -> loop (i + 1) (len + 1) identical
|
||||
in
|
||||
loop 0 0 true
|
||||
in
|
||||
if identical then s
|
||||
else
|
||||
let s' = Bytes.create len in
|
||||
let rec loop i i' =
|
||||
if i = String.length s then Bytes.unsafe_to_string s'
|
||||
else if i = String.length s - 1 then (
|
||||
Bytes.set s' i' s.[i];
|
||||
Bytes.unsafe_to_string s')
|
||||
else
|
||||
match Char.code s.[i] with
|
||||
| (166 | 172 | 173 | 182 | 188 | 189) as c ->
|
||||
let c', c'' =
|
||||
match c with
|
||||
| 166 -> ('O', 'E')
|
||||
| 172 -> ('O', '\180')
|
||||
| 173 -> ('U', '\180')
|
||||
| 182 -> ('o', 'e')
|
||||
| 188 -> ('o', '\180')
|
||||
| 189 -> ('u', '\180')
|
||||
| _ -> (iso_8859_1_unknown, iso_8859_1_unknown)
|
||||
in
|
||||
Bytes.set s' i' c';
|
||||
Bytes.set s' (i' + 1) c'';
|
||||
loop (i + 1) (i' + 2)
|
||||
| c when c >= 224 ->
|
||||
let c' = s.[i + 1] in
|
||||
let c' =
|
||||
match c with
|
||||
| 224 | 226 | 235 | 237 | 254 -> acute c'
|
||||
| 225 | 236 -> grave c'
|
||||
| 227 | 250 -> circum c'
|
||||
| 228 | 230 | 233 -> tilde c'
|
||||
| 232 | 238 -> uml c'
|
||||
| 231 | 234 -> circle c'
|
||||
| 240 | 241 | 242 | 243 | 244 | 247 | 248 | 249 -> cedil c'
|
||||
| 252 -> slash c'
|
||||
| _ -> c'
|
||||
in
|
||||
Bytes.set s' i' c';
|
||||
loop (i + 2) (i' + 1)
|
||||
| c ->
|
||||
let c' =
|
||||
match c with
|
||||
| 161 -> 'L'
|
||||
| 162 -> '\216'
|
||||
| 163 -> '\208'
|
||||
| 164 -> '\222'
|
||||
| 165 -> '\198'
|
||||
| 167 | 174 | 176 -> '\180'
|
||||
| 168 -> '\183'
|
||||
| 169 -> 'b'
|
||||
| 170 -> '\174'
|
||||
| 171 -> '\177'
|
||||
| 177 | 193 -> 'l'
|
||||
| 178 -> '\248'
|
||||
| 179 | 186 -> '\240'
|
||||
| 180 -> '\254'
|
||||
| 181 -> '\230'
|
||||
| 183 -> '"'
|
||||
| 184 -> 'i'
|
||||
| 185 -> '\163'
|
||||
| 190 | 191 -> iso_8859_1_unknown
|
||||
| 192 -> '\176'
|
||||
| 194 -> 'P'
|
||||
| 195 -> '\169'
|
||||
| 196 -> '#'
|
||||
| 197 -> '\191'
|
||||
| 198 -> '\161'
|
||||
| 205 -> '\101'
|
||||
| 206 -> '\111'
|
||||
| 207 -> '\223'
|
||||
| _ -> Char.chr c
|
||||
in
|
||||
Bytes.set s' i' c';
|
||||
loop (i + 1) (i' + 1)
|
||||
in
|
||||
loop 0 0
|
||||
5
lib/ansel.mli
Normal file
5
lib/ansel.mli
Normal file
@@ -0,0 +1,5 @@
|
||||
val of_iso_8859_1 : string -> string
|
||||
(** Convert ISO-8859-1 encoded string to ANSEL encoding used inside gedcom files *)
|
||||
|
||||
val to_iso_8859_1 : string -> string
|
||||
(** Convert ANSEL used inside gedcom files to ISO-8859-1 encoding *)
|
||||
68
lib/ascendDisplay.ml
Normal file
68
lib/ascendDisplay.ml
Normal file
@@ -0,0 +1,68 @@
|
||||
(* Copyright (c) 1998-2007 INRIA *)
|
||||
|
||||
open Config
|
||||
open Gwdb
|
||||
open Util
|
||||
|
||||
let limit_by_tree conf =
|
||||
match List.assoc_opt "max_anc_tree" conf.base_env with
|
||||
| Some x -> max 1 (int_of_string x)
|
||||
| None -> 7
|
||||
|
||||
let print_ancestors_dag conf base v p =
|
||||
let v = min (limit_by_tree conf) v in
|
||||
let set =
|
||||
(* TODO this should be a get_ancestors_set lvl ip *)
|
||||
let rec loop set lev ip =
|
||||
let set = Dag.Pset.add ip set in
|
||||
if lev <= 0 then set
|
||||
else
|
||||
match get_parents (pget conf base ip) with
|
||||
| Some ifam ->
|
||||
let cpl = foi base ifam in
|
||||
let get_left, get_right =
|
||||
match p_getenv conf.env "mf" with
|
||||
| Some "1" -> (get_father, get_mother)
|
||||
| _ -> (get_mother, get_father)
|
||||
in
|
||||
let set = loop set (lev - 1) (get_left cpl) in
|
||||
loop set (lev - 1) (get_right cpl)
|
||||
| None -> set
|
||||
in
|
||||
loop Dag.Pset.empty v (get_iper p)
|
||||
in
|
||||
let elem_txt p = DagDisplay.Item (p, Adef.safe "") in
|
||||
(* Récupère les options d'affichage. *)
|
||||
let options = Util.display_options conf in
|
||||
let vbar_txt ip =
|
||||
let p = pget conf base ip in
|
||||
Printf.sprintf {|%s%s&m=A&t=T&dag=on&v=%d%s |}
|
||||
(commd conf :> string)
|
||||
(acces conf base p :> string)
|
||||
v
|
||||
(options :> string)
|
||||
|> Adef.escaped
|
||||
in
|
||||
let page_title =
|
||||
Util.transl conf "tree" |> Utf8.capitalize_fst |> Adef.safe
|
||||
in
|
||||
DagDisplay.make_and_print_dag conf base elem_txt vbar_txt true set []
|
||||
page_title (Adef.escaped "")
|
||||
|
||||
let print conf base p =
|
||||
match
|
||||
( Util.p_getenv conf.env "t",
|
||||
Util.p_getenv conf.env "dag",
|
||||
p_getint conf.env "v" )
|
||||
with
|
||||
| Some "T", Some "on", Some v -> print_ancestors_dag conf base v p
|
||||
| _ ->
|
||||
let templ =
|
||||
match Util.p_getenv conf.env "t" with
|
||||
| Some ("E" | "F" | "H" | "L") -> "anclist"
|
||||
| Some ("D" | "G" | "M" | "N" | "P" | "X" | "Y" | "Z") -> "ancsosa"
|
||||
| Some ("A" | "C" | "T") -> "anctree"
|
||||
| Some "FC" -> "fanchart"
|
||||
| _ -> "ancmenu"
|
||||
in
|
||||
Perso.interp_templ templ conf base p
|
||||
115
lib/birthDeath.ml
Normal file
115
lib/birthDeath.ml
Normal file
@@ -0,0 +1,115 @@
|
||||
(* Copyright (c) 1998-2007 INRIA *)
|
||||
|
||||
open Config
|
||||
open Def
|
||||
open Gwdb
|
||||
open Util
|
||||
|
||||
let get_k conf =
|
||||
match p_getint conf.env "k" with
|
||||
| Some x -> x
|
||||
| _ -> (
|
||||
try int_of_string (List.assoc "latest_event" conf.base_env)
|
||||
with Not_found | Failure _ -> 20)
|
||||
|
||||
let select (type a) (module Q : Pqueue.S with type elt = a * dmy * calendar)
|
||||
nb_of iterator get get_date conf base =
|
||||
let n = min (max 0 (get_k conf)) (nb_of base) in
|
||||
let ref_date =
|
||||
match p_getint conf.env "by" with
|
||||
| Some by ->
|
||||
let bm = Option.value ~default:(-1) (p_getint conf.env "bm") in
|
||||
let bd = Option.value ~default:(-1) (p_getint conf.env "bd") in
|
||||
Some { day = bd; month = bm; year = by; prec = Sure; delta = 0 }
|
||||
| None -> None
|
||||
in
|
||||
let q, len =
|
||||
Gwdb.Collection.fold
|
||||
(fun (q, len) i ->
|
||||
let x = get base i in
|
||||
match get_date x with
|
||||
| Some (Dgreg (d, cal)) ->
|
||||
let aft =
|
||||
match ref_date with
|
||||
| Some ref_date -> Date.compare_dmy ref_date d <= 0
|
||||
| None -> false
|
||||
in
|
||||
if aft then (q, len)
|
||||
else
|
||||
let e = (x, d, cal) in
|
||||
if len < n then (Q.add e q, len + 1)
|
||||
else (snd (Q.take (Q.add e q)), len)
|
||||
| _ -> (q, len))
|
||||
(Q.empty, 0) (iterator base)
|
||||
in
|
||||
let rec loop list q =
|
||||
if Q.is_empty q then (list, len)
|
||||
else
|
||||
let e, q = Q.take q in
|
||||
loop (e :: list) q
|
||||
in
|
||||
loop [] q
|
||||
|
||||
module PQ = Pqueue.Make (struct
|
||||
type t = Gwdb.person * Def.dmy * Def.calendar
|
||||
|
||||
let leq (_, x, _) (_, y, _) = Date.compare_dmy x y <= 0
|
||||
end)
|
||||
|
||||
module PQ_oldest = Pqueue.Make (struct
|
||||
type t = Gwdb.person * Def.dmy * Def.calendar
|
||||
|
||||
let leq (_, x, _) (_, y, _) = Date.compare_dmy y x <= 0
|
||||
end)
|
||||
|
||||
let select_person conf base get_date find_oldest =
|
||||
select
|
||||
(if find_oldest then (module PQ_oldest) else (module PQ))
|
||||
nb_of_persons Gwdb.ipers (pget conf) get_date conf base
|
||||
|
||||
module FQ = Pqueue.Make (struct
|
||||
type t = Gwdb.family * Def.dmy * Def.calendar
|
||||
|
||||
let leq (_, x, _) (_, y, _) = Date.compare_dmy x y <= 0
|
||||
end)
|
||||
|
||||
module FQ_oldest = Pqueue.Make (struct
|
||||
type t = Gwdb.family * Def.dmy * Def.calendar
|
||||
|
||||
let leq (_, x, _) (_, y, _) = Date.compare_dmy y x <= 0
|
||||
end)
|
||||
|
||||
let select_family conf base get_date find_oldest =
|
||||
select
|
||||
(if find_oldest then (module FQ_oldest) else (module FQ))
|
||||
nb_of_families Gwdb.ifams Gwdb.foi get_date conf base
|
||||
|
||||
let death_date p = Date.date_of_death (get_death p)
|
||||
|
||||
let make_population_pyramid ~nb_intervals ~interval ~limit ~at_date conf base =
|
||||
let men = Array.make (nb_intervals + 1) 0 in
|
||||
let wom = Array.make (nb_intervals + 1) 0 in
|
||||
(* TODO? Load person array *)
|
||||
Gwdb.Collection.iter
|
||||
(fun i ->
|
||||
let p = pget conf base i in
|
||||
let sex = get_sex p in
|
||||
let dea = get_death p in
|
||||
if sex <> Neuter then
|
||||
match Date.cdate_to_dmy_opt (get_birth p) with
|
||||
| None -> ()
|
||||
| Some dmy ->
|
||||
if Date.compare_dmy dmy at_date <= 0 then
|
||||
let a = Date.time_elapsed dmy at_date in
|
||||
let j = min nb_intervals (a.year / interval) in
|
||||
if
|
||||
(dea = NotDead || (dea = DontKnowIfDead && a.year < limit))
|
||||
||
|
||||
match Date.dmy_of_death dea with
|
||||
| None -> false
|
||||
| Some d -> Date.compare_dmy d at_date > 0
|
||||
then
|
||||
if sex = Male then men.(j) <- men.(j) + 1
|
||||
else wom.(j) <- wom.(j) + 1)
|
||||
(Gwdb.ipers base);
|
||||
(men, wom)
|
||||
44
lib/birthDeath.mli
Normal file
44
lib/birthDeath.mli
Normal file
@@ -0,0 +1,44 @@
|
||||
val select_person :
|
||||
Config.config ->
|
||||
Gwdb.base ->
|
||||
(Gwdb.person -> Def.date option) ->
|
||||
bool ->
|
||||
(Gwdb.person * Def.dmy * Def.calendar) list * int
|
||||
(** [select_person conf base get_date find_oldest] select 20 persons
|
||||
from the base according to the one of their date (birth, death,
|
||||
marriage, specific event, etc.) that could be get with [get_date].
|
||||
Returns sorted by date persons that have the latest (if [find_oldest]
|
||||
is false) or oldest (otherwise) date. Selection could be different depending
|
||||
on environement [conf.env]. These variables affect the selection:
|
||||
k - allows to modify default value (20) of selected persons
|
||||
by,bm,bd - allows to set reference date (all dates after the reference
|
||||
one aren't selected)
|
||||
Returns also the number of selected persons *)
|
||||
|
||||
val select_family :
|
||||
Config.config ->
|
||||
Gwdb.base ->
|
||||
(Gwdb.family -> Def.date option) ->
|
||||
bool ->
|
||||
(Gwdb.family * Def.dmy * Def.calendar) list * int
|
||||
(** Same as [select_person] but dealing with families *)
|
||||
|
||||
val death_date : Gwdb.person -> Adef.date option
|
||||
(** Returns person's death date (if exists) *)
|
||||
|
||||
val make_population_pyramid :
|
||||
nb_intervals:int ->
|
||||
interval:int ->
|
||||
limit:int ->
|
||||
at_date:Def.dmy ->
|
||||
Config.config ->
|
||||
Gwdb.base ->
|
||||
int array * int array
|
||||
(** [make_population_pyramid nb_intervals interval interval at_date conf base]
|
||||
Calculates population pyramid of all perons in the base. Population pyramid
|
||||
consists of two separated arrays that regroups number of men's and women's born
|
||||
in each time interval. One array has a size [nb_intervals + 1] and every element
|
||||
is a number of persons born in the giving time interval that represents [interval] years.
|
||||
Calculation starts at the date [at_date] and persons that are considered
|
||||
in pyramid should be alive at this date. [limit] allows to limit persons
|
||||
by age (those that has age greater then limit aren't taken into the account) *)
|
||||
553
lib/birthDeathDisplay.ml
Normal file
553
lib/birthDeathDisplay.ml
Normal file
@@ -0,0 +1,553 @@
|
||||
(* Copyright (c) 1998-2007 INRIA *)
|
||||
|
||||
open Config
|
||||
open Def
|
||||
open Gwdb
|
||||
open Util
|
||||
open BirthDeath
|
||||
|
||||
let month_txt conf d cal =
|
||||
let d = DateDisplay.string_of_date conf (Dgreg ({ d with day = 0 }, cal)) in
|
||||
(d : Adef.safe_string :> string) |> Utf8.capitalize_fst |> Adef.safe
|
||||
|
||||
let list_aux_1 conf d cal last_month_txt was_future =
|
||||
let month_txt = month_txt conf d cal in
|
||||
let future = Date.compare_dmy d conf.today = 1 in
|
||||
if (not future) && was_future then (
|
||||
Output.print_sstring conf "</li></ul></li></ul><p><ul><li>";
|
||||
Output.print_string conf month_txt;
|
||||
Output.print_sstring conf "<ul>")
|
||||
else if month_txt <> last_month_txt then (
|
||||
if (last_month_txt :> string) <> "" then
|
||||
Output.print_sstring conf "</ul></li>";
|
||||
Output.print_sstring conf "<li>";
|
||||
Output.print_string conf month_txt;
|
||||
Output.print_sstring conf "<ul>");
|
||||
(month_txt, future)
|
||||
|
||||
let print_birth conf base =
|
||||
let list, len =
|
||||
select_person conf base (fun p -> Date.od_of_cdate (get_birth p)) false
|
||||
in
|
||||
let title _ =
|
||||
Output.printf conf (fcapitale (ftransl conf "the latest %d births")) len
|
||||
in
|
||||
Hutil.header conf title;
|
||||
Hutil.print_link_to_welcome conf true;
|
||||
Output.print_sstring conf "<ul>\n";
|
||||
ignore
|
||||
@@ List.fold_left
|
||||
(fun (last_month_txt, was_future) (p, d, cal) ->
|
||||
let month_txt, future =
|
||||
list_aux_1 conf d cal last_month_txt was_future
|
||||
in
|
||||
Output.print_sstring conf "<li><b>";
|
||||
Output.print_string conf (referenced_person_text conf base p);
|
||||
Output.print_sstring conf "</b>, ";
|
||||
if future then (
|
||||
Output.print_sstring conf "<em>";
|
||||
Output.print_string conf
|
||||
(DateDisplay.string_of_date conf (Dgreg (d, cal)));
|
||||
Output.print_sstring conf "</em>.")
|
||||
else (
|
||||
Output.print_sstring conf
|
||||
(transl_nth conf "born" (index_of_sex (get_sex p)));
|
||||
Output.print_sstring conf " <em>";
|
||||
Output.print_string conf
|
||||
(DateDisplay.string_of_ondate conf (Dgreg (d, cal)));
|
||||
Output.print_sstring conf "</em>.");
|
||||
Output.print_sstring conf "</li>";
|
||||
(month_txt, future))
|
||||
(Adef.safe "", false)
|
||||
list;
|
||||
Output.print_sstring conf "</ul></li>";
|
||||
Hutil.trailer conf
|
||||
|
||||
let print_death conf base =
|
||||
let list, len = select_person conf base death_date false in
|
||||
let title _ =
|
||||
Printf.sprintf
|
||||
(fcapitale (ftransl conf "the latest %t deaths"))
|
||||
(fun _ -> string_of_int len)
|
||||
|> Output.print_sstring conf
|
||||
in
|
||||
Hutil.header conf title;
|
||||
Hutil.print_link_to_welcome conf true;
|
||||
if list <> [] then (
|
||||
Output.print_sstring conf "<ul>";
|
||||
let _, ages_sum, ages_nb =
|
||||
List.fold_left
|
||||
(fun (last_month_txt, ages_sum, ages_nb) (p, d, cal) ->
|
||||
let month_txt = month_txt conf d cal in
|
||||
if month_txt <> last_month_txt then (
|
||||
if (last_month_txt :> string) <> "" then
|
||||
Output.print_sstring conf "</ul>\n</li>\n";
|
||||
Output.print_sstring conf "<li>";
|
||||
Output.print_string conf month_txt;
|
||||
Output.print_sstring conf "<ul>");
|
||||
let age, ages_sum, ages_nb =
|
||||
let sure d = d.prec = Sure in
|
||||
match Date.cdate_to_dmy_opt (get_birth p) with
|
||||
| None -> (None, ages_sum, ages_nb)
|
||||
| Some d1 ->
|
||||
if sure d1 && sure d && d1 <> d then
|
||||
let a = Date.time_elapsed d1 d in
|
||||
let ages_sum =
|
||||
match get_sex p with
|
||||
| Male -> (fst ages_sum + a.year, snd ages_sum)
|
||||
| Female -> (fst ages_sum, snd ages_sum + a.year)
|
||||
| Neuter -> ages_sum
|
||||
in
|
||||
let ages_nb =
|
||||
match get_sex p with
|
||||
| Male -> (fst ages_nb + 1, snd ages_nb)
|
||||
| Female -> (fst ages_nb, snd ages_nb + 1)
|
||||
| Neuter -> ages_nb
|
||||
in
|
||||
(Some a, ages_sum, ages_nb)
|
||||
else (None, ages_sum, ages_nb)
|
||||
in
|
||||
Output.print_sstring conf "<li><b>";
|
||||
Output.print_string conf (referenced_person_text conf base p);
|
||||
Output.print_sstring conf "</b>, ";
|
||||
Output.print_sstring conf
|
||||
(transl_nth conf "died" (index_of_sex (get_sex p)));
|
||||
Output.print_sstring conf " <em>";
|
||||
Output.print_string conf
|
||||
(DateDisplay.string_of_ondate conf (Dgreg (d, cal)));
|
||||
Output.print_sstring conf "</em>";
|
||||
Option.iter
|
||||
(fun a ->
|
||||
Output.print_sstring conf " <em>(";
|
||||
Output.print_string conf (DateDisplay.string_of_age conf a);
|
||||
Output.print_sstring conf ")</em>")
|
||||
age;
|
||||
Output.print_sstring conf "</li>";
|
||||
(month_txt, ages_sum, ages_nb))
|
||||
(Adef.safe "", (0, 0), (0, 0))
|
||||
list
|
||||
in
|
||||
Output.print_sstring conf "</ul></li></ul>";
|
||||
let aux sex nb sum =
|
||||
if nb >= 3 then (
|
||||
transl conf "average age at death"
|
||||
|> Utf8.capitalize_fst |> Output.print_sstring conf;
|
||||
Output.print_sstring conf " (";
|
||||
Output.print_sstring conf (transl_nth conf "M/F" sex);
|
||||
Output.print_sstring conf ") : ";
|
||||
Output.print_string conf
|
||||
(DateDisplay.string_of_age conf
|
||||
{ day = 0; month = 0; year = sum / nb; delta = 0; prec = Sure });
|
||||
Output.print_sstring conf "<br>")
|
||||
in
|
||||
aux 0 (fst ages_nb) (fst ages_sum);
|
||||
aux 1 (snd ages_nb) (snd ages_sum);
|
||||
Output.print_sstring conf
|
||||
{|<br><div align="center"><hr width="50%"></div><br>|};
|
||||
let aux name def =
|
||||
string_of_int
|
||||
@@
|
||||
match p_getenv conf.env name with
|
||||
| Some s -> int_of_string s
|
||||
| None -> def
|
||||
in
|
||||
let by = aux "by" conf.today.year in
|
||||
let bm = aux "bm" conf.today.month in
|
||||
let bd = aux "bd" conf.today.day in
|
||||
Output.print_sstring conf {|<form method="get" action="|};
|
||||
Output.print_sstring conf conf.command;
|
||||
Output.print_sstring conf {|"><p>|};
|
||||
Util.hidden_env conf;
|
||||
Util.hidden_input conf "m" (Adef.encoded "LD");
|
||||
Output.print_sstring conf
|
||||
@@ Printf.sprintf
|
||||
(fcapitale (ftransl conf "the latest %t deaths"))
|
||||
(fun _ ->
|
||||
{|<input name="k" value="|} ^ string_of_int len
|
||||
^ {|" size="4" maxlength="4">|});
|
||||
Output.print_sstring conf "\n... (";
|
||||
Output.print_sstring conf (transl conf "before");
|
||||
Output.print_sstring conf "...\n";
|
||||
let aux name value size =
|
||||
Output.print_sstring conf {|<input name="|};
|
||||
Output.print_sstring conf name;
|
||||
Output.print_sstring conf {|" value="|};
|
||||
Output.print_sstring conf value;
|
||||
Output.print_sstring conf {|" size="|};
|
||||
Output.print_sstring conf size;
|
||||
Output.print_sstring conf {|" maxlength="|};
|
||||
Output.print_sstring conf size;
|
||||
Output.print_sstring conf {|">|}
|
||||
in
|
||||
aux "by" by "4";
|
||||
aux "bm" bm "2";
|
||||
aux "bd" bd "2";
|
||||
Output.print_sstring conf ")";
|
||||
Output.print_sstring conf
|
||||
{|<button type="submit" class="btn btn-primary btn-lg">|};
|
||||
transl_nth conf "validate/delete" 0
|
||||
|> Utf8.capitalize_fst |> Output.print_sstring conf;
|
||||
Output.print_sstring conf "</button></p></form>");
|
||||
Hutil.trailer conf
|
||||
|
||||
let print_oldest_alive conf base =
|
||||
let limit = match p_getint conf.env "lim" with Some x -> x | _ -> 0 in
|
||||
let get_oldest_alive p =
|
||||
match get_death p with
|
||||
| NotDead -> Date.od_of_cdate (get_birth p)
|
||||
| DontKnowIfDead when limit > 0 -> (
|
||||
match Date.od_of_cdate (get_birth p) with
|
||||
| Some (Dgreg (d, _)) as x when conf.today.year - d.year <= limit -> x
|
||||
| Some _ | None -> None)
|
||||
| Death _ | DontKnowIfDead | DeadYoung | DeadDontKnowWhen | OfCourseDead ->
|
||||
None
|
||||
in
|
||||
let list, len = select_person conf base get_oldest_alive true in
|
||||
let title _ =
|
||||
Printf.sprintf
|
||||
(fcapitale (ftransl conf "the %d oldest perhaps still alive"))
|
||||
len
|
||||
|> Output.print_sstring conf
|
||||
in
|
||||
Hutil.header conf title;
|
||||
Hutil.print_link_to_welcome conf true;
|
||||
Output.print_sstring conf "<ul>\n";
|
||||
List.iter
|
||||
(fun (p, d, cal) ->
|
||||
Output.print_sstring conf "<li><b>";
|
||||
Output.print_string conf (referenced_person_text conf base p);
|
||||
Output.print_sstring conf "</b>, ";
|
||||
Output.print_sstring conf
|
||||
(transl_nth conf "born" (index_of_sex (get_sex p)));
|
||||
Output.print_sstring conf " <em>";
|
||||
Output.print_string conf
|
||||
(DateDisplay.string_of_ondate conf (Dgreg (d, cal)));
|
||||
Output.print_sstring conf "</em>";
|
||||
if get_death p = NotDead && d.prec = Sure then (
|
||||
let a = Date.time_elapsed d conf.today in
|
||||
Output.print_sstring conf " <em>(";
|
||||
Output.print_string conf (DateDisplay.string_of_age conf a);
|
||||
Output.print_sstring conf ")</em>");
|
||||
Output.print_sstring conf ".</li>")
|
||||
list;
|
||||
Output.print_sstring conf "</ul>";
|
||||
Hutil.trailer conf
|
||||
|
||||
let print_longest_lived conf base =
|
||||
let get_longest p =
|
||||
if Util.authorized_age conf base p then
|
||||
match (Date.cdate_to_dmy_opt (get_birth p), get_death p) with
|
||||
| Some bd, Death (_, cd) -> (
|
||||
match Date.cdate_to_dmy_opt cd with
|
||||
| None -> None
|
||||
| Some dd -> Some (Dgreg (Date.time_elapsed bd dd, Dgregorian)))
|
||||
| _ -> None
|
||||
else None
|
||||
in
|
||||
let list, len = select_person conf base get_longest false in
|
||||
let title _ =
|
||||
Printf.sprintf (fcapitale (ftransl conf "the %d who lived the longest")) len
|
||||
|> Output.print_sstring conf
|
||||
in
|
||||
Hutil.header conf title;
|
||||
Hutil.print_link_to_welcome conf true;
|
||||
Output.print_sstring conf "<ul>";
|
||||
List.iter
|
||||
(fun (p, d, _) ->
|
||||
Output.print_sstring conf "<li><strong>";
|
||||
Output.print_string conf (referenced_person_text conf base p);
|
||||
Output.print_sstring conf "</strong>";
|
||||
Output.print_string conf (DateDisplay.short_dates_text conf base p);
|
||||
Output.print_sstring conf " (";
|
||||
Output.print_sstring conf (string_of_int d.year);
|
||||
Output.print_sstring conf " ";
|
||||
Output.print_sstring conf (transl conf "years old");
|
||||
Output.print_sstring conf ")";
|
||||
Output.print_sstring conf ".";
|
||||
Output.print_sstring conf "</li>")
|
||||
list;
|
||||
Output.print_sstring conf "</ul>";
|
||||
Hutil.trailer conf
|
||||
|
||||
let print_marr_or_eng conf base title list =
|
||||
Hutil.header conf title;
|
||||
Hutil.print_link_to_welcome conf true;
|
||||
Output.print_sstring conf "<ul>\n";
|
||||
ignore
|
||||
@@ List.fold_left
|
||||
(fun (last_month_txt, was_future) (fam, d, cal) ->
|
||||
let month_txt, future =
|
||||
list_aux_1 conf d cal last_month_txt was_future
|
||||
in
|
||||
Output.print_sstring conf "<li><b>";
|
||||
Output.print_string conf
|
||||
(referenced_person_text conf base (pget conf base (get_father fam)));
|
||||
Output.print_sstring conf "</b> ";
|
||||
Output.print_sstring conf (transl_nth conf "and" 0);
|
||||
Output.print_sstring conf " <b>";
|
||||
Output.print_string conf
|
||||
(referenced_person_text conf base (pget conf base (get_mother fam)));
|
||||
Output.print_sstring conf "</b>, ";
|
||||
if future then (
|
||||
Output.print_sstring conf "<em>";
|
||||
Output.print_string conf
|
||||
(DateDisplay.string_of_date conf (Dgreg (d, cal)));
|
||||
Output.print_sstring conf "</em>")
|
||||
else (
|
||||
(match get_relation fam with
|
||||
| NotMarried | NoSexesCheckNotMarried ->
|
||||
Output.print_sstring conf
|
||||
@@ transl_nth conf "relation/relations" 0
|
||||
| Married | NoSexesCheckMarried ->
|
||||
Output.print_sstring conf @@ transl conf "married"
|
||||
| Engaged -> Output.print_sstring conf @@ transl conf "engaged"
|
||||
| MarriageBann | MarriageContract | MarriageLicense | Pacs
|
||||
| Residence | NoMention ->
|
||||
());
|
||||
Output.print_sstring conf " <em>";
|
||||
Output.print_string conf
|
||||
(DateDisplay.string_of_ondate conf (Dgreg (d, cal)));
|
||||
Output.print_sstring conf "</em>.");
|
||||
Output.print_sstring conf "</li>";
|
||||
(month_txt, future))
|
||||
(Adef.safe "", false)
|
||||
list;
|
||||
Output.print_sstring conf "</ul></li>";
|
||||
Hutil.trailer conf
|
||||
|
||||
let print_marriage conf base =
|
||||
let list, len =
|
||||
select_family conf base
|
||||
(fun fam ->
|
||||
let rel = get_relation fam in
|
||||
if rel = Married || rel = NoSexesCheckMarried then
|
||||
Date.od_of_cdate (get_marriage fam)
|
||||
else None)
|
||||
false
|
||||
in
|
||||
let title _ =
|
||||
Printf.sprintf (fcapitale (ftransl conf "the latest %d marriages")) len
|
||||
|> Output.print_sstring conf
|
||||
in
|
||||
print_marr_or_eng conf base title list
|
||||
|
||||
let print_oldest_engagements conf base =
|
||||
let list, len =
|
||||
select_family conf base
|
||||
(fun fam ->
|
||||
if get_relation fam = Engaged then
|
||||
let husb = pget conf base (get_father fam) in
|
||||
let wife = pget conf base (get_mother fam) in
|
||||
match (get_death husb, get_death wife) with
|
||||
| (NotDead | DontKnowIfDead), (NotDead | DontKnowIfDead) ->
|
||||
Date.od_of_cdate (get_marriage fam)
|
||||
| _ -> None
|
||||
else None)
|
||||
true
|
||||
in
|
||||
let title _ =
|
||||
Printf.sprintf
|
||||
(fcapitale
|
||||
(ftransl conf "the %d oldest couples perhaps still alive and engaged"))
|
||||
len
|
||||
|> Output.print_sstring conf
|
||||
in
|
||||
print_marr_or_eng conf base title list
|
||||
|
||||
let old_print_statistics conf =
|
||||
let title _ =
|
||||
transl conf "statistics" |> Utf8.capitalize_fst |> Output.print_sstring conf
|
||||
in
|
||||
let n =
|
||||
try int_of_string (List.assoc "latest_event" conf.base_env)
|
||||
with Not_found | Failure _ -> 20
|
||||
in
|
||||
Hutil.header conf title;
|
||||
Hutil.print_link_to_welcome conf true;
|
||||
Output.print_sstring conf "<ul>";
|
||||
let aux m label =
|
||||
Output.print_sstring conf {|<li><a href="|};
|
||||
Output.print_string conf (commd conf);
|
||||
Output.print_sstring conf {|m=|};
|
||||
Output.print_sstring conf m;
|
||||
Output.print_sstring conf {|&k=|};
|
||||
Output.print_sstring conf (string_of_int n);
|
||||
Output.print_sstring conf {|">|};
|
||||
Output.print_sstring conf (Printf.sprintf (ftransl conf label) n);
|
||||
Output.print_sstring conf {|</a></li>|}
|
||||
in
|
||||
if conf.wizard || conf.friend then (
|
||||
aux "LB" "the latest %d births";
|
||||
aux "LD" "the latest %d deaths";
|
||||
(* FIXME *)
|
||||
aux "LM" "the latest %d marriages";
|
||||
aux "OE" "the %d oldest couples perhaps still alive and engaged";
|
||||
aux "OA" "the %d oldest perhaps still alive");
|
||||
aux "LL" "the %d who lived the longest";
|
||||
Output.print_sstring conf "</ul>\n";
|
||||
Hutil.trailer conf
|
||||
|
||||
(* *)
|
||||
|
||||
type 'a env = Vother of 'a
|
||||
|
||||
let get_vother = function Vother x -> Some x
|
||||
let set_vother x = Vother x
|
||||
|
||||
let print_statistics conf =
|
||||
if p_getenv conf.env "old" = Some "on" then old_print_statistics conf
|
||||
else
|
||||
Hutil.interp conf "stats"
|
||||
{
|
||||
Templ.eval_var = (fun _ -> raise Not_found);
|
||||
Templ.eval_transl = (fun _ -> Templ.eval_transl conf);
|
||||
Templ.eval_predefined_apply = (fun _ -> raise Not_found);
|
||||
Templ.get_vother;
|
||||
Templ.set_vother;
|
||||
Templ.print_foreach = (fun _ -> raise Not_found);
|
||||
}
|
||||
[] ()
|
||||
|
||||
let print_population_pyramid conf base =
|
||||
let interval =
|
||||
match p_getint conf.env "int" with Some i -> max 1 i | None -> 5
|
||||
in
|
||||
let limit = match p_getint conf.env "lim" with Some x -> x | _ -> 0 in
|
||||
let at_date =
|
||||
match p_getint conf.env "y" with
|
||||
| Some i -> { year = i; month = 31; day = 12; prec = Sure; delta = 0 }
|
||||
| None -> conf.today
|
||||
in
|
||||
let nb_intervals = 150 / interval in
|
||||
let men, wom =
|
||||
make_population_pyramid ~nb_intervals ~interval ~limit ~at_date conf base
|
||||
in
|
||||
let at_year = at_date.year in
|
||||
let string_of_nb n =
|
||||
Mutil.string_of_int_sep (transl conf "(thousand separator)") n
|
||||
in
|
||||
let title _ =
|
||||
transl conf "population pyramid"
|
||||
|> Utf8.capitalize_fst |> Output.print_sstring conf;
|
||||
Output.print_sstring conf " (";
|
||||
Output.print_sstring conf (string_of_int at_year);
|
||||
Output.print_sstring conf ")"
|
||||
in
|
||||
let print_image doit sex iname =
|
||||
Output.print_sstring conf "<td>";
|
||||
if doit then (
|
||||
Output.print_sstring conf {|<img src="|};
|
||||
Output.print_sstring conf (Util.images_prefix conf);
|
||||
Output.print_sstring conf "/";
|
||||
Output.print_string conf iname;
|
||||
Output.print_sstring conf {|" alt="|};
|
||||
Output.print_sstring conf (transl_nth conf "M/F" sex);
|
||||
Output.print_sstring conf {|" title="|};
|
||||
Output.print_sstring conf (transl_nth conf "M/F" sex);
|
||||
Output.print_sstring conf {|">|})
|
||||
else Output.print_sstring conf " ";
|
||||
Output.print_sstring conf "</td>"
|
||||
in
|
||||
Hutil.header conf title;
|
||||
Hutil.print_link_to_welcome conf true;
|
||||
let max_hum =
|
||||
let max_men = Array.fold_left max 0 men in
|
||||
let max_wom = Array.fold_left max 0 wom in
|
||||
max 1 (max max_men max_wom)
|
||||
in
|
||||
let max_size = 70 in
|
||||
let band_size n = ((2 * max_size * n) + max_hum) / (2 * max_hum) in
|
||||
let first_interv =
|
||||
let rec loop i =
|
||||
if i <= 0 then 0
|
||||
else if men.(i) > 0 || wom.(i) > 0 then i
|
||||
else loop (i - 1)
|
||||
in
|
||||
loop nb_intervals
|
||||
in
|
||||
Output.print_sstring conf "<div>\n";
|
||||
Output.print_sstring conf {|<table id="table_pop_pyr" border="|};
|
||||
Output.print_sstring conf (string_of_int conf.border);
|
||||
Output.print_sstring conf
|
||||
{|" cellspacing="0" cellpadding="0" style="margin:auto">|};
|
||||
for i = first_interv downto 0 do
|
||||
let nb_men = men.(i) in
|
||||
let nb_wom = wom.(i) in
|
||||
Output.print_sstring conf "<tr><td class=\"pyramid_year\">";
|
||||
Output.print_sstring conf (string_of_int @@ (at_year - (i * interval)));
|
||||
Output.print_sstring conf "</td><td> </td>";
|
||||
print_image (i = 0) 0 (Adef.safe "male.png");
|
||||
Output.print_sstring conf "<td> </td><td align=\"right\">\n";
|
||||
Output.printf conf
|
||||
{|<table cellspacing="0" cellpadding="0"><tr><td class="pyramid_nb">|};
|
||||
if nb_men <> 0 then Output.print_sstring conf (string_of_int nb_men);
|
||||
Output.print_sstring conf " </td><td>";
|
||||
let aux_img nb img =
|
||||
if nb <> 0 then (
|
||||
let n = max 1 (band_size nb) in
|
||||
Output.print_sstring conf {|<img src="|};
|
||||
Output.print_string conf img;
|
||||
Output.print_sstring conf {|" width="|};
|
||||
Output.print_sstring conf (string_of_int @@ (n * 3));
|
||||
Output.print_sstring conf {|" height="22">|})
|
||||
in
|
||||
aux_img nb_men
|
||||
(Adef.encoded (Filename.concat (Util.images_prefix conf) "pyr_male.png"));
|
||||
Output.print_sstring conf
|
||||
{|</td></tr></table></td><td align="center" class="pyramid_center">|};
|
||||
if i = nb_intervals then Output.print_sstring conf " "
|
||||
else Output.print_sstring conf (string_of_int @@ ((i + 1) * interval));
|
||||
Output.print_sstring conf
|
||||
{|</td><td align="left"><table cellspacing="0" cellpadding="0"><tr><td>|};
|
||||
aux_img nb_wom
|
||||
(Adef.encoded
|
||||
(Filename.concat (Util.images_prefix conf) "pyr_female.png"));
|
||||
Output.print_sstring conf {|</td><td class="pyramid_nb"> |};
|
||||
if nb_wom <> 0 then Output.print_sstring conf (string_of_int nb_wom);
|
||||
Output.print_sstring conf "</td></tr></table></td><td> </td>\n";
|
||||
print_image (i = 0) 1 (Adef.safe "female.png");
|
||||
Output.print_sstring conf {|<td> </td><td class="pyramid_year">|};
|
||||
Output.print_sstring conf (string_of_int @@ (at_year - (i * interval)));
|
||||
Output.print_sstring conf "</td></tr>"
|
||||
done;
|
||||
Output.print_sstring conf "</table>";
|
||||
Output.print_sstring conf "</div><p>";
|
||||
let sum_men = Array.fold_left ( + ) 0 men in
|
||||
let sum_wom = Array.fold_left ( + ) 0 wom in
|
||||
transl conf "number of living persons"
|
||||
|> Utf8.capitalize_fst |> Output.print_sstring conf;
|
||||
Output.print_sstring conf (transl conf ":");
|
||||
Output.print_sstring conf " ";
|
||||
Output.print_sstring conf (string_of_nb (sum_men + sum_wom));
|
||||
Output.print_sstring conf {|.</p><form method="get" action="|};
|
||||
Output.print_string conf (commd conf);
|
||||
Output.print_sstring conf {|"><div class="form-inline">|};
|
||||
hidden_env conf;
|
||||
Util.hidden_input conf "m" (Adef.encoded "POP_PYR");
|
||||
Output.print_sstring conf {|<label for="yr">|};
|
||||
transl_nth conf "year/month/day" 0
|
||||
|> Utf8.capitalize_fst |> Output.print_sstring conf;
|
||||
Output.print_sstring conf "</label>";
|
||||
Output.print_sstring conf {|<input type="number" id="yr" name="y" value="|};
|
||||
Output.print_sstring conf (string_of_int at_year);
|
||||
Output.print_sstring conf
|
||||
{|" class="form-control col-1 ml-2" step="1">
|
||||
<label for="int" class="ml-3">|};
|
||||
transl conf "interval" |> Utf8.capitalize_fst |> Output.print_sstring conf;
|
||||
Output.print_sstring conf
|
||||
{|</label><input type="number" id="int" name="int" value="|};
|
||||
Output.print_sstring conf (string_of_int interval);
|
||||
Output.print_sstring conf
|
||||
{|" class="form-control col-1 ml-2" step="1" min="0" max="130">
|
||||
<label for="lim" class="ml-3">|};
|
||||
transl conf "limit" |> Utf8.capitalize_fst |> Output.print_sstring conf;
|
||||
Output.print_sstring conf
|
||||
{|</label><input type="number" id="lim" name="lim" value="|};
|
||||
Output.print_sstring conf (string_of_int limit);
|
||||
Output.print_sstring conf
|
||||
{|" class="form-control col-1 ml-2" step="1" min="0" max="|};
|
||||
Output.print_sstring conf (string_of_nb (sum_men + sum_wom));
|
||||
Output.print_sstring conf
|
||||
{|"><button type="submit" class="btn btn-primary ml-3">OK</button>
|
||||
</div></form>|};
|
||||
Hutil.trailer conf
|
||||
26
lib/birthDeathDisplay.mli
Normal file
26
lib/birthDeathDisplay.mli
Normal file
@@ -0,0 +1,26 @@
|
||||
val print_birth : Config.config -> Gwdb.base -> unit
|
||||
(** Lists the last births *)
|
||||
|
||||
val print_death : Config.config -> Gwdb.base -> unit
|
||||
(** Lists the last deaths *)
|
||||
|
||||
val print_longest_lived : Config.config -> Gwdb.base -> unit
|
||||
(** Lists the persons who lived the longest *)
|
||||
|
||||
val print_oldest_alive : Config.config -> Gwdb.base -> unit
|
||||
(** Displays the list of the oldest persons that are still alive or, if unknown,
|
||||
whose death are not probable *)
|
||||
|
||||
val print_marriage : Config.config -> Gwdb.base -> unit
|
||||
(** Lists the last marriages *)
|
||||
|
||||
val print_oldest_engagements : Config.config -> Gwdb.base -> unit
|
||||
(** Displays the list of the oldest couples that still exist *)
|
||||
|
||||
val print_statistics : Config.config -> unit
|
||||
(** Displays several links for statistics: latest births, death, marriages, the
|
||||
oldest couples, persons that are alive and who lived the longest, as well as
|
||||
a population pyramid *)
|
||||
|
||||
val print_population_pyramid : Config.config -> Gwdb.base -> unit
|
||||
(** Displays a population pyramid from the base data *)
|
||||
603
lib/birthdayDisplay.ml
Normal file
603
lib/birthdayDisplay.ml
Normal file
@@ -0,0 +1,603 @@
|
||||
(* Copyright (c) 1998-2007 INRIA *)
|
||||
|
||||
open Config
|
||||
open Def
|
||||
open Gwdb
|
||||
open Util
|
||||
|
||||
type date_event = DeBirth | DeDeath of death_reason
|
||||
|
||||
let print_age conf a_ref a =
|
||||
Output.print_sstring conf " <em>";
|
||||
Output.print_sstring conf (string_of_int a);
|
||||
Output.print_sstring conf "</em>";
|
||||
Output.print_sstring conf ", ";
|
||||
match a_ref - a with
|
||||
| 0 -> Output.print_sstring conf (transl conf "birth")
|
||||
| 1 -> Output.print_sstring conf (transl conf "one year old")
|
||||
| n ->
|
||||
Output.print_sstring conf (string_of_int n);
|
||||
Output.print_sstring conf " ";
|
||||
Output.print_sstring conf (transl conf "years old")
|
||||
|
||||
let print_anniversary_day conf base dead_people liste =
|
||||
let a_ref = conf.today.year in
|
||||
Output.print_sstring conf "<ul>";
|
||||
List.iter
|
||||
(fun (p, a, date_event, txt_of) ->
|
||||
let is = index_of_sex (get_sex p) in
|
||||
Output.print_sstring conf "<li>";
|
||||
Output.print_string conf (txt_of conf base p);
|
||||
if not dead_people then print_age conf a_ref a
|
||||
else (
|
||||
Output.print_sstring conf ", <em>";
|
||||
(Output.print_sstring conf
|
||||
@@
|
||||
match date_event with
|
||||
| DeBirth -> transl_nth conf "born" is
|
||||
| DeDeath Unspecified -> transl_nth conf "died" is
|
||||
| DeDeath Killed -> transl_nth conf "killed (in action)" is
|
||||
| DeDeath Murdered -> transl_nth conf "murdered" is
|
||||
| DeDeath Executed -> transl_nth conf "executed (legally killed)" is
|
||||
| DeDeath Disappeared -> transl_nth conf "disappeared" is);
|
||||
Output.print_sstring conf " ";
|
||||
Output.print_sstring conf (transl conf "in (year)");
|
||||
Output.print_sstring conf " ";
|
||||
Output.print_sstring conf (string_of_int a);
|
||||
Output.print_sstring conf "</em>");
|
||||
Output.print_sstring conf "</li>")
|
||||
liste;
|
||||
Output.print_sstring conf "</ul>"
|
||||
|
||||
let gen_print conf base mois f_scan dead_people =
|
||||
let tab = Array.make 31 [] in
|
||||
let title _ =
|
||||
let lab =
|
||||
if dead_people then transl conf "anniversaries"
|
||||
else transl conf "birthdays"
|
||||
in
|
||||
Output.printf conf "%s %s" (Utf8.capitalize_fst lab)
|
||||
(Util.translate_eval (transl_nth conf "(month)" (mois - 1)))
|
||||
in
|
||||
(try
|
||||
while true do
|
||||
let p, txt_of = f_scan () in
|
||||
if not dead_people then
|
||||
match (Date.cdate_to_dmy_opt (get_birth p), get_death p) with
|
||||
| Some d, (NotDead | DontKnowIfDead) ->
|
||||
if
|
||||
d.prec = Sure && d.day <> 0 && d.month <> 0 && d.month = mois
|
||||
&& d.delta = 0
|
||||
then
|
||||
if authorized_age conf base p then
|
||||
let j = d.day in
|
||||
tab.(pred j) <- (p, d.year, DeBirth, txt_of) :: tab.(pred j)
|
||||
| _ -> ()
|
||||
else
|
||||
match get_death p with
|
||||
| NotDead | DontKnowIfDead -> ()
|
||||
| Death _ | DeadYoung | DeadDontKnowWhen | OfCourseDead -> (
|
||||
(match Date.cdate_to_dmy_opt (get_birth p) with
|
||||
| None -> ()
|
||||
| Some dt ->
|
||||
if
|
||||
dt.prec = Sure && dt.day <> 0 && dt.month <> 0
|
||||
&& dt.month = mois && dt.delta = 0
|
||||
then
|
||||
if authorized_age conf base p then
|
||||
let j = dt.day in
|
||||
tab.(pred j) <-
|
||||
(p, dt.year, DeBirth, txt_of) :: tab.(pred j));
|
||||
match get_death p with
|
||||
| NotDead | DeadYoung | DeadDontKnowWhen | DontKnowIfDead
|
||||
| OfCourseDead ->
|
||||
()
|
||||
| Death (dr, d) -> (
|
||||
match Date.cdate_to_dmy_opt d with
|
||||
| None -> ()
|
||||
| Some dt ->
|
||||
if
|
||||
dt.prec = Sure && dt.day <> 0 && dt.month <> 0
|
||||
&& dt.month = mois && dt.delta = 0
|
||||
then
|
||||
if authorized_age conf base p then
|
||||
let j = dt.day in
|
||||
let a = dt.year in
|
||||
tab.(pred j) <-
|
||||
(p, a, DeDeath dr, txt_of) :: tab.(pred j)))
|
||||
done
|
||||
with Not_found -> ());
|
||||
Hutil.header conf title;
|
||||
if Array.for_all (( = ) []) tab then (
|
||||
Output.print_sstring conf "<p>\n";
|
||||
Output.printf conf "%s.\n"
|
||||
(Utf8.capitalize_fst (transl conf "no anniversary"));
|
||||
Output.print_sstring conf "</p>\n");
|
||||
Output.print_sstring conf "<ul>\n";
|
||||
for j = 1 to 31 do
|
||||
if tab.(pred j) <> [] then (
|
||||
Output.print_sstring conf "<li>\n";
|
||||
Output.printf conf "%d\n" j;
|
||||
(let liste =
|
||||
List.sort
|
||||
(fun (_, a1, _, _) (_, a2, _, _) -> compare a1 a2)
|
||||
tab.(pred j)
|
||||
in
|
||||
print_anniversary_day conf base dead_people liste);
|
||||
Output.print_sstring conf "</li>\n")
|
||||
done;
|
||||
Output.print_sstring conf "</ul>\n";
|
||||
Hutil.trailer conf
|
||||
|
||||
let print_anniversary_list conf base dead_people dt liste =
|
||||
let a_ref = dt.year in
|
||||
Output.print_sstring conf "<ul>\n";
|
||||
List.iter
|
||||
(fun (p, a, date_event, txt_of) ->
|
||||
Output.print_sstring conf "<li>";
|
||||
if dead_people then (
|
||||
Output.print_sstring conf "<em>";
|
||||
(match date_event with
|
||||
| DeBirth -> Output.print_sstring conf (transl conf "birth")
|
||||
| DeDeath _ -> Output.print_sstring conf (transl conf "death"));
|
||||
Output.print_sstring conf "</em> -> ";
|
||||
Output.print_string conf (txt_of conf base p);
|
||||
Output.print_sstring conf " <em>";
|
||||
Output.print_sstring conf (transl conf "in (year)");
|
||||
Output.print_sstring conf " ";
|
||||
Output.print_sstring conf (string_of_int a);
|
||||
Output.print_sstring conf " (";
|
||||
Output.print_sstring conf
|
||||
(Printf.sprintf (ftransl conf "%d years ago") (conf.today.year - a));
|
||||
Output.print_sstring conf ")</em>")
|
||||
else (
|
||||
Output.print_string conf (txt_of conf base p);
|
||||
(* TODO year of birth *)
|
||||
match get_death p with NotDead -> print_age conf a_ref a | _ -> ());
|
||||
Output.print_sstring conf "</li>")
|
||||
liste;
|
||||
Output.print_sstring conf "</ul>"
|
||||
|
||||
let f_scan conf base =
|
||||
let next = Gwdb.Collection.iterator (Gwdb.ipers base) in
|
||||
fun () ->
|
||||
match next () with
|
||||
| Some i -> (pget conf base i, referenced_person_title_text)
|
||||
| None -> raise Not_found
|
||||
|
||||
let print_birth conf base mois =
|
||||
gen_print conf base mois (f_scan conf base) false
|
||||
|
||||
let print_dead conf base mois = gen_print conf base mois (f_scan conf base) true
|
||||
|
||||
let print_birth_day conf base day_name fphrase wd dt list =
|
||||
match list with
|
||||
| [] ->
|
||||
Output.print_sstring conf "<p>";
|
||||
Output.print_sstring conf
|
||||
(Utf8.capitalize_fst (transl conf "no birthday"));
|
||||
Output.print_sstring conf " ";
|
||||
Output.print_string conf day_name;
|
||||
Output.print_sstring conf ".</p>"
|
||||
| _ ->
|
||||
Output.print_sstring conf "<p>\n";
|
||||
let txt =
|
||||
transl_nth conf "(week day)" wd ^ " " ^ DateDisplay.code_dmy conf dt
|
||||
|> transl_decline conf "on (weekday day month year)"
|
||||
|> Adef.safe
|
||||
in
|
||||
Output.printf conf fphrase
|
||||
(Utf8.capitalize_fst (day_name : Adef.safe_string :> string)
|
||||
^<^ ",\n"
|
||||
^<^ std_color conf ("<b>" ^<^ txt ^>^ "</b>")
|
||||
: Adef.safe_string
|
||||
:> string)
|
||||
(transl conf "the birthday");
|
||||
Output.print_sstring conf "...</p>";
|
||||
print_anniversary_list conf base false dt list
|
||||
|
||||
let propose_months conf mode =
|
||||
begin_centered conf;
|
||||
Output.print_sstring conf "<span>";
|
||||
transl conf "select a month to see all the anniversaries"
|
||||
|> Utf8.capitalize_fst |> Output.print_sstring conf;
|
||||
Output.print_sstring conf "</span>";
|
||||
Output.print_sstring conf {|<table border="|};
|
||||
Output.print_sstring conf (string_of_int conf.border);
|
||||
Output.print_sstring conf {|"><tr><td>|};
|
||||
Output.print_sstring conf {|<form class="form-inline" method="get" action="|};
|
||||
Output.print_sstring conf conf.command;
|
||||
Output.print_sstring conf {|"><p>|};
|
||||
Util.hidden_env conf;
|
||||
mode ();
|
||||
Output.print_sstring conf
|
||||
{|<select class="form-control form-control-lg" name="v">|};
|
||||
for i = 1 to 12 do
|
||||
Output.print_sstring conf {|<option value="|};
|
||||
Output.print_sstring conf (string_of_int i);
|
||||
Output.print_sstring conf {|"|};
|
||||
Output.print_sstring conf
|
||||
(if i = conf.today.month then {| selected="selected">|} else ">");
|
||||
transl_nth conf "(month)" (i - 1)
|
||||
|> Util.translate_eval |> Utf8.capitalize_fst |> Output.print_sstring conf;
|
||||
Output.print_sstring conf "</option>"
|
||||
done;
|
||||
Output.print_sstring conf "</select>";
|
||||
Output.print_sstring conf
|
||||
{|<button type="submit" class="btn btn-primary btn-lg ml-2">|};
|
||||
transl_nth conf "validate/delete" 0
|
||||
|> Utf8.capitalize_fst |> Output.print_sstring conf;
|
||||
Output.print_sstring conf "</button></p></form></td></tr></table>";
|
||||
end_centered conf
|
||||
|
||||
let day_after d =
|
||||
let day, r =
|
||||
if d.day >= Date.nb_days_in_month d.month d.year then (1, 1)
|
||||
else (succ d.day, 0)
|
||||
in
|
||||
let month, r = if d.month + r > 12 then (1, 1) else (d.month + r, 0) in
|
||||
let year = d.year + r in
|
||||
{ day; month; year; prec = Sure; delta = 0 }
|
||||
|
||||
let print_anniv conf base day_name fphrase wd dt = function
|
||||
| [] ->
|
||||
Output.print_sstring conf "<p>";
|
||||
transl conf "no anniversary"
|
||||
|> Utf8.capitalize_fst |> Output.print_sstring conf;
|
||||
Output.print_sstring conf " ";
|
||||
Output.print_string conf day_name;
|
||||
Output.print_sstring conf ".</p>"
|
||||
| list ->
|
||||
Output.print_sstring conf "<p>";
|
||||
let txt =
|
||||
transl_nth conf "(week day)" wd ^ " " ^ DateDisplay.code_dmy conf dt
|
||||
|> transl_decline conf "on (weekday day month year)"
|
||||
|> Adef.safe
|
||||
in
|
||||
Output.printf conf fphrase
|
||||
(Utf8.capitalize_fst (day_name : Adef.safe_string :> string)
|
||||
^<^ ",\n"
|
||||
^<^ std_color conf ("<b>" ^<^ txt ^>^ "</b>")
|
||||
: Adef.safe_string
|
||||
:> string)
|
||||
(transl conf "the anniversary");
|
||||
Output.print_sstring conf "...</p>";
|
||||
print_anniversary_list conf base true dt list
|
||||
|
||||
let list_aux conf base list cb =
|
||||
Output.print_sstring conf "<ul>";
|
||||
List.iter
|
||||
(fun (fam, year) ->
|
||||
Output.print_sstring conf "<li>";
|
||||
Output.print_string conf
|
||||
(referenced_person_title_text conf base
|
||||
(pget conf base (get_father fam)));
|
||||
Output.print_sstring conf " ";
|
||||
Output.print_sstring conf (transl_nth conf "and" 0);
|
||||
Output.print_sstring conf " ";
|
||||
Output.print_string conf
|
||||
(referenced_person_title_text conf base
|
||||
(pget conf base (get_mother fam)));
|
||||
Output.print_sstring conf ", <em>";
|
||||
Output.print_sstring conf (transl conf "in (year)");
|
||||
Output.print_sstring conf " ";
|
||||
Output.print_sstring conf (string_of_int year);
|
||||
cb conf year;
|
||||
Output.print_sstring conf "</em></li>")
|
||||
list;
|
||||
Output.print_sstring conf "</ul>"
|
||||
|
||||
let print_marriage conf base month =
|
||||
let title _ =
|
||||
let lab = transl conf "anniversaries of marriage" in
|
||||
Output.printf conf "%s %s" (Utf8.capitalize_fst lab)
|
||||
(transl_decline conf "in (month year)"
|
||||
(transl_nth conf "(month)" (month - 1)))
|
||||
in
|
||||
let tab = Array.make 31 [] in
|
||||
Hutil.header conf title;
|
||||
Gwdb.Collection.iter
|
||||
(fun ifam ->
|
||||
let fam = foi base ifam in
|
||||
match Date.cdate_to_dmy_opt (get_marriage fam) with
|
||||
| Some { day = d; month = m; year = y; prec = Sure } when d <> 0 && m <> 0
|
||||
->
|
||||
let father = pget conf base (get_father fam) in
|
||||
let mother = pget conf base (get_mother fam) in
|
||||
if
|
||||
m = month
|
||||
&& authorized_age conf base father
|
||||
&& (not (is_hidden father))
|
||||
&& authorized_age conf base mother
|
||||
&& not (is_hidden mother)
|
||||
then tab.(pred d) <- (fam, y) :: tab.(pred d)
|
||||
| _ -> ())
|
||||
(Gwdb.ifams base);
|
||||
Output.print_sstring conf "<ul>";
|
||||
for i = 1 to 31 do
|
||||
match tab.(i - 1) with
|
||||
| [] -> ()
|
||||
| list ->
|
||||
let list = List.sort (fun (_, y1) (_, y2) -> compare y1 y2) list in
|
||||
Output.print_sstring conf " <li>";
|
||||
Output.print_sstring conf (string_of_int i);
|
||||
list_aux conf base list (fun _ _ -> ());
|
||||
Output.print_sstring conf " </li>"
|
||||
done;
|
||||
Output.print_sstring conf "</ul>";
|
||||
Hutil.trailer conf
|
||||
|
||||
let print_anniversaries_of_marriage conf base list =
|
||||
list_aux conf base list (fun conf year ->
|
||||
Output.print_sstring conf " (";
|
||||
Printf.sprintf (ftransl conf "%d years ago") (conf.today.year - year)
|
||||
|> Output.print_sstring conf;
|
||||
Output.print_sstring conf ")")
|
||||
|
||||
let print_marriage_day conf base day_name fphrase wd dt = function
|
||||
| [] ->
|
||||
Output.print_sstring conf "<p>";
|
||||
transl conf "no anniversary"
|
||||
|> Utf8.capitalize_fst |> Output.print_sstring conf;
|
||||
Output.print_sstring conf " ";
|
||||
Output.print_string conf day_name;
|
||||
Output.print_sstring conf ".</p>"
|
||||
| list ->
|
||||
Output.print_sstring conf "<p>";
|
||||
Output.printf conf fphrase
|
||||
(Utf8.capitalize_fst (day_name : Adef.safe_string :> string)
|
||||
^<^ ",\n"
|
||||
^<^ std_color conf
|
||||
("<b>"
|
||||
^ transl_decline conf "on (weekday day month year)"
|
||||
(transl_nth conf "(week day)" wd
|
||||
^ " "
|
||||
^ DateDisplay.code_dmy conf dt)
|
||||
^ "</b>"
|
||||
|> Adef.safe)
|
||||
: Adef.safe_string
|
||||
:> string)
|
||||
(transl conf "the anniversary of marriage");
|
||||
Output.print_sstring conf "...</p>";
|
||||
print_anniversaries_of_marriage conf base list
|
||||
|
||||
let match_dates conf base p d1 d2 =
|
||||
if d1.day = d2.day && d1.month = d2.month then authorized_age conf base p
|
||||
else if
|
||||
d1.day = 29 && d1.month = 2 && d2.day = 1 && d2.month = 3
|
||||
&& not (Date.leap_year d2.year)
|
||||
then authorized_age conf base p
|
||||
else false
|
||||
|
||||
let gen_print_menu_birth conf base f_scan mode =
|
||||
let title _ =
|
||||
transl conf "birthdays" |> Utf8.capitalize_fst |> Output.print_sstring conf
|
||||
in
|
||||
let tom = day_after conf.today in
|
||||
let aft = day_after tom in
|
||||
let list_tod = ref [] in
|
||||
let list_tom = ref [] in
|
||||
let list_aft = ref [] in
|
||||
(match Util.find_person_in_env conf base "" with
|
||||
| Some p ->
|
||||
Perso.interp_notempl_with_menu title "perso_header" conf base p;
|
||||
Output.print_sstring conf "<h2>";
|
||||
title false;
|
||||
Output.print_sstring conf "</h2>"
|
||||
| None -> Hutil.header conf title);
|
||||
(try
|
||||
while true do
|
||||
let p, txt_of = f_scan () in
|
||||
match (Date.cdate_to_dmy_opt (get_birth p), get_death p) with
|
||||
| Some d, (NotDead | DontKnowIfDead) ->
|
||||
if d.prec = Sure && d.day <> 0 && d.month <> 0 then
|
||||
if match_dates conf base p d conf.today then
|
||||
list_tod := (p, d.year, DeBirth, txt_of) :: !list_tod
|
||||
else if match_dates conf base p d tom then
|
||||
list_tom := (p, d.year, DeBirth, txt_of) :: !list_tom
|
||||
else if match_dates conf base p d aft then
|
||||
list_aft := (p, d.year, DeBirth, txt_of) :: !list_aft
|
||||
| _ -> ()
|
||||
done
|
||||
with Not_found -> ());
|
||||
List.iter
|
||||
(fun xx ->
|
||||
xx := List.sort (fun (_, a1, _, _) (_, a2, _, _) -> compare a1 a2) !xx)
|
||||
[ list_tod; list_tom; list_aft ];
|
||||
print_birth_day conf base
|
||||
(transl conf "today" |> Adef.safe)
|
||||
(ftransl conf "%s, it is %s of")
|
||||
conf.today_wd conf.today !list_tod;
|
||||
print_birth_day conf base
|
||||
(transl conf "tomorrow" |> Adef.safe)
|
||||
(ftransl conf "%s, it will be %s of")
|
||||
((conf.today_wd + 1) mod 7)
|
||||
tom !list_tom;
|
||||
print_birth_day conf base
|
||||
(transl conf "the day after tomorrow" |> Adef.safe)
|
||||
(ftransl conf "%s, it will be %s of")
|
||||
((conf.today_wd + 2) mod 7)
|
||||
aft !list_aft;
|
||||
Output.print_sstring conf " ";
|
||||
propose_months conf mode;
|
||||
Output.print_sstring conf " ";
|
||||
Hutil.trailer conf
|
||||
|
||||
let print_menu_birth conf base =
|
||||
let f_scan =
|
||||
let next = Gwdb.Collection.iterator (Gwdb.ipers base) in
|
||||
fun () ->
|
||||
match next () with
|
||||
| Some i -> (pget conf base i, referenced_person_title_text)
|
||||
| None -> raise Not_found
|
||||
in
|
||||
let mode () =
|
||||
Output.print_sstring conf
|
||||
"<input type=\"hidden\" name=\"m\" value=\"AN\">\n"
|
||||
in
|
||||
gen_print_menu_birth conf base f_scan mode
|
||||
|
||||
let gen_print_menu_dead conf base f_scan mode =
|
||||
let title _ =
|
||||
transl conf "anniversaries of dead people"
|
||||
|> Utf8.capitalize_fst |> Output.print_sstring conf
|
||||
in
|
||||
let tom = day_after conf.today in
|
||||
let aft = day_after tom in
|
||||
let list_tod = ref [] in
|
||||
let list_tom = ref [] in
|
||||
let list_aft = ref [] in
|
||||
Hutil.header conf title;
|
||||
(try
|
||||
while true do
|
||||
let p, txt_of = f_scan () in
|
||||
match get_death p with
|
||||
| NotDead | DontKnowIfDead -> ()
|
||||
| Death _ | DeadYoung | DeadDontKnowWhen | OfCourseDead -> (
|
||||
(match Date.cdate_to_dmy_opt (get_birth p) with
|
||||
| None -> ()
|
||||
| Some d ->
|
||||
if d.prec = Sure && d.day <> 0 && d.month <> 0 then
|
||||
if match_dates conf base p d conf.today then
|
||||
list_tod := (p, d.year, DeBirth, txt_of) :: !list_tod
|
||||
else if match_dates conf base p d tom then
|
||||
list_tom := (p, d.year, DeBirth, txt_of) :: !list_tom
|
||||
else if match_dates conf base p d aft then
|
||||
list_aft := (p, d.year, DeBirth, txt_of) :: !list_aft);
|
||||
match get_death p with
|
||||
| Death (dr, d) -> (
|
||||
match Date.cdate_to_dmy_opt d with
|
||||
| None -> ()
|
||||
| Some d ->
|
||||
if d.prec = Sure && d.day <> 0 && d.month <> 0 then
|
||||
if match_dates conf base p d conf.today then
|
||||
list_tod := (p, d.year, DeDeath dr, txt_of) :: !list_tod
|
||||
else if match_dates conf base p d tom then
|
||||
list_tom := (p, d.year, DeDeath dr, txt_of) :: !list_tom
|
||||
else if match_dates conf base p d aft then
|
||||
list_aft := (p, d.year, DeDeath dr, txt_of) :: !list_aft)
|
||||
| NotDead | DeadYoung | DeadDontKnowWhen | DontKnowIfDead
|
||||
| OfCourseDead ->
|
||||
())
|
||||
done
|
||||
with Not_found -> ());
|
||||
List.iter
|
||||
(fun xx ->
|
||||
xx := List.sort (fun (_, a1, _, _) (_, a2, _, _) -> compare a1 a2) !xx)
|
||||
[ list_tod; list_tom; list_aft ];
|
||||
print_anniv conf base
|
||||
(transl conf "today" |> Adef.safe)
|
||||
(ftransl conf "%s, it is %s of")
|
||||
conf.today_wd conf.today !list_tod;
|
||||
print_anniv conf base
|
||||
(transl conf "tomorrow" |> Adef.safe)
|
||||
(ftransl conf "%s, it will be %s of")
|
||||
((conf.today_wd + 1) mod 7)
|
||||
tom !list_tom;
|
||||
print_anniv conf base
|
||||
(transl conf "the day after tomorrow" |> Adef.safe)
|
||||
(ftransl conf "%s, it will be %s of")
|
||||
((conf.today_wd + 2) mod 7)
|
||||
aft !list_aft;
|
||||
Output.print_sstring conf "\n";
|
||||
propose_months conf mode;
|
||||
Output.print_sstring conf "\n";
|
||||
Hutil.trailer conf
|
||||
|
||||
let print_menu_dead conf base =
|
||||
let f_scan =
|
||||
let next = Gwdb.Collection.iterator (Gwdb.ipers base) in
|
||||
fun () ->
|
||||
match next () with
|
||||
| Some i -> (pget conf base i, referenced_person_title_text)
|
||||
| None -> raise Not_found
|
||||
in
|
||||
gen_print_menu_dead conf base f_scan (fun () ->
|
||||
Util.hidden_input conf "m" @@ Adef.encoded "AD")
|
||||
|
||||
let match_mar_dates conf base cpl d1 d2 =
|
||||
if d1.day = d2.day && d1.month = d2.month then
|
||||
authorized_age conf base (pget conf base (get_father cpl))
|
||||
&& authorized_age conf base (pget conf base (get_mother cpl))
|
||||
else if
|
||||
d1.day = 29 && d1.month = 2 && d2.day = 1 && d2.month = 3
|
||||
&& not (Date.leap_year d2.year)
|
||||
then
|
||||
authorized_age conf base (pget conf base (get_father cpl))
|
||||
&& authorized_age conf base (pget conf base (get_mother cpl))
|
||||
else false
|
||||
|
||||
let print_menu_marriage conf base =
|
||||
let title _ =
|
||||
transl conf "anniversaries of marriage"
|
||||
|> Utf8.capitalize_fst |> Output.print_sstring conf
|
||||
in
|
||||
let tom = day_after conf.today in
|
||||
let aft = day_after tom in
|
||||
let list_tod = ref [] in
|
||||
let list_tom = ref [] in
|
||||
let list_aft = ref [] in
|
||||
Hutil.header conf title;
|
||||
Gwdb.Collection.iter
|
||||
(fun ifam ->
|
||||
let fam = foi base ifam in
|
||||
match (Date.cdate_to_dmy_opt (get_marriage fam), get_divorce fam) with
|
||||
| Some d, NotDivorced when d.day <> 0 && d.month <> 0 && d.prec = Sure ->
|
||||
let update_list cpl =
|
||||
if match_mar_dates conf base cpl d conf.today then
|
||||
list_tod := (cpl, d.year) :: !list_tod
|
||||
else if match_mar_dates conf base cpl d tom then
|
||||
list_tom := (cpl, d.year) :: !list_tom
|
||||
else if match_mar_dates conf base cpl d aft then
|
||||
list_aft := (cpl, d.year) :: !list_aft
|
||||
in
|
||||
if conf.use_restrict then (
|
||||
let father = pget conf base (get_father fam) in
|
||||
let mother = pget conf base (get_mother fam) in
|
||||
if (not (is_hidden father)) && not (is_hidden mother) then
|
||||
update_list fam)
|
||||
else update_list fam
|
||||
| _ -> ())
|
||||
(Gwdb.ifams base);
|
||||
List.iter
|
||||
(fun xx -> xx := List.sort (fun (_, y1) (_, y2) -> compare y1 y2) !xx)
|
||||
[ list_tod; list_tom; list_aft ];
|
||||
print_marriage_day conf base
|
||||
(transl conf "today" |> Adef.safe)
|
||||
(ftransl conf "%s, it is %s of")
|
||||
conf.today_wd conf.today !list_tod;
|
||||
print_marriage_day conf base
|
||||
(transl conf "tomorrow" |> Adef.safe)
|
||||
(ftransl conf "%s, it will be %s of")
|
||||
((conf.today_wd + 1) mod 7)
|
||||
tom !list_tom;
|
||||
print_marriage_day conf base
|
||||
(transl conf "the day after tomorrow" |> Adef.safe)
|
||||
(ftransl conf "%s, it will be %s of")
|
||||
((conf.today_wd + 2) mod 7)
|
||||
aft !list_aft;
|
||||
Output.print_sstring conf "\n";
|
||||
propose_months conf (fun () ->
|
||||
Util.hidden_input conf "m" @@ Adef.encoded "AM");
|
||||
Output.print_sstring conf "\n";
|
||||
Hutil.trailer conf
|
||||
|
||||
(* template *)
|
||||
type 'a env = Vother of 'a
|
||||
|
||||
let get_vother = function Vother x -> Some x
|
||||
let set_vother x = Vother x
|
||||
|
||||
let print_anniversaries conf =
|
||||
if p_getenv conf.env "old" = Some "on" then ()
|
||||
else
|
||||
Hutil.interp conf "annivmenu"
|
||||
{
|
||||
Templ.eval_var = (fun _ -> raise Not_found);
|
||||
Templ.eval_transl = (fun _ -> Templ.eval_transl conf);
|
||||
Templ.eval_predefined_apply = (fun _ -> raise Not_found);
|
||||
Templ.get_vother;
|
||||
Templ.set_vother;
|
||||
Templ.print_foreach = (fun _ -> raise Not_found);
|
||||
}
|
||||
[] ()
|
||||
67
lib/birthdayDisplay.mli
Normal file
67
lib/birthdayDisplay.mli
Normal file
@@ -0,0 +1,67 @@
|
||||
val gen_print :
|
||||
Config.config ->
|
||||
Gwdb.base ->
|
||||
int ->
|
||||
(unit ->
|
||||
Gwdb.person * (Config.config -> Gwdb.base -> Gwdb.person -> Adef.safe_string)) ->
|
||||
bool ->
|
||||
unit
|
||||
(** [gen_print conf base month (next,txt_of) dead_people] displays anniversaries
|
||||
for a given month separated by day.
|
||||
If [dead_people] is true then displays birth/death anniversaries for dead people with death reason.
|
||||
Otherwise displays birthdays for alive people.
|
||||
[next] is function that returns next person from iterator
|
||||
and [txt_of] text/link that describes person's information *)
|
||||
|
||||
val print_birth : Config.config -> Gwdb.base -> int -> unit
|
||||
(** Displays birthdays for alive people for a given month *)
|
||||
|
||||
val print_dead : Config.config -> Gwdb.base -> int -> unit
|
||||
(** Displays anniversaries for dead people for a given month *)
|
||||
|
||||
val print_marriage : Config.config -> Gwdb.base -> int -> unit
|
||||
(** Displays marriage anniversaries for a given month *)
|
||||
|
||||
val gen_print_menu_birth :
|
||||
Config.config ->
|
||||
Gwdb.base ->
|
||||
(unit ->
|
||||
Gwdb.person * (Config.config -> Gwdb.base -> Gwdb.person -> Adef.safe_string)) ->
|
||||
(unit -> unit) ->
|
||||
unit
|
||||
(** [gen_print_menu_birth conf base (next,txt_of) mode] displays the main birthdays menu for alive people
|
||||
that contains:
|
||||
- Persons that has their birthdays today
|
||||
- Persons that has their birthdays tomorrow
|
||||
- Persons that has their birthdays after today
|
||||
- Form to select the month of birthdays we want to see.
|
||||
[next] is function that returns next person from iterator, [txt_of] text/link that
|
||||
describes person's information and [mode] that add some additional hidden inputs in the month form *)
|
||||
|
||||
val print_menu_birth : Config.config -> Gwdb.base -> unit
|
||||
(** Displays the main birthdays menu considering all alive people *)
|
||||
|
||||
val gen_print_menu_dead :
|
||||
Config.config ->
|
||||
Gwdb.base ->
|
||||
(unit ->
|
||||
Gwdb.person * (Config.config -> Gwdb.base -> Gwdb.person -> Adef.safe_string)) ->
|
||||
(unit -> unit) ->
|
||||
unit
|
||||
(** [gen_print_menu_dead conf base (next,txt_of) mode] displays the main anniversaries menu for dead people
|
||||
that contains:
|
||||
- Persons that has their anniversaries today
|
||||
- Persons that has their anniversaries tomorrow
|
||||
- Persons that has their anniversaries after today
|
||||
- Form to select the month of anniversaries we want to see.
|
||||
[next] is function that returns next person from iterator, [txt_of] text/link that
|
||||
describes person's information and [mode] that add some additional hidden inputs in the month form *)
|
||||
|
||||
val print_menu_dead : Config.config -> Gwdb.base -> unit
|
||||
(** Displays the main anniversaries menu considering all dead people *)
|
||||
|
||||
val print_menu_marriage : Config.config -> Gwdb.base -> unit
|
||||
(** Displays the main wedding anniversaries menu *)
|
||||
|
||||
val print_anniversaries : Config.config -> unit
|
||||
(** Displays the menu of anniversaries selection *)
|
||||
91
lib/changeChildren.ml
Normal file
91
lib/changeChildren.ml
Normal file
@@ -0,0 +1,91 @@
|
||||
(* Copyright (c) 1998-2007 INRIA *)
|
||||
|
||||
open Config
|
||||
open Def
|
||||
open Gwdb
|
||||
open Util
|
||||
|
||||
let digest_children base ipl =
|
||||
List.fold_left
|
||||
(fun _s ip ->
|
||||
let p = poi base ip in
|
||||
sou base (get_first_name p)
|
||||
^ "\n"
|
||||
^ sou base (get_surname p)
|
||||
^ "\n"
|
||||
^ string_of_int (get_occ p)
|
||||
^ "\n")
|
||||
"" ipl
|
||||
|> Mutil.digest
|
||||
|
||||
let check_digest conf digest =
|
||||
match p_getenv conf.env "digest" with
|
||||
| Some ini_digest -> if digest <> ini_digest then Update.error_digest conf
|
||||
| None -> ()
|
||||
|
||||
exception ChangeChildrenConflict of person * person
|
||||
exception FirstNameMissing of iper
|
||||
|
||||
let check_conflict base p key new_occ ipl =
|
||||
let name = Name.lower key in
|
||||
List.iter
|
||||
(fun ip ->
|
||||
let p1 = poi base ip in
|
||||
if
|
||||
get_iper p1 <> get_iper p
|
||||
&& Name.lower (p_first_name base p1 ^ " " ^ p_surname base p1) = name
|
||||
&& get_occ p1 = new_occ
|
||||
then raise @@ ChangeChildrenConflict (p, p1))
|
||||
ipl
|
||||
|
||||
let change_child conf base parent_surname changed ip =
|
||||
let p = poi base ip in
|
||||
let var = "c" ^ string_of_iper (get_iper p) in
|
||||
let new_first_name =
|
||||
match p_getenv conf.env (var ^ "_first_name") with
|
||||
| Some x -> only_printable x
|
||||
| _ -> p_first_name base p
|
||||
in
|
||||
let new_surname =
|
||||
match p_getenv conf.env (var ^ "_surname") with
|
||||
| Some x ->
|
||||
let x = only_printable x in
|
||||
if x = "" then parent_surname else x
|
||||
| _ -> p_surname base p
|
||||
in
|
||||
let new_occ =
|
||||
match p_getint conf.env (var ^ "_occ") with Some x -> x | _ -> 0
|
||||
in
|
||||
if new_first_name = "" then raise (FirstNameMissing ip)
|
||||
else if
|
||||
new_first_name <> p_first_name base p
|
||||
|| new_surname <> p_surname base p
|
||||
|| new_occ <> get_occ p
|
||||
then (
|
||||
let key = new_first_name ^ " " ^ new_surname in
|
||||
let ipl = Gutil.person_ht_find_all base key in
|
||||
check_conflict base p key new_occ ipl;
|
||||
Image.rename_portrait conf base p (new_first_name, new_surname, new_occ);
|
||||
(* On ajoute les enfants dans le type Change_children_name *)
|
||||
(* pour la future mise à jour de l'historique et du fichier gwf. *)
|
||||
let changed =
|
||||
( (p_first_name base p, p_surname base p, get_occ p, ip),
|
||||
(new_first_name, new_surname, new_occ, ip) )
|
||||
:: changed
|
||||
in
|
||||
let p =
|
||||
{
|
||||
(gen_person_of_person p) with
|
||||
first_name = Gwdb.insert_string base new_first_name;
|
||||
surname = Gwdb.insert_string base new_surname;
|
||||
occ = new_occ;
|
||||
}
|
||||
in
|
||||
patch_person base ip p;
|
||||
changed)
|
||||
else changed
|
||||
|
||||
let change_children conf base parent_surname =
|
||||
List.fold_left
|
||||
(fun changed ip -> change_child conf base parent_surname changed ip)
|
||||
[]
|
||||
25
lib/changeChildren.mli
Normal file
25
lib/changeChildren.mli
Normal file
@@ -0,0 +1,25 @@
|
||||
val digest_children : Gwdb.base -> Gwdb.iper list -> string
|
||||
(** Returns digest (using md5 algorithm) of concatenated for every children first name, surname and occurence number *)
|
||||
|
||||
val check_digest : Config.config -> string -> unit
|
||||
(** Checks if children digest in environement [conf.env] corresponds to specified digest. Other print error page. *)
|
||||
|
||||
exception ChangeChildrenConflict of Gwdb.person * Gwdb.person
|
||||
(** Exception raised when children change defines a new children information (new first name, new surname and new occurence number) are in
|
||||
conflict with another person already existing in the base *)
|
||||
|
||||
exception FirstNameMissing of Gwdb.iper
|
||||
(** Exception raised when children change removes it first name *)
|
||||
|
||||
val change_children :
|
||||
Config.config ->
|
||||
Gwdb.base ->
|
||||
string ->
|
||||
Gwdb.iper list ->
|
||||
((string * string * int * Gwdb.iper) * (string * string * int * Gwdb.iper))
|
||||
list
|
||||
(** Change all person's children by looking up information to update inside [conf.env] that was send by the form.
|
||||
Changes also children's personal image name. Could raise [ChangeChildrenConflict] if new children's key
|
||||
is in conflict with another and [FirstNameMissing] if new children's first name is empty. If surname modification is
|
||||
requested but absent then children takes parent's surname. Returns informations used by [Update] module to record
|
||||
children's update operation. *)
|
||||
208
lib/changeChildrenDisplay.ml
Normal file
208
lib/changeChildrenDisplay.ml
Normal file
@@ -0,0 +1,208 @@
|
||||
(* Copyright (c) 1998-2007 INRIA *)
|
||||
|
||||
open Config
|
||||
open Def
|
||||
open Gwdb
|
||||
open Util
|
||||
open ChangeChildren
|
||||
|
||||
let print_child_person conf base p =
|
||||
let var = Adef.encoded ("c" ^ string_of_iper (get_iper p)) in
|
||||
let first_name =
|
||||
match p_getenv conf.env ((var :> string) ^ "_first_name") with
|
||||
| Some v -> v
|
||||
| None -> p_first_name base p
|
||||
in
|
||||
let surname =
|
||||
match p_getenv conf.env ((var :> string) ^ "_surname") with
|
||||
| Some v -> v
|
||||
| None -> p_surname base p
|
||||
in
|
||||
let occ =
|
||||
match p_getint conf.env ((var :> string) ^ "_occ") with
|
||||
| Some i -> i
|
||||
| None -> get_occ p
|
||||
in
|
||||
Output.print_sstring conf {|<table class="m1-2"><tbody><tr align="|};
|
||||
Output.print_sstring conf conf.left;
|
||||
Output.print_sstring conf {|"><td>|};
|
||||
Output.print_sstring conf {|<label for="|};
|
||||
Output.print_string conf var;
|
||||
Output.print_sstring conf {|_fn" class="mx-2 mb-0">|};
|
||||
transl_nth conf "first name/first names" 0
|
||||
|> Utf8.capitalize_fst |> Output.print_sstring conf;
|
||||
Output.print_sstring conf {|</label></td><td colspan="3">|};
|
||||
Output.print_sstring conf {|<input name=\"|};
|
||||
Output.print_string conf var;
|
||||
Output.print_sstring conf
|
||||
{|_first_name" class="form-control" size="23" maxlength="200" id="|};
|
||||
Output.print_string conf var;
|
||||
Output.print_sstring conf {|_fn" value="|};
|
||||
Output.print_string conf (Util.escape_html first_name);
|
||||
Output.print_sstring conf {|">|};
|
||||
Output.print_sstring conf {|</td><td align="|};
|
||||
Output.print_sstring conf conf.right;
|
||||
Output.print_sstring conf {|"><label for="|};
|
||||
Output.print_string conf var;
|
||||
Output.print_sstring conf {|_occ" class="mx-2 mb-0">|};
|
||||
transl conf "number" |> Utf8.capitalize_fst |> Output.print_sstring conf;
|
||||
Output.print_sstring conf {|</label></td><td>|};
|
||||
Output.print_sstring conf {|<input class="form-control" id="|};
|
||||
Output.print_string conf var;
|
||||
Output.print_sstring conf {|_occ" name="|};
|
||||
Output.print_string conf var;
|
||||
Output.print_sstring conf {|occ" size="5" maxlength="8"|};
|
||||
Output.print_sstring conf
|
||||
(if occ = 0 then "" else {| value="|} ^ string_of_int occ ^ {|"|});
|
||||
Output.print_sstring conf {|</td></tr><tr align="|};
|
||||
Output.print_sstring conf conf.left;
|
||||
Output.print_sstring conf {|"><td>|};
|
||||
Output.print_sstring conf {|<label for="|};
|
||||
Output.print_string conf var;
|
||||
Output.print_sstring conf {|_sn" class="mx-2 mb-0">|};
|
||||
transl_nth conf "surname/surnames" 0
|
||||
|> Utf8.capitalize_fst |> Output.print_sstring conf;
|
||||
Output.print_sstring conf {|</label></td><td colspan="5">|};
|
||||
Output.print_sstring conf {|<input name="|};
|
||||
Output.print_string conf var;
|
||||
Output.print_sstring conf
|
||||
{|_surname" class="form-control" size="40" maxlength="200" id="|};
|
||||
Output.print_string conf var;
|
||||
Output.print_sstring conf {|_sn" value="|};
|
||||
Output.print_string conf (Util.escape_html surname);
|
||||
Output.print_sstring conf {|">|};
|
||||
Output.print_sstring conf {|</td></tr></tbody></table>|}
|
||||
|
||||
let print_children conf base ipl =
|
||||
Output.print_sstring conf "<ul>\n";
|
||||
List.iter
|
||||
(fun ip ->
|
||||
let p = poi base ip in
|
||||
Output.print_sstring conf {|<li class="mt-3"><span class="ml-2">|};
|
||||
Output.print_string conf
|
||||
(reference conf base p (gen_person_text conf base p));
|
||||
Output.print_string conf (DateDisplay.short_dates_text conf base p);
|
||||
Output.print_sstring conf {|</span>|};
|
||||
print_child_person conf base p;
|
||||
Output.print_sstring conf "</li>")
|
||||
ipl;
|
||||
Output.print_sstring conf "</ul>"
|
||||
|
||||
let print_change conf base p =
|
||||
let title _ =
|
||||
transl conf "change children's names"
|
||||
|> Utf8.capitalize_fst |> Output.print_sstring conf
|
||||
in
|
||||
let children = children_of_p base p in
|
||||
let digest = digest_children base children in
|
||||
Perso.interp_notempl_with_menu title "perso_header" conf base p;
|
||||
Output.print_sstring conf "<h2>";
|
||||
title false;
|
||||
(let s : Adef.safe_string = gen_person_text conf base p in
|
||||
let r : Adef.safe_string = reference conf base p s in
|
||||
Util.transl_a_of_b conf "" (r :> string) (s :> string)
|
||||
|> Output.print_sstring conf);
|
||||
Output.print_sstring conf " ";
|
||||
Output.print_string conf (DateDisplay.short_dates_text conf base p);
|
||||
Output.print_sstring conf {|</h2><form method="post" action="|};
|
||||
Output.print_sstring conf conf.command;
|
||||
Output.print_sstring conf {|">|};
|
||||
Util.hidden_env conf;
|
||||
Util.hidden_input_s conf "ip" (string_of_iper (get_iper p));
|
||||
Util.hidden_input_s conf "digest" digest;
|
||||
Util.hidden_input_s conf "m" "CHG_CHN_OK";
|
||||
print_children conf base children;
|
||||
Output.print_sstring conf
|
||||
{|<button type="submit" class="btn btn-primary btn-lg ml-5 mb-2">|};
|
||||
transl_nth conf "validate/delete" 0
|
||||
|> Utf8.capitalize_fst |> Output.print_sstring conf;
|
||||
Output.print_sstring conf "</button></form>";
|
||||
Hutil.trailer conf
|
||||
|
||||
let print conf base =
|
||||
match p_getenv conf.env "ip" with
|
||||
| Some i ->
|
||||
let p = poi base (iper_of_string i) in
|
||||
print_change conf base p
|
||||
| _ -> Hutil.incorrect_request conf
|
||||
|
||||
let print_children_list conf base u =
|
||||
Output.print_sstring conf "<h4>";
|
||||
transl_nth conf "child/children" 1
|
||||
|> Utf8.capitalize_fst |> Output.print_sstring conf;
|
||||
Output.print_sstring conf "</h4><p><ul>";
|
||||
Array.iter
|
||||
(fun ifam ->
|
||||
let des = foi base ifam in
|
||||
Array.iter
|
||||
(fun ip ->
|
||||
let p = poi base ip in
|
||||
Output.print_sstring conf "<li>";
|
||||
gen_person_text conf base p
|
||||
|> reference conf base p |> Output.print_string conf;
|
||||
Output.print_string conf (DateDisplay.short_dates_text conf base p))
|
||||
(get_children des))
|
||||
(get_family u);
|
||||
Output.print_sstring conf "</ul>"
|
||||
|
||||
let print_change_done conf base p =
|
||||
let title _ =
|
||||
transl conf "children's names changed"
|
||||
|> Utf8.capitalize_fst |> Output.print_sstring conf
|
||||
in
|
||||
Hutil.header conf title;
|
||||
gen_person_text conf base p
|
||||
|> reference conf base p |> Output.print_string conf;
|
||||
Output.print_string conf (DateDisplay.short_dates_text conf base p);
|
||||
print_children_list conf base p;
|
||||
Hutil.trailer conf
|
||||
|
||||
let print_conflict conf base ip_var p =
|
||||
let var = "c" ^ string_of_iper ip_var in
|
||||
Update.print_create_conflict conf base p var
|
||||
|
||||
let error_person conf err =
|
||||
let title _ =
|
||||
transl conf "error" |> Utf8.capitalize_fst |> Output.print_sstring conf
|
||||
in
|
||||
Hutil.rheader conf title;
|
||||
Output.printf conf "%s\n" (Utf8.capitalize_fst err);
|
||||
Hutil.trailer conf;
|
||||
raise
|
||||
@@ Update.ModErr
|
||||
(Update.UERR (__FILE__ ^ " " ^ string_of_int __LINE__ |> Adef.safe))
|
||||
|
||||
let print_update_child conf base =
|
||||
match p_getenv conf.env "m" with
|
||||
| Some "CHG_CHN_OK" -> print conf base
|
||||
| _ -> Hutil.incorrect_request conf
|
||||
|
||||
let print_change_ok conf base p =
|
||||
let ipl = children_of_p base p in
|
||||
let parent_surname = p_surname base p in
|
||||
let redisp = Option.is_some (p_getenv conf.env "return") in
|
||||
if redisp then print_update_child conf base
|
||||
else (
|
||||
check_digest conf (digest_children base ipl);
|
||||
let changed =
|
||||
try change_children conf base parent_surname ipl with
|
||||
| ChangeChildrenConflict (p, p') ->
|
||||
print_conflict conf base (get_iper p) p'
|
||||
| FirstNameMissing _ ->
|
||||
error_person conf (transl conf "first name missing")
|
||||
in
|
||||
Util.commit_patches conf base;
|
||||
let changed =
|
||||
U_Change_children_name
|
||||
(Util.string_gen_person base (gen_person_of_person p), changed)
|
||||
in
|
||||
History.record conf base changed "cn";
|
||||
print_change_done conf base p)
|
||||
|
||||
let print_ok o_conf base =
|
||||
let conf = Update.update_conf o_conf in
|
||||
match p_getenv conf.env "ip" with
|
||||
| Some i ->
|
||||
let p = poi base (iper_of_string i) in
|
||||
print_change_ok conf base p
|
||||
| _ -> Hutil.incorrect_request conf
|
||||
8
lib/changeChildrenDisplay.mli
Normal file
8
lib/changeChildrenDisplay.mli
Normal file
@@ -0,0 +1,8 @@
|
||||
val print : Config.config -> Gwdb.base -> unit
|
||||
(** Displays a form where all person's children with their first names, surnames and occurence numbers are listed
|
||||
and could be modified on submit. Id of person should be mentionned in environement [conf.env] with binding {i "ip"=id}
|
||||
otherwise displays Bad request page. *)
|
||||
|
||||
val print_ok : Config.config -> Gwdb.base -> unit
|
||||
(** Performs and displays results of children modification requested by form submiting. If changes of one of children raises an error
|
||||
displays corresponding error page that either just informs user about error source either propose to fix up solution. *)
|
||||
349
lib/check.ml
Normal file
349
lib/check.ml
Normal file
@@ -0,0 +1,349 @@
|
||||
(* $Id: check.ml,v 5.28 2008-11-03 15:40:10 ddr Exp $ *)
|
||||
(* Copyright (c) 1998-2007 INRIA *)
|
||||
|
||||
open Def
|
||||
open Gwdb
|
||||
|
||||
(* Printing check errors *)
|
||||
|
||||
let designation base p =
|
||||
let s = Gutil.designation base p in
|
||||
if String.get s 0 = '?' || String.get s (String.length s - 1) = '?' then
|
||||
s ^ " (i=" ^ string_of_iper (get_iper p) ^ ")"
|
||||
else s
|
||||
|
||||
let string_of_epers_name base epers_name =
|
||||
match epers_name with
|
||||
| Epers_Birth -> "birth"
|
||||
| Epers_Baptism -> "baptism"
|
||||
| Epers_Death -> "death"
|
||||
| Epers_Burial -> "burial"
|
||||
| Epers_Cremation -> "cremation"
|
||||
| Epers_Accomplishment -> "accomplishment"
|
||||
| Epers_Acquisition -> "acquisition"
|
||||
| Epers_Adhesion -> "adhesion"
|
||||
| Epers_BaptismLDS -> "baptism (LDS)"
|
||||
| Epers_BarMitzvah -> "bar mitzvah"
|
||||
| Epers_BatMitzvah -> "bat mitzvah"
|
||||
| Epers_Benediction -> "benediction"
|
||||
| Epers_ChangeName -> "change name"
|
||||
| Epers_Circumcision -> "circumcision"
|
||||
| Epers_Confirmation -> "confirmation"
|
||||
| Epers_ConfirmationLDS -> "confirmation (LDS)"
|
||||
| Epers_Decoration -> "decoration"
|
||||
| Epers_DemobilisationMilitaire -> "military demobilisation"
|
||||
| Epers_Diploma -> "diploma"
|
||||
| Epers_Distinction -> "distinction"
|
||||
| Epers_Dotation -> "dotation"
|
||||
| Epers_DotationLDS -> "dotation (LDS)"
|
||||
| Epers_Education -> "education"
|
||||
| Epers_Election -> "election"
|
||||
| Epers_Emigration -> "emigration"
|
||||
| Epers_Excommunication -> "excommunication"
|
||||
| Epers_FamilyLinkLDS -> "family link (LDS)"
|
||||
| Epers_FirstCommunion -> "first communion"
|
||||
| Epers_Funeral -> "funeral"
|
||||
| Epers_Graduate -> "graduation"
|
||||
| Epers_Hospitalisation -> "hospitalisation"
|
||||
| Epers_Illness -> "illness"
|
||||
| Epers_Immigration -> "immigration"
|
||||
| Epers_ListePassenger -> "passenger liste"
|
||||
| Epers_MilitaryDistinction -> "military distinction"
|
||||
| Epers_MilitaryPromotion -> "military promotion"
|
||||
| Epers_MilitaryService -> "military service"
|
||||
| Epers_MobilisationMilitaire -> "military mobilisation"
|
||||
| Epers_Naturalisation -> "naturalisation"
|
||||
| Epers_Occupation -> "occupation"
|
||||
| Epers_Ordination -> "ordination"
|
||||
| Epers_Property -> "property"
|
||||
| Epers_Recensement -> "recensement"
|
||||
| Epers_Residence -> "residence"
|
||||
| Epers_Retired -> "retirement"
|
||||
| Epers_ScellentChildLDS -> "scellent child (LDS)"
|
||||
| Epers_ScellentParentLDS -> "scellent parent (LDS)"
|
||||
| Epers_ScellentSpouseLDS -> "scellent spouse (LDS)"
|
||||
| Epers_VenteBien -> "sell"
|
||||
| Epers_Will -> "will"
|
||||
| Epers_Name n -> sou base n
|
||||
|
||||
let string_of_efam_name base efam_name =
|
||||
match efam_name with
|
||||
| Efam_Marriage -> "marriage"
|
||||
| Efam_NoMarriage -> "relation"
|
||||
| Efam_NoMention -> "relation"
|
||||
| Efam_Engage -> "engagement"
|
||||
| Efam_Divorce -> "divorce"
|
||||
| Efam_Separated -> "separation"
|
||||
| Efam_Annulation -> "annulation"
|
||||
| Efam_MarriageBann -> "marriage bann"
|
||||
| Efam_MarriageContract -> "marriage contract"
|
||||
| Efam_MarriageLicense -> "marriage licence"
|
||||
| Efam_PACS -> "PACS"
|
||||
| Efam_Residence -> "residence"
|
||||
| Efam_Name n -> sou base n
|
||||
|
||||
let print_base_error oc base = function
|
||||
| AlreadyDefined p ->
|
||||
Printf.fprintf oc "%s is defined several times\n" (designation base p)
|
||||
| OwnAncestor p ->
|
||||
Printf.fprintf oc "%s is his/her own ancestor\n" (designation base p)
|
||||
| BadSexOfMarriedPerson p ->
|
||||
Printf.fprintf oc "%s bad sex for a married person\n" (designation base p)
|
||||
|
||||
let print_base_warning oc base = function
|
||||
| BigAgeBetweenSpouses (p1, p2, a) ->
|
||||
Printf.fprintf oc
|
||||
"The difference of age between %s and %s is quite important: %d\n"
|
||||
(designation base p1) (designation base p2) a.year
|
||||
| BirthAfterDeath p ->
|
||||
Printf.fprintf oc "%s born after his/her death\n" (designation base p)
|
||||
| ChangedOrderOfChildren (ifam, _, _, _) ->
|
||||
let cpl = foi base ifam in
|
||||
Printf.fprintf oc "Changed order of children of %s and %s\n"
|
||||
(designation base (poi base (get_father cpl)))
|
||||
(designation base (poi base (get_mother cpl)))
|
||||
| ChildrenNotInOrder (ifam, _, elder, x) ->
|
||||
let cpl = foi base ifam in
|
||||
Printf.fprintf oc
|
||||
"The following children of\n %s\nand\n %s\nare not in order:\n"
|
||||
(designation base (poi base (get_father cpl)))
|
||||
(designation base (poi base (get_mother cpl)));
|
||||
Printf.fprintf oc "- %s\n" (designation base elder);
|
||||
Printf.fprintf oc "- %s\n" (designation base x)
|
||||
| ChangedOrderOfMarriages (p, _, _) ->
|
||||
Printf.fprintf oc "Changed order of marriages of %s\n"
|
||||
(designation base p)
|
||||
| ChangedOrderOfFamilyEvents (ifam, _, _) ->
|
||||
let cpl = foi base ifam in
|
||||
Printf.fprintf oc "Changed order of family's events for %s\n"
|
||||
(designation base (poi base (get_father cpl)));
|
||||
Printf.fprintf oc "Changed order of family's events for %s\n"
|
||||
(designation base (poi base (get_mother cpl)))
|
||||
| ChangedOrderOfPersonEvents (p, _, _) ->
|
||||
Printf.fprintf oc "Changed order of person's events for %s\n"
|
||||
(designation base p)
|
||||
| CloseChildren (ifam, c1, c2) ->
|
||||
let cpl = foi base ifam in
|
||||
Printf.fprintf oc
|
||||
"The following children of\n %s\nand\n %s\nare born very close:\n"
|
||||
(designation base (poi base (get_father cpl)))
|
||||
(designation base (poi base (get_mother cpl)));
|
||||
Printf.fprintf oc "- %s\n" (designation base c1);
|
||||
Printf.fprintf oc "- %s\n" (designation base c2)
|
||||
| DeadOld (p, a) ->
|
||||
Printf.fprintf oc "%s died at the advanced age of %d years old\n"
|
||||
(designation base p) a.year
|
||||
| DeadTooEarlyToBeFather (father, child) ->
|
||||
Printf.fprintf oc "%s " (designation base child);
|
||||
Printf.fprintf oc
|
||||
"is born more than 2 years after the death of his/her father";
|
||||
Printf.fprintf oc " %s\n" (designation base father)
|
||||
| DistantChildren (ifam, p1, p2) ->
|
||||
let cpl = foi base ifam in
|
||||
Printf.fprintf oc
|
||||
"The following children of\n %s\nand\n %s\nare born very close:\n"
|
||||
(designation base (poi base (get_father cpl)))
|
||||
(designation base (poi base (get_mother cpl)));
|
||||
Printf.fprintf oc "- %s\n" (designation base p1);
|
||||
Printf.fprintf oc "- %s\n" (designation base p2)
|
||||
| FEventOrder (p, e1, e2) ->
|
||||
Printf.fprintf oc "%s's %s before his/her %s\n" (designation base p)
|
||||
(string_of_efam_name base e1.efam_name)
|
||||
(string_of_efam_name base e2.efam_name)
|
||||
| FWitnessEventAfterDeath (p, e, _fam) ->
|
||||
Printf.fprintf oc "%s " (designation base p);
|
||||
Printf.fprintf oc "witnessed the %s after his/her death\n"
|
||||
(string_of_efam_name base e.efam_name)
|
||||
| FWitnessEventBeforeBirth (p, e, _fam) ->
|
||||
Printf.fprintf oc "%s " (designation base p);
|
||||
Printf.fprintf oc "witnessed the %s before his/her birth\n"
|
||||
(string_of_efam_name base e.efam_name)
|
||||
| IncoherentSex (p, fixed, not_fixed) ->
|
||||
Printf.fprintf oc "%s sex not coherent with relations"
|
||||
(designation base p);
|
||||
if fixed > 0 then
|
||||
if not_fixed > 0 then
|
||||
Printf.fprintf oc " (fixed in %d of the %d cases)" fixed
|
||||
(fixed + not_fixed)
|
||||
else Printf.fprintf oc " (fixed)";
|
||||
Printf.fprintf oc "\n"
|
||||
| IncoherentAncestorDate (anc, p) ->
|
||||
Printf.fprintf oc "%s " (designation base p);
|
||||
Printf.fprintf oc " has a younger ancestor:";
|
||||
Printf.fprintf oc " %s\n" (designation base anc)
|
||||
| MarriageDateAfterDeath p ->
|
||||
Printf.fprintf oc "%s " (designation base p);
|
||||
Printf.fprintf oc "marriage after his/her death\n"
|
||||
| MarriageDateBeforeBirth p ->
|
||||
Printf.fprintf oc "%s " (designation base p);
|
||||
Printf.fprintf oc "marriage before his/her birth\n"
|
||||
| MotherDeadBeforeChildBirth (mother, child) ->
|
||||
Printf.fprintf oc "%s is born after the death of his/her mother %s\n"
|
||||
(designation base child) (designation base mother)
|
||||
| ParentBornAfterChild (parent, child) ->
|
||||
Printf.fprintf oc "%s born after his/her child %s\n"
|
||||
(designation base parent) (designation base child)
|
||||
| ParentTooOld (p, a, _) ->
|
||||
Printf.fprintf oc "%s was parent at age of %d\n" (designation base p)
|
||||
a.year
|
||||
| ParentTooYoung (p, a, _) ->
|
||||
Printf.fprintf oc "%s was parent at age of %d\n" (designation base p)
|
||||
a.year
|
||||
| PossibleDuplicateFam (f1, f2) ->
|
||||
Printf.fprintf oc "possible duplicate families: %s and %s\n"
|
||||
(string_of_ifam f1) (string_of_ifam f2)
|
||||
| PossibleDuplicateFamHomonymous (f1, f2, p) ->
|
||||
let f = foi base f1 in
|
||||
let fath = get_father f in
|
||||
let moth = get_mother f in
|
||||
let curr, hom =
|
||||
if eq_iper fath (get_iper p) then (moth, fath) else (fath, moth)
|
||||
in
|
||||
Printf.fprintf oc
|
||||
"possible duplicate families: %s and %s, %s has unions with several \
|
||||
persons named %s\n"
|
||||
(string_of_ifam f1) (string_of_ifam f2)
|
||||
(designation base (poi base curr))
|
||||
(designation base (poi base hom))
|
||||
| PEventOrder (p, e1, e2) ->
|
||||
Printf.fprintf oc "%s's %s before his/her %s\n" (designation base p)
|
||||
(string_of_epers_name base e1.epers_name)
|
||||
(string_of_epers_name base e2.epers_name)
|
||||
| PWitnessEventAfterDeath (p, e, _origin) ->
|
||||
Printf.fprintf oc "%s " (designation base p);
|
||||
Printf.fprintf oc "witnessed the %s after his/her death\n"
|
||||
(string_of_epers_name base e.epers_name)
|
||||
| PWitnessEventBeforeBirth (p, e, _origin) ->
|
||||
Printf.fprintf oc "%s " (designation base p);
|
||||
Printf.fprintf oc "witnessed the %s before his/her birth\n"
|
||||
(string_of_epers_name base e.epers_name)
|
||||
| TitleDatesError (p, t) ->
|
||||
Printf.fprintf oc "%s " (designation base p);
|
||||
Printf.fprintf oc "has incorrect title dates as:\n";
|
||||
Printf.fprintf oc " %s %s\n" (sou base t.t_ident) (sou base t.t_place)
|
||||
| UndefinedSex p ->
|
||||
Printf.fprintf oc "Undefined sex for %s\n" (designation base p)
|
||||
| YoungForMarriage (p, a, _) | OldForMarriage (p, a, _) ->
|
||||
Printf.fprintf oc "%s married at age %d\n" (designation base p) a.year
|
||||
|
||||
type check_date =
|
||||
| CheckBefore of int
|
||||
| CheckAfter of int
|
||||
| CheckOther of int
|
||||
| CheckInfered of check_date
|
||||
|
||||
let min_year_of p =
|
||||
let aux = function
|
||||
| { prec = After; year } -> CheckAfter year
|
||||
| { prec = Before; year } -> CheckBefore year
|
||||
| { year } -> CheckOther year
|
||||
in
|
||||
Option.map aux (Date.cdate_to_dmy_opt (get_birth p))
|
||||
|
||||
let dummy_date = CheckInfered (CheckOther max_int)
|
||||
|
||||
(* check ad print warning if ancestors is born before person *)
|
||||
let rec check_ancestors base warning year year_tab ip ini_p =
|
||||
let infer = function
|
||||
| CheckBefore i -> CheckInfered (CheckBefore (pred i))
|
||||
| CheckAfter i -> CheckInfered (CheckAfter (pred i))
|
||||
| CheckOther i -> CheckInfered (CheckOther (pred i))
|
||||
| CheckInfered (CheckBefore i) -> CheckInfered (CheckBefore (pred i))
|
||||
| CheckInfered (CheckAfter i) -> CheckInfered (CheckAfter (pred i))
|
||||
| CheckInfered (CheckOther i) -> CheckInfered (CheckOther (pred i))
|
||||
| _ -> assert false
|
||||
in
|
||||
let own = function CheckInfered _ -> false | _ -> true in
|
||||
let test a b p p' =
|
||||
match (a, b) with
|
||||
| ( CheckAfter y,
|
||||
( CheckBefore y'
|
||||
| CheckOther y'
|
||||
| CheckInfered (CheckBefore y')
|
||||
| CheckInfered (CheckOther y') ) )
|
||||
when y >= y' ->
|
||||
warning (IncoherentAncestorDate (Lazy.force p, p'))
|
||||
| _ -> ()
|
||||
in
|
||||
if Gwdb.Marker.get year_tab ip = dummy_date then (
|
||||
let p = poi base ip in
|
||||
let new_year, new_ini_p =
|
||||
match min_year_of p with Some y -> (y, p) | None -> (infer year, ini_p)
|
||||
in
|
||||
Gwdb.Marker.set year_tab ip new_year;
|
||||
test new_year year (lazy p) ini_p;
|
||||
match get_parents p with
|
||||
| Some ifam ->
|
||||
let fam = foi base ifam in
|
||||
let f ip =
|
||||
let year = Gwdb.Marker.get year_tab ip in
|
||||
if year = dummy_date then
|
||||
check_ancestors base warning new_year year_tab ip new_ini_p
|
||||
else if own year then
|
||||
test year new_year (lazy (poi base ip)) new_ini_p
|
||||
in
|
||||
f @@ get_father fam;
|
||||
f @@ get_mother fam
|
||||
| None -> ())
|
||||
|
||||
let check_base ?(verbose = false) ?(mem = false) base error warning changed_p =
|
||||
if not mem then (
|
||||
Gwdb.load_persons_array base;
|
||||
Gwdb.load_ascends_array base;
|
||||
Gwdb.load_unions_array base;
|
||||
Gwdb.load_couples_array base);
|
||||
let persons = Gwdb.ipers base in
|
||||
let len = Gwdb.Collection.length persons in
|
||||
let year_tab = Gwdb.iper_marker (Gwdb.ipers base) dummy_date in
|
||||
if verbose then (
|
||||
Printf.eprintf "check persons\n";
|
||||
ProgrBar.start ();
|
||||
Gwdb.Collection.iteri
|
||||
(fun i ip ->
|
||||
ProgrBar.run i len;
|
||||
let p = poi base ip in
|
||||
if Gwdb.Marker.get year_tab ip = dummy_date then
|
||||
check_ancestors base warning dummy_date year_tab ip p;
|
||||
match CheckItem.person ~onchange:false base warning p with
|
||||
| Some ippl -> List.iter changed_p ippl
|
||||
| None -> ())
|
||||
persons;
|
||||
ProgrBar.finish ())
|
||||
else
|
||||
Gwdb.Collection.iter
|
||||
(fun ip ->
|
||||
let p = poi base ip in
|
||||
if Gwdb.Marker.get year_tab ip = dummy_date then
|
||||
check_ancestors base warning dummy_date year_tab ip p;
|
||||
match CheckItem.person ~onchange:false base warning p with
|
||||
| Some ippl -> List.iter changed_p ippl
|
||||
| None -> ())
|
||||
persons;
|
||||
if not mem then (
|
||||
Gwdb.clear_unions_array base;
|
||||
Gwdb.load_families_array base;
|
||||
Gwdb.load_descends_array base);
|
||||
let families = Gwdb.ifams base in
|
||||
let len = Gwdb.Collection.length families in
|
||||
if verbose then (
|
||||
Printf.eprintf "check families\n";
|
||||
ProgrBar.start ();
|
||||
Gwdb.Collection.iteri
|
||||
(fun i ifam ->
|
||||
ProgrBar.run i len;
|
||||
CheckItem.family ~onchange:false base warning ifam @@ foi base ifam)
|
||||
families;
|
||||
ProgrBar.finish ())
|
||||
else
|
||||
Gwdb.Collection.iter
|
||||
(fun ifam ->
|
||||
CheckItem.family ~onchange:false base warning ifam @@ foi base ifam)
|
||||
families;
|
||||
if not mem then (
|
||||
Gwdb.clear_persons_array base;
|
||||
Gwdb.clear_families_array base;
|
||||
Gwdb.clear_descends_array base);
|
||||
Consang.check_noloop base error;
|
||||
if not mem then (
|
||||
Gwdb.clear_ascends_array base;
|
||||
Gwdb.clear_couples_array base)
|
||||
23
lib/check.mli
Normal file
23
lib/check.mli
Normal file
@@ -0,0 +1,23 @@
|
||||
(* Copyright (c) 2006-2007 INRIA *)
|
||||
|
||||
(* checking database ; independent from its implementation on disk *)
|
||||
|
||||
open Gwdb
|
||||
|
||||
val print_base_error : out_channel -> base -> CheckItem.base_error -> unit
|
||||
(** Print database specification error on the giving channel *)
|
||||
|
||||
val print_base_warning : out_channel -> base -> CheckItem.base_warning -> unit
|
||||
(** Print database specification warning on the giving channel *)
|
||||
|
||||
val check_base :
|
||||
?verbose:bool ->
|
||||
?mem:bool ->
|
||||
base ->
|
||||
(CheckItem.base_error -> unit) ->
|
||||
(CheckItem.base_warning -> unit) ->
|
||||
(iper * person * Def.sex option * relation list option -> unit) ->
|
||||
unit
|
||||
(** [check_base base onwarning onerror _] makes full database proprety check. Checks every person and family separetely
|
||||
with corresponding function inside [CheckItem] module. Checks also person's graph in order to find cycles (if person
|
||||
is own ancestor). *)
|
||||
1101
lib/checkItem.ml
Normal file
1101
lib/checkItem.ml
Normal file
File diff suppressed because it is too large
Load Diff
74
lib/checkItem.mli
Normal file
74
lib/checkItem.mli
Normal file
@@ -0,0 +1,74 @@
|
||||
(* $Id: checkItem.mli,v 1.12 2007-09-05 13:19:25 ddr Exp $ *)
|
||||
(* Copyright (c) 2006-2007 INRIA *)
|
||||
|
||||
open Gwdb
|
||||
|
||||
type base_error = person Def.error
|
||||
(** Database specification error *)
|
||||
|
||||
type base_warning =
|
||||
(iper, person, ifam, family, title, pers_event, fam_event) Def.warning
|
||||
(** Database specification warning *)
|
||||
|
||||
(* *)
|
||||
type base_misc = (person, family, title) Def.misc
|
||||
|
||||
val check_siblings :
|
||||
?onchange:bool ->
|
||||
base ->
|
||||
(base_warning -> unit) ->
|
||||
ifam * family ->
|
||||
(person -> unit) ->
|
||||
unit
|
||||
(** [check_siblings ?onchange base warning (ifam, fam) callback]
|
||||
Checks birth date consistency between siblings.
|
||||
Also calls [callback] with each child. *)
|
||||
|
||||
val person :
|
||||
?onchange:bool ->
|
||||
base ->
|
||||
(base_warning -> unit) ->
|
||||
person ->
|
||||
(iper * person * Def.sex option * relation list option) list option
|
||||
(** [person onchange base warn p] checks person's properties:
|
||||
|
||||
- personal events
|
||||
- person's age
|
||||
- person's titles dates
|
||||
- etc.
|
||||
If [onchange] is set then sort person's events
|
||||
Calls [warn] on corresponding [base_warning] when find some inconsistencies. *)
|
||||
|
||||
val family :
|
||||
?onchange:bool -> base -> (base_warning -> unit) -> ifam -> family -> unit
|
||||
(** [family onchange base warn f] checks family properties like :
|
||||
|
||||
- familial events
|
||||
- parents marraige
|
||||
- children age gap and birth
|
||||
- etc.
|
||||
If [onchange] is set then sort family's events
|
||||
Calls [warn] on corresponding [base_warning] when find some inconsistencies. *)
|
||||
|
||||
val on_person_update : base -> (base_warning -> unit) -> person -> unit
|
||||
(** Unlike [person] who checks directly the properties of a person, checks the properties
|
||||
of a person in relation to other people (his children, parents, spouses, witnesses, etc).
|
||||
Calls [warn] on corresponding [base_warning] when find some inconsistencies.
|
||||
*)
|
||||
|
||||
val sort_children : base -> iper array -> (iper array * iper array) option
|
||||
(** Sort array of children by their birth date from oldest to youngest.
|
||||
Returns old array and sorted version. *)
|
||||
|
||||
val check_other_fields : base -> (base_misc -> unit) -> ifam -> family -> unit
|
||||
(** Cheks if family, father and mother have sources. Otherwise call [misc] on [base_misc] *)
|
||||
|
||||
val eq_warning : base -> base_warning -> base_warning -> bool
|
||||
(** equality between base_warnings *)
|
||||
|
||||
val person_warnings : Config.config -> base -> person -> base_warning list
|
||||
(** [person_warnings conf base p]
|
||||
Shorthand for [CheckItem.person] and [CheckItem.on_person_update] on [p]
|
||||
and [CheckItem.check_siblings] on they children
|
||||
using [auth_warning] for filtering.
|
||||
*)
|
||||
192
lib/config.ml
Normal file
192
lib/config.ml
Normal file
@@ -0,0 +1,192 @@
|
||||
(* Copyright (c) 1998-2007 INRIA *)
|
||||
|
||||
open Def
|
||||
open Gwdb
|
||||
|
||||
type auth_scheme_kind =
|
||||
| NoAuth
|
||||
| TokenAuth of token_auth_scheme
|
||||
| HttpAuth of http_auth_scheme
|
||||
|
||||
and token_auth_scheme = { ts_user : string; ts_pass : string }
|
||||
|
||||
and http_auth_scheme =
|
||||
| Basic of basic_auth_scheme
|
||||
| Digest of digest_auth_scheme
|
||||
|
||||
and basic_auth_scheme = {
|
||||
bs_realm : string;
|
||||
bs_user : string;
|
||||
bs_pass : string;
|
||||
}
|
||||
|
||||
and digest_auth_scheme = {
|
||||
ds_username : string;
|
||||
ds_realm : string;
|
||||
ds_nonce : string;
|
||||
ds_meth : string;
|
||||
ds_uri : string;
|
||||
ds_qop : string;
|
||||
ds_nc : string;
|
||||
ds_cnonce : string;
|
||||
ds_response : string;
|
||||
}
|
||||
|
||||
type output_conf = {
|
||||
status : Def.httpStatus -> unit;
|
||||
header : string -> unit;
|
||||
body : string -> unit;
|
||||
flush : unit -> unit;
|
||||
}
|
||||
|
||||
type env = (string * Adef.encoded_string) list
|
||||
|
||||
type config = {
|
||||
from : string;
|
||||
api_mode : bool;
|
||||
manitou : bool;
|
||||
supervisor : bool;
|
||||
wizard : bool;
|
||||
is_printed_by_template : bool;
|
||||
debug : bool;
|
||||
query_start : float;
|
||||
friend : bool;
|
||||
just_friend_wizard : bool;
|
||||
user : string;
|
||||
username : string;
|
||||
userkey : string;
|
||||
auth_scheme : auth_scheme_kind;
|
||||
command : string;
|
||||
indep_command : string;
|
||||
highlight : string;
|
||||
lang : string;
|
||||
vowels : string list;
|
||||
default_lang : string;
|
||||
browser_lang : string;
|
||||
default_sosa_ref : iper * Gwdb.person option;
|
||||
multi_parents : bool;
|
||||
authorized_wizards_notes : bool;
|
||||
public_if_titles : bool;
|
||||
public_if_no_date : bool;
|
||||
mutable setup_link : bool;
|
||||
access_by_key : bool;
|
||||
private_years : int;
|
||||
private_years_death : int;
|
||||
private_years_marriage : int;
|
||||
hide_names : bool;
|
||||
use_restrict : bool;
|
||||
no_image : bool;
|
||||
no_note : bool;
|
||||
bname : string;
|
||||
nb_of_persons : int;
|
||||
cgi_passwd : string;
|
||||
env : env;
|
||||
mutable senv : env;
|
||||
mutable henv : env;
|
||||
base_env : (string * string) list (* content of .gwf file *);
|
||||
allowed_titles : string list Lazy.t;
|
||||
denied_titles : string list Lazy.t;
|
||||
request : string list;
|
||||
lexicon : (string, string) Hashtbl.t;
|
||||
mutable charset : string;
|
||||
is_rtl : bool;
|
||||
left : string;
|
||||
right : string;
|
||||
auth_file : string;
|
||||
border : int;
|
||||
mutable n_connect : (int * int * int * (string * float) list) option;
|
||||
today : dmy;
|
||||
today_wd : int;
|
||||
time : int * int * int;
|
||||
ctime : float; (* TODO verify usefulness *)
|
||||
mutable output_conf : output_conf;
|
||||
(* HTTP printer *)
|
||||
(* prefix for image urls:
|
||||
the value of argument -images_url if specified, otherwise
|
||||
command ^ "?m=IM&v=" in CGI mode
|
||||
"images" otherwise *)
|
||||
gw_prefix : string;
|
||||
images_prefix : string;
|
||||
etc_prefix : string;
|
||||
(* in CGI mode, provides location of etc files to Apache for direct loading *)
|
||||
(* if true, the base name is in the b argument of the query string: ?b=BASE&...
|
||||
if false, the base name is the last element of the uri path: .../base?... *)
|
||||
cgi : bool;
|
||||
forced_plugins : string list;
|
||||
plugins : string list;
|
||||
}
|
||||
|
||||
(**/**)
|
||||
|
||||
(** A dummy {!type:config} value, with uninitialized fields.
|
||||
Used for testing purpose *)
|
||||
let empty =
|
||||
{
|
||||
from = "";
|
||||
manitou = false;
|
||||
supervisor = false;
|
||||
wizard = false;
|
||||
api_mode = false;
|
||||
is_printed_by_template = false;
|
||||
debug = false;
|
||||
query_start = 0.;
|
||||
friend = false;
|
||||
just_friend_wizard = false;
|
||||
user = "";
|
||||
username = "";
|
||||
userkey = "";
|
||||
auth_scheme = NoAuth;
|
||||
command = "";
|
||||
indep_command = "";
|
||||
highlight = "";
|
||||
lang = "";
|
||||
vowels = [];
|
||||
default_lang = "";
|
||||
browser_lang = "";
|
||||
default_sosa_ref = (Gwdb.dummy_iper, None);
|
||||
multi_parents = false;
|
||||
authorized_wizards_notes = false;
|
||||
public_if_titles = false;
|
||||
public_if_no_date = false;
|
||||
setup_link = false;
|
||||
access_by_key = false;
|
||||
private_years = 0;
|
||||
private_years_death = 0;
|
||||
private_years_marriage = 0;
|
||||
hide_names = false;
|
||||
use_restrict = false;
|
||||
no_image = false;
|
||||
no_note = false;
|
||||
bname = "";
|
||||
nb_of_persons = -1;
|
||||
cgi_passwd = "";
|
||||
env = [];
|
||||
senv = [];
|
||||
henv = [];
|
||||
base_env = [];
|
||||
allowed_titles = lazy [];
|
||||
denied_titles = lazy [];
|
||||
request = [];
|
||||
lexicon = Hashtbl.create 16;
|
||||
charset = "";
|
||||
is_rtl = false;
|
||||
left = "";
|
||||
right = "";
|
||||
auth_file = "";
|
||||
border = 0;
|
||||
n_connect = None;
|
||||
today = { Def.day = 0; month = 0; year = 0; delta = 0; prec = Def.Sure };
|
||||
today_wd = 0;
|
||||
time = (0, 0, 0);
|
||||
ctime = 0.;
|
||||
gw_prefix = "";
|
||||
images_prefix = "";
|
||||
etc_prefix = "";
|
||||
cgi = false;
|
||||
output_conf =
|
||||
{ status = ignore; header = ignore; body = ignore; flush = ignore };
|
||||
forced_plugins = [];
|
||||
plugins = [];
|
||||
}
|
||||
|
||||
(**/**)
|
||||
127
lib/config.mli
Normal file
127
lib/config.mli
Normal file
@@ -0,0 +1,127 @@
|
||||
open Def
|
||||
open Gwdb
|
||||
|
||||
(** Authentication scheme data type *)
|
||||
type auth_scheme_kind =
|
||||
| NoAuth
|
||||
| TokenAuth of token_auth_scheme
|
||||
| HttpAuth of http_auth_scheme
|
||||
|
||||
and token_auth_scheme = { ts_user : string; ts_pass : string }
|
||||
(** Authentication via security token *)
|
||||
|
||||
(** Authentication via HTTP *)
|
||||
and http_auth_scheme =
|
||||
| Basic of basic_auth_scheme
|
||||
| Digest of digest_auth_scheme
|
||||
|
||||
and basic_auth_scheme = {
|
||||
bs_realm : string;
|
||||
bs_user : string;
|
||||
bs_pass : string;
|
||||
}
|
||||
(** Basic authentication scheme inside {i Autorization} HTTP header *)
|
||||
|
||||
and digest_auth_scheme = {
|
||||
ds_username : string;
|
||||
ds_realm : string;
|
||||
ds_nonce : string;
|
||||
ds_meth : string;
|
||||
ds_uri : string;
|
||||
ds_qop : string;
|
||||
ds_nc : string;
|
||||
ds_cnonce : string;
|
||||
ds_response : string;
|
||||
}
|
||||
(** Digest authentication scheme inside {i Autorization} HTTP header *)
|
||||
|
||||
type output_conf = {
|
||||
status : Def.httpStatus -> unit;
|
||||
header : string -> unit;
|
||||
body : string -> unit;
|
||||
flush : unit -> unit;
|
||||
}
|
||||
(** HTTP printer, that prints and sends requests on the user's socket *)
|
||||
|
||||
type env = (string * Adef.encoded_string) list
|
||||
|
||||
(** Geneweb configuration data type *)
|
||||
type config = {
|
||||
from : string;
|
||||
api_mode : bool;
|
||||
manitou : bool;
|
||||
supervisor : bool;
|
||||
wizard : bool;
|
||||
is_printed_by_template : bool;
|
||||
debug : bool;
|
||||
query_start : float;
|
||||
friend : bool;
|
||||
just_friend_wizard : bool;
|
||||
user : string;
|
||||
username : string;
|
||||
userkey : string;
|
||||
auth_scheme : auth_scheme_kind;
|
||||
command : string;
|
||||
indep_command : string;
|
||||
highlight : string;
|
||||
lang : string;
|
||||
vowels : string list;
|
||||
default_lang : string;
|
||||
browser_lang : string;
|
||||
default_sosa_ref : iper * Gwdb.person option;
|
||||
multi_parents : bool;
|
||||
authorized_wizards_notes : bool;
|
||||
public_if_titles : bool;
|
||||
public_if_no_date : bool;
|
||||
mutable setup_link : bool;
|
||||
access_by_key : bool;
|
||||
private_years : int;
|
||||
private_years_death : int;
|
||||
private_years_marriage : int;
|
||||
hide_names : bool;
|
||||
use_restrict : bool;
|
||||
no_image : bool;
|
||||
no_note : bool;
|
||||
bname : string;
|
||||
nb_of_persons : int;
|
||||
cgi_passwd : string;
|
||||
env : env;
|
||||
mutable senv : env;
|
||||
mutable henv : env;
|
||||
base_env : (string * string) list (* content of .gwf file *);
|
||||
allowed_titles : string list Lazy.t;
|
||||
denied_titles : string list Lazy.t;
|
||||
request : string list;
|
||||
lexicon : (string, string) Hashtbl.t;
|
||||
mutable charset : string;
|
||||
is_rtl : bool;
|
||||
left : string;
|
||||
right : string;
|
||||
auth_file : string;
|
||||
border : int;
|
||||
mutable n_connect : (int * int * int * (string * float) list) option;
|
||||
today : dmy;
|
||||
today_wd : int;
|
||||
time : int * int * int;
|
||||
ctime : float;
|
||||
mutable output_conf : output_conf;
|
||||
(* HTTP printer *)
|
||||
(* prefix for image urls:
|
||||
the value of argument -images_url if specified, otherwise
|
||||
command ^ "?m=IM&v=" in CGI mode
|
||||
"images" otherwise *)
|
||||
gw_prefix : string;
|
||||
images_prefix : string;
|
||||
(* if true, the base name is in the b argument of the query string: ?b=BASE&...
|
||||
if false, the base name is the last element of the uri path: .../base?... *)
|
||||
etc_prefix : string;
|
||||
(* in CGI mode, provides location of etc files to Apache for direct loading *)
|
||||
cgi : bool;
|
||||
forced_plugins : string list;
|
||||
plugins : string list;
|
||||
}
|
||||
(** Geneweb configuration data type *)
|
||||
|
||||
val empty : config
|
||||
(** A dummy {!type:config} value, with uninitialized fields.
|
||||
Used for testing purpose *)
|
||||
290
lib/core/consang.ml
Normal file
290
lib/core/consang.ml
Normal file
@@ -0,0 +1,290 @@
|
||||
(* Copyright (c) 1998-2007 INRIA *)
|
||||
|
||||
(* Algorithm relationship and links from Didier Remy *)
|
||||
|
||||
open Def
|
||||
open Gwdb
|
||||
|
||||
type anc_stat = MaybeAnc | IsAnc
|
||||
|
||||
(* relationship:
|
||||
- elim_ancestor
|
||||
to prune displayed relationships
|
||||
- anc_stat1, anc_stat2
|
||||
optimization to answer faster when ancestors list is exhausted
|
||||
- lens1, lens2:
|
||||
the third parameter (list iper) of the list has been added to
|
||||
be able to reconstitute the branch in case of the sex of the
|
||||
persons in the branch is important to display the relationship
|
||||
text
|
||||
*)
|
||||
|
||||
type relationship = {
|
||||
mutable weight1 : float;
|
||||
mutable weight2 : float;
|
||||
mutable relationship : float;
|
||||
mutable lens1 : (int * int * iper list) list;
|
||||
mutable lens2 : (int * int * iper list) list;
|
||||
mutable inserted : int;
|
||||
mutable elim_ancestors : bool;
|
||||
mutable anc_stat1 : anc_stat;
|
||||
mutable anc_stat2 : anc_stat;
|
||||
}
|
||||
|
||||
type relationship_info = {
|
||||
tstab : (Gwdb.iper, int) Gwdb.Marker.t;
|
||||
reltab : (Gwdb.iper, relationship) Gwdb.Marker.t;
|
||||
mutable queue : Gwdb.iper list array;
|
||||
}
|
||||
|
||||
let half x = x *. 0.5
|
||||
|
||||
type visit =
|
||||
| NotVisited (* not visited person *)
|
||||
| BeingVisited
|
||||
(* visited person but visit of ascendants haven't been terminated *)
|
||||
| Visited (* visited person and his ascendants *)
|
||||
|
||||
let rec noloop_aux base error tab i =
|
||||
match Gwdb.Marker.get tab i with
|
||||
| NotVisited ->
|
||||
(match get_parents (poi base i) with
|
||||
| Some ifam ->
|
||||
let fam = foi base ifam in
|
||||
let fath = get_father fam in
|
||||
let moth = get_mother fam in
|
||||
Gwdb.Marker.set tab i BeingVisited;
|
||||
noloop_aux base error tab fath;
|
||||
noloop_aux base error tab moth
|
||||
| None -> ());
|
||||
Gwdb.Marker.set tab i Visited
|
||||
| BeingVisited -> error (OwnAncestor (poi base i))
|
||||
| Visited -> ()
|
||||
|
||||
(** It is highly recommended to load ascends and couples array before
|
||||
running [check_noloop]
|
||||
*)
|
||||
let check_noloop base error =
|
||||
let tab = Gwdb.iper_marker (Gwdb.ipers base) NotVisited in
|
||||
Gwdb.Collection.iter (noloop_aux base error tab) (Gwdb.ipers base)
|
||||
|
||||
let check_noloop_for_person_list base error list =
|
||||
let tab = Gwdb.iper_marker (Gwdb.ipers base) NotVisited in
|
||||
List.iter (noloop_aux base error tab) list
|
||||
|
||||
exception TopologicalSortError of person
|
||||
|
||||
(* Return tab such as: i is an ancestor of j => tab.(i) > tab.(j) *)
|
||||
(* This complicated topological sort has the important following properties:
|
||||
- only "ascends" has to be loaded; no need to load "union" and "descend"
|
||||
which use much memory space.
|
||||
- the value of tab is minimum; it is important for the optimization of
|
||||
relationship computation (stopping the computation when the ancestor
|
||||
list of one of the person is exhausted).
|
||||
*)
|
||||
let topological_sort base poi =
|
||||
let persons = Gwdb.ipers base in
|
||||
let tab = Gwdb.iper_marker (Gwdb.ipers base) 0 in
|
||||
let cnt = ref 0 in
|
||||
Gwdb.Collection.iter
|
||||
(fun i ->
|
||||
let a = poi base i in
|
||||
match get_parents a with
|
||||
| Some ifam ->
|
||||
let cpl = foi base ifam in
|
||||
let ifath = get_father cpl in
|
||||
let imoth = get_mother cpl in
|
||||
Gwdb.Marker.set tab ifath (Gwdb.Marker.get tab ifath + 1);
|
||||
Gwdb.Marker.set tab imoth (Gwdb.Marker.get tab imoth + 1)
|
||||
| _ -> ())
|
||||
persons;
|
||||
(* starting from the leaf vertex of graph (persons without childs) *)
|
||||
let todo =
|
||||
Gwdb.Collection.fold
|
||||
(fun acc i -> if Gwdb.Marker.get tab i = 0 then i :: acc else acc)
|
||||
[] persons
|
||||
in
|
||||
let rec loop tval list =
|
||||
if list = [] then ()
|
||||
else
|
||||
let new_list =
|
||||
List.fold_left
|
||||
(fun new_list i ->
|
||||
let a = poi base i in
|
||||
Gwdb.Marker.set tab i tval;
|
||||
incr cnt;
|
||||
match get_parents a with
|
||||
| Some ifam ->
|
||||
let cpl = foi base ifam in
|
||||
let ifath = get_father cpl in
|
||||
let imoth = get_mother cpl in
|
||||
Gwdb.Marker.set tab ifath (Gwdb.Marker.get tab ifath - 1);
|
||||
Gwdb.Marker.set tab imoth (Gwdb.Marker.get tab imoth - 1);
|
||||
let new_list =
|
||||
if Gwdb.Marker.get tab ifath = 0 then ifath :: new_list
|
||||
else new_list
|
||||
in
|
||||
if Gwdb.Marker.get tab imoth = 0 then imoth :: new_list
|
||||
else new_list
|
||||
| _ -> new_list)
|
||||
[] list
|
||||
in
|
||||
loop (tval + 1) new_list
|
||||
in
|
||||
loop 0 todo;
|
||||
if !cnt <> nb_of_persons base then
|
||||
check_noloop base (function
|
||||
| OwnAncestor p -> raise (TopologicalSortError p)
|
||||
| _ -> assert false);
|
||||
tab
|
||||
|
||||
let phony_rel =
|
||||
{
|
||||
weight1 = 0.0;
|
||||
weight2 = 0.0;
|
||||
relationship = 0.0;
|
||||
lens1 = [];
|
||||
lens2 = [];
|
||||
inserted = 0;
|
||||
elim_ancestors = false;
|
||||
anc_stat1 = MaybeAnc;
|
||||
anc_stat2 = MaybeAnc;
|
||||
}
|
||||
|
||||
let make_relationship_info base tstab =
|
||||
let tab = Gwdb.iper_marker (Gwdb.ipers base) phony_rel in
|
||||
{ tstab; reltab = tab; queue = [||] }
|
||||
|
||||
let rec insert_branch_len_rec ((len, n, ip) as x) = function
|
||||
| [] -> [ (len, n, [ ip ]) ]
|
||||
| ((len1, n1, ipl1) as y) :: lens ->
|
||||
if len = len1 then
|
||||
let n2 = n + n1 in
|
||||
let n2 = if n < 0 || n1 < 0 || n2 < 0 then -1 else n2 in
|
||||
(len1, n2, ip :: ipl1) :: lens
|
||||
else y :: insert_branch_len_rec x lens
|
||||
|
||||
let insert_branch_len ip lens (len, n, _ipl) =
|
||||
insert_branch_len_rec (succ len, n, ip) lens
|
||||
|
||||
let consang_of p =
|
||||
if get_consang p = Adef.no_consang then 0.0
|
||||
else Adef.float_of_fix (get_consang p)
|
||||
|
||||
let mark = ref 0
|
||||
|
||||
let new_mark () =
|
||||
incr mark;
|
||||
!mark
|
||||
|
||||
let relationship_and_links base ri b ip1 ip2 =
|
||||
let i1 = ip1 in
|
||||
let i2 = ip2 in
|
||||
if i1 = i2 then (1.0, [])
|
||||
else
|
||||
let reltab = ri.reltab in
|
||||
let tstab = ri.tstab in
|
||||
let yes_inserted = new_mark () in
|
||||
let reset u =
|
||||
let tu = Gwdb.Marker.get reltab u in
|
||||
if tu == phony_rel then
|
||||
Gwdb.Marker.set reltab u
|
||||
{
|
||||
weight1 = 0.0;
|
||||
weight2 = 0.0;
|
||||
relationship = 0.0;
|
||||
lens1 = [];
|
||||
lens2 = [];
|
||||
inserted = yes_inserted;
|
||||
elim_ancestors = false;
|
||||
anc_stat1 = MaybeAnc;
|
||||
anc_stat2 = MaybeAnc;
|
||||
}
|
||||
else (
|
||||
tu.weight1 <- 0.0;
|
||||
tu.weight2 <- 0.0;
|
||||
tu.relationship <- 0.0;
|
||||
tu.lens1 <- [];
|
||||
tu.lens2 <- [];
|
||||
tu.inserted <- yes_inserted;
|
||||
tu.elim_ancestors <- false;
|
||||
tu.anc_stat1 <- MaybeAnc;
|
||||
tu.anc_stat2 <- MaybeAnc)
|
||||
in
|
||||
let qi = ref (min (Gwdb.Marker.get tstab i1) (Gwdb.Marker.get tstab i2)) in
|
||||
let qmax = ref (-1) in
|
||||
let insert u =
|
||||
let v = Gwdb.Marker.get tstab u in
|
||||
reset u;
|
||||
(if v >= Array.length ri.queue then
|
||||
let len = Array.length ri.queue in
|
||||
ri.queue <- Array.append ri.queue (Array.make (v + 1 - len) []));
|
||||
if !qmax < 0 then (
|
||||
for i = !qi to v - 1 do
|
||||
ri.queue.(i) <- []
|
||||
done;
|
||||
qmax := v;
|
||||
ri.queue.(v) <- [ u ])
|
||||
else (
|
||||
if v > !qmax then (
|
||||
for i = !qmax + 1 to v do
|
||||
ri.queue.(i) <- []
|
||||
done;
|
||||
qmax := v);
|
||||
ri.queue.(v) <- u :: ri.queue.(v))
|
||||
in
|
||||
let relationship = ref 0.0 in
|
||||
let nb_anc1 = ref 1 in
|
||||
let nb_anc2 = ref 1 in
|
||||
let tops = ref [] in
|
||||
let treat_parent ip_from u y =
|
||||
if (Gwdb.Marker.get reltab y).inserted <> yes_inserted then insert y;
|
||||
let ty = Gwdb.Marker.get reltab y in
|
||||
let p1 = half u.weight1 in
|
||||
let p2 = half u.weight2 in
|
||||
if u.anc_stat1 = IsAnc && ty.anc_stat1 <> IsAnc then (
|
||||
ty.anc_stat1 <- IsAnc;
|
||||
incr nb_anc1);
|
||||
if u.anc_stat2 = IsAnc && ty.anc_stat2 <> IsAnc then (
|
||||
ty.anc_stat2 <- IsAnc;
|
||||
incr nb_anc2);
|
||||
ty.weight1 <- ty.weight1 +. p1;
|
||||
ty.weight2 <- ty.weight2 +. p2;
|
||||
ty.relationship <- ty.relationship +. (p1 *. p2);
|
||||
if u.elim_ancestors then ty.elim_ancestors <- true;
|
||||
if b && not ty.elim_ancestors then (
|
||||
ty.lens1 <- List.fold_left (insert_branch_len ip_from) ty.lens1 u.lens1;
|
||||
ty.lens2 <- List.fold_left (insert_branch_len ip_from) ty.lens2 u.lens2)
|
||||
in
|
||||
let treat_ancestor u =
|
||||
let tu = Gwdb.Marker.get reltab u in
|
||||
let a = poi base u in
|
||||
let contribution =
|
||||
(tu.weight1 *. tu.weight2) -. (tu.relationship *. (1.0 +. consang_of a))
|
||||
in
|
||||
if tu.anc_stat1 = IsAnc then decr nb_anc1;
|
||||
if tu.anc_stat2 = IsAnc then decr nb_anc2;
|
||||
relationship := !relationship +. contribution;
|
||||
if b && contribution <> 0.0 && not tu.elim_ancestors then (
|
||||
tops := u :: !tops;
|
||||
tu.elim_ancestors <- true);
|
||||
match get_parents a with
|
||||
| Some ifam ->
|
||||
let cpl = foi base ifam in
|
||||
treat_parent u tu (get_father cpl);
|
||||
treat_parent u tu (get_mother cpl)
|
||||
| _ -> ()
|
||||
in
|
||||
insert i1;
|
||||
insert i2;
|
||||
(Gwdb.Marker.get reltab i1).weight1 <- 1.0;
|
||||
(Gwdb.Marker.get reltab i2).weight2 <- 1.0;
|
||||
(Gwdb.Marker.get reltab i1).lens1 <- [ (0, 1, []) ];
|
||||
(Gwdb.Marker.get reltab i2).lens2 <- [ (0, 1, []) ];
|
||||
(Gwdb.Marker.get reltab i1).anc_stat1 <- IsAnc;
|
||||
(Gwdb.Marker.get reltab i2).anc_stat2 <- IsAnc;
|
||||
while !qi <= !qmax && !nb_anc1 > 0 && !nb_anc2 > 0 do
|
||||
List.iter treat_ancestor ri.queue.(!qi);
|
||||
incr qi
|
||||
done;
|
||||
(half !relationship, !tops)
|
||||
64
lib/core/consang.mli
Normal file
64
lib/core/consang.mli
Normal file
@@ -0,0 +1,64 @@
|
||||
(* Copyright (c) 1998-2007 INRIA *)
|
||||
|
||||
open Def
|
||||
open Gwdb
|
||||
|
||||
(* TODOOCP: doc *)
|
||||
|
||||
type anc_stat
|
||||
(** Relation with ancestor status *)
|
||||
|
||||
type relationship = {
|
||||
mutable weight1 : float;
|
||||
mutable weight2 : float;
|
||||
mutable relationship : float;
|
||||
mutable lens1 : (int * int * iper list) list;
|
||||
mutable lens2 : (int * int * iper list) list;
|
||||
mutable inserted : int;
|
||||
mutable elim_ancestors : bool;
|
||||
mutable anc_stat1 : anc_stat;
|
||||
mutable anc_stat2 : anc_stat;
|
||||
}
|
||||
(** Consanguinity information attached to person (relationship between parents) *)
|
||||
|
||||
type relationship_info = {
|
||||
(* Information about topological rank for each person *)
|
||||
tstab : (Gwdb.iper, int) Gwdb.Marker.t;
|
||||
reltab : (Gwdb.iper, relationship) Gwdb.Marker.t;
|
||||
mutable queue : Gwdb.iper list array;
|
||||
}
|
||||
(** Computation consanguinity state for every person in the base *)
|
||||
|
||||
exception TopologicalSortError of person
|
||||
(** Error that could occure while topological sorting, and raised when person is ancestor of himself. *)
|
||||
|
||||
val topological_sort :
|
||||
Gwdb.base ->
|
||||
(Gwdb.base -> Gwdb.iper -> Gwdb.person) ->
|
||||
(Gwdb.iper, int) Gwdb.Marker.t
|
||||
(** Returns result of topological sort of persons. Result is represented as marker that associates to every person in the base his
|
||||
topologic rank (let's suppose [r]). Global rule is : if person p1 is ancestor of p2 then r(p1) > r(p2). For example, all leaf
|
||||
persons (without children) have rank 0, their parents (if no another child that has child themself) - rank 1, parents of their
|
||||
parents - rank 2, etc. Raises [TopologicalSortError] if person is directly or undirectly is ancestor of himself (cycle). *)
|
||||
|
||||
val make_relationship_info :
|
||||
base -> (Gwdb.iper, int) Gwdb.Marker.t -> relationship_info
|
||||
(** Initialise relationship info. *)
|
||||
|
||||
(* Returns relationship rate between two person and common ancestors (is exists). *)
|
||||
val relationship_and_links :
|
||||
base ->
|
||||
relationship_info ->
|
||||
bool ->
|
||||
Gwdb.iper ->
|
||||
Gwdb.iper ->
|
||||
float * Gwdb.iper list
|
||||
|
||||
val check_noloop : base -> (person error -> unit) -> unit
|
||||
(** [check_noloop base onerror] scans database person's oriented graph (vertex is a person and edge is parenthood from child to parent). If
|
||||
cycle is found (person is directly or undirectly is ancestor of himself) calls [onerror] with [OwnAncestor] error. Array of
|
||||
ascendants should be load in the memory. *)
|
||||
|
||||
val check_noloop_for_person_list :
|
||||
base -> (person error -> unit) -> Gwdb.iper list -> unit
|
||||
(** Same as [check_noloop] but scans only specified list of persons and their ancestors instead of entire database. *)
|
||||
142
lib/core/consangAll.ml
Normal file
142
lib/core/consangAll.ml
Normal file
@@ -0,0 +1,142 @@
|
||||
(* Copyright (c) 1998-2007 INRIA *)
|
||||
|
||||
open Gwdb
|
||||
|
||||
(* let rec clear_descend_consang base consang mark ifam =
|
||||
* let des = foi base ifam in
|
||||
* Array.iter
|
||||
* (fun ip ->
|
||||
* if not (Gwdb.Marker.get mark ip) then
|
||||
* begin
|
||||
* consang ip Adef.no_consang;
|
||||
* Gwdb.Marker.set mark ip true ;
|
||||
* let u = poi base ip in
|
||||
* Array.iter (clear_descend_consang base consang mark) (get_family u)
|
||||
* end)
|
||||
* (get_children des) *)
|
||||
|
||||
let relationship base tab ip1 ip2 =
|
||||
fst (Consang.relationship_and_links base tab false ip1 ip2)
|
||||
|
||||
let trace verbosity cnt max_cnt =
|
||||
if verbosity >= 2 then (
|
||||
Printf.eprintf "%7d\008\008\008\008\008\008\008" cnt;
|
||||
flush stderr)
|
||||
else if verbosity >= 1 then ProgrBar.run (max_cnt - cnt) max_cnt
|
||||
|
||||
let consang_array base =
|
||||
let patched = ref false in
|
||||
let fget i = get_parents @@ poi base i in
|
||||
let cget i = get_consang @@ poi base i in
|
||||
let cset i v =
|
||||
patched := true;
|
||||
patch_ascend base i
|
||||
Def.{ (gen_ascend_of_person @@ poi base i) with consang = v }
|
||||
in
|
||||
(fget, cget, cset, patched)
|
||||
|
||||
let compute ?(verbosity = 2) base from_scratch =
|
||||
let () = load_ascends_array base in
|
||||
let () = load_couples_array base in
|
||||
let fget, cget, cset, patched = consang_array base in
|
||||
(try
|
||||
let tab =
|
||||
let ts = Consang.topological_sort base poi in
|
||||
Consang.make_relationship_info base ts
|
||||
in
|
||||
let persons = Gwdb.ipers base in
|
||||
let families = Gwdb.ifams base in
|
||||
let consang_tab = Gwdb.ifam_marker families Adef.no_consang in
|
||||
let cnt = ref 0 in
|
||||
(* FIXME *)
|
||||
(* if not from_scratch then
|
||||
* begin
|
||||
* let mark = Gwdb.Marker.make (Gwdb.Collection.length persons) false in
|
||||
* List.iter
|
||||
* (fun ip ->
|
||||
* let u = poi base ip in
|
||||
* Array.iter (clear_descend_consang base cset mark) (get_family u))
|
||||
* (patched_ascends base)
|
||||
* end; *)
|
||||
Gwdb.Collection.iter
|
||||
(fun i ->
|
||||
if from_scratch then (
|
||||
cset i Adef.no_consang;
|
||||
incr cnt)
|
||||
else
|
||||
let cg = cget i in
|
||||
Option.iter
|
||||
(fun ifam -> Gwdb.Marker.set consang_tab ifam cg)
|
||||
(fget i);
|
||||
if cg = Adef.no_consang then incr cnt)
|
||||
persons;
|
||||
(* number of persons which need consanguinity to be computed *)
|
||||
let max_cnt = !cnt in
|
||||
let most = ref None in
|
||||
if verbosity >= 1 then Printf.eprintf "To do: %d persons\n" max_cnt;
|
||||
if max_cnt <> 0 then
|
||||
if verbosity >= 2 then (
|
||||
Printf.eprintf "Computing consanguinity...";
|
||||
flush stderr)
|
||||
else if verbosity >= 1 then ProgrBar.start ();
|
||||
let running = ref true in
|
||||
while !running do
|
||||
running := false;
|
||||
Gwdb.Collection.iter
|
||||
(fun i ->
|
||||
(* if person's consanguinity wasn't calculated *)
|
||||
if cget i = Adef.no_consang then
|
||||
match fget i with
|
||||
(* if person has parents *)
|
||||
| Some ifam ->
|
||||
let pconsang = Gwdb.Marker.get consang_tab ifam in
|
||||
(* if parent's family's consanguinity wasn't calculated *)
|
||||
if pconsang = Adef.no_consang then
|
||||
let cpl = foi base ifam in
|
||||
let ifath = get_father cpl in
|
||||
let imoth = get_mother cpl in
|
||||
(* if parent's consanguinity was calculated *)
|
||||
if
|
||||
cget ifath != Adef.no_consang
|
||||
&& cget imoth != Adef.no_consang
|
||||
then (
|
||||
let consang = relationship base tab ifath imoth in
|
||||
trace verbosity !cnt max_cnt;
|
||||
decr cnt;
|
||||
let cg = Adef.fix_of_float consang in
|
||||
cset i cg;
|
||||
Gwdb.Marker.set consang_tab ifam cg;
|
||||
if verbosity >= 2 then
|
||||
if
|
||||
match !most with Some m -> cg > cget m | None -> true
|
||||
then (
|
||||
Printf.eprintf "\nMax consanguinity %g for %s... "
|
||||
consang
|
||||
(Gutil.designation base (poi base i));
|
||||
flush stderr;
|
||||
most := Some i)
|
||||
(* if it wasn't makes further another run over persons *))
|
||||
else running := true
|
||||
(* if it was then set to person his family's consanguinity *)
|
||||
else (
|
||||
trace verbosity !cnt max_cnt;
|
||||
decr cnt;
|
||||
cset i pconsang)
|
||||
(* if he doesn't then set his consanguinity to 0 *)
|
||||
| None ->
|
||||
trace verbosity !cnt max_cnt;
|
||||
decr cnt;
|
||||
cset i (Adef.fix_of_float 0.0))
|
||||
persons
|
||||
done;
|
||||
if max_cnt <> 0 then
|
||||
if verbosity >= 2 then (
|
||||
Printf.eprintf " done \n";
|
||||
flush stderr)
|
||||
else if verbosity >= 1 then ProgrBar.finish ()
|
||||
with Sys.Break when verbosity > 0 ->
|
||||
Printf.eprintf "\n";
|
||||
flush stderr;
|
||||
());
|
||||
if !patched then Gwdb.commit_patches base;
|
||||
!patched
|
||||
11
lib/core/consangAll.mli
Normal file
11
lib/core/consangAll.mli
Normal file
@@ -0,0 +1,11 @@
|
||||
(* Copyright (c) 2006-2007 INRIA *)
|
||||
|
||||
open Gwdb
|
||||
|
||||
val compute : ?verbosity:int -> base -> bool -> bool
|
||||
(** [compute base from_scratch]
|
||||
[?verbosity] may be 0, 1 or 2 (default is 2)
|
||||
Compute consanguinity for each person in the base. If [from_scratch] is set then recompute
|
||||
consanguinity for entire database.
|
||||
Return [true] if base has been patched, [false] otherwise.
|
||||
*)
|
||||
12
lib/core/dune.in
Normal file
12
lib/core/dune.in
Normal file
@@ -0,0 +1,12 @@
|
||||
(library
|
||||
(name geneweb_core)
|
||||
(public_name geneweb.core)
|
||||
(wrapped false)
|
||||
(synopsis "GeneWeb Core library")
|
||||
(preprocess (action (run %{bin:cppo} %%%CPPO_D%%% -V OCAML:%{ocaml_version} %{input-file})))
|
||||
(libraries unix
|
||||
geneweb_def
|
||||
geneweb_gwdb
|
||||
geneweb_sosa_mli
|
||||
geneweb_util)
|
||||
)
|
||||
554
lib/cousins.ml
Normal file
554
lib/cousins.ml
Normal file
@@ -0,0 +1,554 @@
|
||||
(* Copyright (c) 1998-2007 INRIA *)
|
||||
|
||||
open Def
|
||||
open Gwdb
|
||||
open Util
|
||||
|
||||
type one_cousin =
|
||||
Gwdb_driver.iper * Gwdb_driver.ifam list * Gwdb_driver.iper * int
|
||||
|
||||
type cousins_i_j = one_cousin list
|
||||
|
||||
let default_max_cnt = 2000
|
||||
|
||||
let max_cousin_level conf =
|
||||
let default_max_cousin_lvl = 6 in
|
||||
try int_of_string (List.assoc "max_cousins_level" conf.Config.base_env)
|
||||
with Not_found | Failure _ -> default_max_cousin_lvl
|
||||
|
||||
let children_of base u =
|
||||
Array.fold_right
|
||||
(fun ifam list ->
|
||||
let des = foi base ifam in
|
||||
Array.fold_right List.cons (get_children des) list)
|
||||
(get_family u) []
|
||||
|
||||
let children_of_fam base ifam = Array.to_list (get_children @@ foi base ifam)
|
||||
|
||||
let siblings_by conf base iparent ip =
|
||||
let list = children_of base (pget conf base iparent) in
|
||||
List.filter (( <> ) ip) list
|
||||
|
||||
let merge_siblings l1 l2 =
|
||||
let l =
|
||||
let rec rev_merge r = function
|
||||
| [] -> r
|
||||
| ((v, _) as x) :: l ->
|
||||
rev_merge (if List.mem_assoc v r then r else x :: r) l
|
||||
in
|
||||
rev_merge (List.rev l1) l2
|
||||
in
|
||||
List.rev l
|
||||
|
||||
let siblings conf base ip =
|
||||
match get_parents (pget conf base ip) with
|
||||
| None -> []
|
||||
| Some ifam ->
|
||||
let cpl = foi base ifam in
|
||||
let fath_sib =
|
||||
List.map
|
||||
(fun ip -> (ip, (get_father cpl, Male)))
|
||||
(siblings_by conf base (get_father cpl) ip)
|
||||
in
|
||||
let moth_sib =
|
||||
List.map
|
||||
(fun ip -> (ip, (get_mother cpl, Female)))
|
||||
(siblings_by conf base (get_mother cpl) ip)
|
||||
in
|
||||
merge_siblings fath_sib moth_sib
|
||||
|
||||
let rec has_desc_lev conf base lev u =
|
||||
if lev <= 1 then true
|
||||
else
|
||||
Array.exists
|
||||
(fun ifam ->
|
||||
let des = foi base ifam in
|
||||
Array.exists
|
||||
(fun ip -> has_desc_lev conf base (lev - 1) (pget conf base ip))
|
||||
(get_children des))
|
||||
(get_family u)
|
||||
|
||||
let br_inter_is_empty b1 b2 =
|
||||
List.for_all (fun (ip, _) -> not (List.mem_assoc ip b2)) b1
|
||||
|
||||
(* Algorithms *)
|
||||
|
||||
let sibling_has_desc_lev conf base lev (ip, _) =
|
||||
has_desc_lev conf base lev (pget conf base ip)
|
||||
|
||||
(* begin cousins *)
|
||||
|
||||
let cousins_table = Array.make_matrix 1 1 []
|
||||
let tm = Unix.localtime (Unix.time ())
|
||||
let today_year = tm.Unix.tm_year + 1900
|
||||
let cousins_t = ref None
|
||||
let cousins_dates_t = ref None
|
||||
let mal = 12
|
||||
let mdl = 12
|
||||
|
||||
let update_min_max (min, max) date =
|
||||
((if date < min then date else min), if date > max then date else max)
|
||||
|
||||
let max_ancestor_level conf base ip max_lvl =
|
||||
let max_lvl =
|
||||
match List.assoc_opt "max_anc_level" conf.Config.base_env with
|
||||
| Some v when v <> "" -> int_of_string v
|
||||
| _ -> max_lvl
|
||||
in
|
||||
let x = ref 0 in
|
||||
let mark = Gwdb.iper_marker (Gwdb.ipers base) false in
|
||||
(* Loading ITL cache, up to 10 generations. *)
|
||||
let () = !GWPARAM_ITL.init_cache conf base ip 10 0 0 in
|
||||
let rec loop level ip =
|
||||
(* Ne traite pas l'index s'il a déjà été traité. *)
|
||||
(* Pose surement probleme pour des implexes. *)
|
||||
if not @@ Gwdb.Marker.get mark ip then (
|
||||
(* Met à jour le tableau d'index pour indiquer que l'index est traité. *)
|
||||
Gwdb.Marker.set mark ip true;
|
||||
x := max !x level;
|
||||
if !x <> max_lvl then
|
||||
match get_parents (pget conf base ip) with
|
||||
| Some ifam ->
|
||||
let cpl = foi base ifam in
|
||||
loop (succ level) (get_father cpl);
|
||||
loop (succ level) (get_mother cpl)
|
||||
| _ ->
|
||||
x :=
|
||||
max !x
|
||||
(!GWPARAM_ITL.max_ancestor_level
|
||||
conf base ip conf.bname max_lvl level))
|
||||
in
|
||||
loop 0 ip;
|
||||
!x
|
||||
|
||||
let max_descendant_level conf _base _ip max_lvl =
|
||||
(* TODO we should compute this value *)
|
||||
match List.assoc_opt "max_desc_level" conf.Config.base_env with
|
||||
| Some v when v <> "" -> int_of_string v
|
||||
| _ -> max_lvl
|
||||
|
||||
let get_min_max_dates base l =
|
||||
let rec loop (min, max) = function
|
||||
| [] -> (min, max)
|
||||
| one_cousin :: l -> (
|
||||
let ip, _, _, _ = one_cousin in
|
||||
let not_dead = get_death (poi base ip) = NotDead in
|
||||
let birth_date, death_date, _ =
|
||||
Gutil.get_birth_death_date (poi base ip)
|
||||
in
|
||||
match (birth_date, death_date) with
|
||||
| Some (Dgreg (b, _)), Some (Dgreg (d, _)) ->
|
||||
let birth =
|
||||
match b.prec with
|
||||
| After | Before | About | Maybe | OrYear _ | YearInt _ -> false
|
||||
| _ -> true
|
||||
in
|
||||
let death =
|
||||
match d.prec with
|
||||
| After | Before | About | Maybe | OrYear _ | YearInt _ -> false
|
||||
| _ -> true
|
||||
in
|
||||
if birth && death then
|
||||
let min, max = update_min_max (min, max) b.year in
|
||||
let min, max = update_min_max (min, max) d.year in
|
||||
loop (min, max) l
|
||||
else if birth && not death then
|
||||
loop (update_min_max (min, max) b.year) l
|
||||
else if (not birth) && death then
|
||||
loop (update_min_max (min, max) d.year) l
|
||||
else loop (min, max) l
|
||||
| Some (Dgreg (b, _)), _ -> (
|
||||
match b.prec with
|
||||
| After | Before | About | Maybe | OrYear _ | YearInt _ ->
|
||||
if not_dead then loop (update_min_max (min, max) today_year) l
|
||||
else loop (min, max) l
|
||||
| _ ->
|
||||
let min, max = update_min_max (min, max) b.year in
|
||||
if not_dead then loop (update_min_max (min, max) today_year) l
|
||||
else loop (min, max) l)
|
||||
| _, Some (Dgreg (d, _)) -> (
|
||||
match d.prec with
|
||||
| After | Before | About | Maybe | OrYear _ | YearInt _ ->
|
||||
loop (min, max) l
|
||||
| _ -> loop (update_min_max (min, max) d.year) l)
|
||||
| _, _ -> loop (min, max) l)
|
||||
in
|
||||
loop (10000, -10000) l
|
||||
|
||||
let rec ascendants base acc l =
|
||||
match l with
|
||||
| [] -> acc
|
||||
(* TODO type for this tuple?; why list of level? *)
|
||||
| (ip, _, _, lev) :: l -> (
|
||||
match get_parents (poi base ip) with
|
||||
| None -> ascendants base acc l
|
||||
| Some ifam ->
|
||||
let cpl = foi base ifam in
|
||||
let ifath = get_father cpl in
|
||||
let imoth = get_mother cpl in
|
||||
let acc = [ (ifath, [], ifath, lev + 1) ] @ acc in
|
||||
let acc = [ (imoth, [], imoth, lev + 1) ] @ acc in
|
||||
ascendants base acc l)
|
||||
|
||||
(* descendants des ip de liste1 sauf ceux présents dans liste2 *)
|
||||
let descendants_aux base liste1 liste2 =
|
||||
let liste2 =
|
||||
List.map
|
||||
(fun one_cousin ->
|
||||
let ip, _, _, _ = one_cousin in
|
||||
ip)
|
||||
liste2
|
||||
in
|
||||
let rec loop0 acc = function
|
||||
| [] -> acc
|
||||
| one_cousin :: l ->
|
||||
let ip, ifaml, ipar0, lev = one_cousin in
|
||||
let fams = Array.to_list (get_family (poi base ip)) in
|
||||
let chlds =
|
||||
(* accumuler tous les enfants de ip *)
|
||||
let rec loop1 acc fams =
|
||||
(* iterer sur chaque famille *)
|
||||
match fams with
|
||||
| [] -> acc
|
||||
| ifam :: fams ->
|
||||
let children =
|
||||
let rec loop2 acc2 children =
|
||||
match children with
|
||||
| [] -> acc2
|
||||
| ipch :: children ->
|
||||
loop2
|
||||
((ipch, ifam :: ifaml, ipar0, lev - 1) :: acc2)
|
||||
children
|
||||
in
|
||||
loop2 [] (Array.to_list (get_children (foi base ifam)))
|
||||
in
|
||||
loop1 (acc @ children) fams
|
||||
in
|
||||
loop1 [] fams
|
||||
in
|
||||
let chlds =
|
||||
List.fold_left (* on élimine les enfants présents dans l2 *)
|
||||
(fun acc one_cousin ->
|
||||
let ip, _ifaml, _ipar, _lev = one_cousin in
|
||||
if List.mem ip liste2 then acc else one_cousin :: acc)
|
||||
[] chlds
|
||||
in
|
||||
loop0 (chlds @ acc) l
|
||||
in
|
||||
loop0 [] liste1
|
||||
|
||||
let descendants base cousins_cnt i j =
|
||||
let liste1 = cousins_cnt.(i).(j - 1) in
|
||||
let liste2 = if i > 0 then cousins_cnt.(i - 1).(j - 1) else [] in
|
||||
descendants_aux base liste1 liste2
|
||||
|
||||
let init_cousins_cnt conf base p =
|
||||
let _max_a_l = max_ancestor_level conf base (get_iper p) mal in
|
||||
let max_a_l =
|
||||
match p_getenv conf.Config.env "v" with
|
||||
| Some v -> int_of_string v
|
||||
| None -> 3
|
||||
in
|
||||
let max_d_l = max_descendant_level conf base (get_iper p) mdl in
|
||||
|
||||
let rec loop0 j cousins_cnt cousins_dates =
|
||||
(* initiate lists of direct descendants *)
|
||||
cousins_cnt.(0).(j) <- descendants base cousins_cnt 0 j;
|
||||
cousins_dates.(0).(j) <- get_min_max_dates base cousins_cnt.(0).(j);
|
||||
if j < Array.length cousins_cnt.(0) - 1 && cousins_cnt.(0).(j) <> [] then
|
||||
loop0 (j + 1) cousins_cnt cousins_dates
|
||||
else ()
|
||||
in
|
||||
let rec loop1 i cousins_cnt cousins_dates =
|
||||
(* get ascendants *)
|
||||
cousins_cnt.(i).(0) <- ascendants base [] cousins_cnt.(i - 1).(0);
|
||||
cousins_dates.(i).(0) <- get_min_max_dates base cousins_cnt.(i).(0);
|
||||
let rec loop2 i j cousins_cnt cousins_dates =
|
||||
(* get descendants of c1, except persons of previous level (c2) *)
|
||||
cousins_cnt.(i).(j) <- descendants base cousins_cnt i j;
|
||||
cousins_dates.(i).(j) <- get_min_max_dates base cousins_cnt.(i).(j);
|
||||
if j < Array.length cousins_cnt.(0) - 1 && cousins_cnt.(i).(j) <> [] then
|
||||
loop2 i (j + 1) cousins_cnt cousins_dates
|
||||
else if
|
||||
(* TODO limit construction to l1 *)
|
||||
i < Array.length cousins_cnt - 1 && cousins_cnt.(i).(0) <> []
|
||||
then loop1 (i + 1) cousins_cnt cousins_dates
|
||||
else ()
|
||||
in
|
||||
loop2 i 1 cousins_cnt cousins_dates
|
||||
in
|
||||
|
||||
let expand_tables key v1 max_a_l cousins_cnt cousins_dates =
|
||||
Printf.sprintf "******** Expand tables from %d to %d ********\n" v1 max_a_l
|
||||
|> !GWPARAM.syslog `LOG_WARNING;
|
||||
if
|
||||
max_a_l + 3 > Sys.max_array_length
|
||||
|| max_d_l + max_a_l + 3 > Sys.max_array_length
|
||||
then failwith "Cousins table too large for system";
|
||||
let new_cousins_cnt =
|
||||
Array.make_matrix (max_a_l + 3) (max_d_l + max_a_l + 3) []
|
||||
in
|
||||
let new_cousins_dates =
|
||||
Array.make_matrix (max_a_l + 3) (max_d_l + max_a_l + 3) (0, 0)
|
||||
in
|
||||
for i = 0 to v1 do
|
||||
new_cousins_cnt.(i) <- cousins_cnt.(i);
|
||||
new_cousins_dates.(i) <- cousins_dates.(i)
|
||||
done;
|
||||
loop0 (max_d_l + v1) cousins_cnt cousins_dates;
|
||||
loop1 v1 cousins_cnt cousins_dates;
|
||||
(key, max_a_l, cousins_cnt, cousins_dates)
|
||||
in
|
||||
|
||||
let build_tables key =
|
||||
Printf.sprintf "******** Compute %d × %d table ********\n" (max_a_l + 3)
|
||||
(max_d_l + max_a_l + 3)
|
||||
|> !GWPARAM.syslog `LOG_WARNING;
|
||||
if
|
||||
max_a_l + 3 > Sys.max_array_length
|
||||
|| max_d_l + max_a_l + 3 > Sys.max_array_length
|
||||
then failwith "Cousins table too large for system";
|
||||
let () = load_ascends_array base in
|
||||
let () = load_couples_array base in
|
||||
(* +3: there may be more descendants for cousins than my own *)
|
||||
let cousins_cnt =
|
||||
Array.make_matrix (max_a_l + 3) (max_d_l + max_a_l + 3) []
|
||||
in
|
||||
let cousins_dates =
|
||||
Array.make_matrix (max_a_l + 3) (max_d_l + max_a_l + 3) (0, 0)
|
||||
in
|
||||
cousins_cnt.(0).(0) <-
|
||||
[ (get_iper p, [ Gwdb.dummy_ifam ], Gwdb.dummy_iper, 0) ];
|
||||
cousins_dates.(0).(0) <- get_min_max_dates base cousins_cnt.(0).(0);
|
||||
loop0 1 cousins_cnt cousins_dates;
|
||||
loop1 1 cousins_cnt cousins_dates;
|
||||
(key, max_a_l, cousins_cnt, cousins_dates)
|
||||
in
|
||||
|
||||
let fn = Name.strip_lower @@ sou base @@ get_surname p in
|
||||
let sn = Name.strip_lower @@ sou base @@ get_first_name p in
|
||||
let occ = get_occ p in
|
||||
let key = Format.sprintf "%s.%d.%s" fn occ sn in
|
||||
match (!cousins_t, !cousins_dates_t) with
|
||||
| Some t, Some d_t -> (t, d_t)
|
||||
| _, _ ->
|
||||
let _pnoc, _v1, t', d_t' =
|
||||
match List.assoc_opt "cache_cousins_tool" conf.Config.base_env with
|
||||
| Some "yes" -> (
|
||||
Printf.eprintf "Cache_cousins_tool=yes\n";
|
||||
flush stderr;
|
||||
let pnoc, v1, t', d_t' =
|
||||
Mutil.read_or_create_value "cousins_cache" (fun () ->
|
||||
build_tables key)
|
||||
in
|
||||
match (pnoc, v1) with
|
||||
| pnoc, v1 when pnoc = key && max_a_l <= v1 -> (pnoc, v1, t', d_t')
|
||||
| pnoc, v1 when pnoc = key ->
|
||||
let _pnoc, _v1, t', d_t' =
|
||||
Mutil.read_or_create_value "cousins_cache" (fun () ->
|
||||
build_tables key)
|
||||
in
|
||||
Sys.remove "cousins_cache";
|
||||
Mutil.read_or_create_value "cousins_cache" ~magic:key (fun () ->
|
||||
expand_tables key v1 max_a_l t' d_t')
|
||||
| _ ->
|
||||
Sys.remove "cousins_cache";
|
||||
Mutil.read_or_create_value "cousins_cache" (fun () ->
|
||||
build_tables key))
|
||||
| _ ->
|
||||
Printf.eprintf "Cache_cousins_tools=no\n";
|
||||
flush stderr;
|
||||
build_tables key
|
||||
in
|
||||
|
||||
cousins_t := Some t';
|
||||
cousins_dates_t := Some d_t';
|
||||
flush stderr;
|
||||
(t', d_t')
|
||||
|
||||
(* for cousins_dates.(l1).(l2) determine min or max date *)
|
||||
let min_max_date conf base p min_max l1 l2 =
|
||||
let _cousins_cnt, cousins_dates =
|
||||
match (!cousins_t, !cousins_dates_t) with
|
||||
| Some t, Some d_t -> (t, d_t)
|
||||
| _, _ -> init_cousins_cnt conf base p
|
||||
in
|
||||
let i = try int_of_string l1 with Failure _ -> -1 in
|
||||
let j = try int_of_string l2 with Failure _ -> -1 in
|
||||
match (i, j) with
|
||||
| -1, _ | _, -1 -> None
|
||||
| _, _ ->
|
||||
let min, max =
|
||||
if
|
||||
i + 1 > Array.length cousins_dates
|
||||
|| j + 1 > Array.length cousins_dates.(i)
|
||||
then (-1, -1)
|
||||
else cousins_dates.(i).(j)
|
||||
in
|
||||
if min_max then Some min else Some max
|
||||
|
||||
(* determine non empty max ancestor level (max_i)
|
||||
and non empty max descendant level
|
||||
*)
|
||||
let max_l1_l2 conf base p =
|
||||
let cousins_cnt, _cousins_dates =
|
||||
match (!cousins_t, !cousins_dates_t) with
|
||||
| Some t, Some d_t -> (t, d_t)
|
||||
| _, _ -> init_cousins_cnt conf base p
|
||||
in
|
||||
let max_i = Array.length cousins_cnt - 1 in
|
||||
let max_j = Array.length cousins_cnt.(0) - 1 in
|
||||
let max_a =
|
||||
let rec loop0 i =
|
||||
if cousins_cnt.(i).(0) <> [] && i < max_i - 1 then loop0 (i + 1) else i
|
||||
in
|
||||
loop0 0
|
||||
in
|
||||
let rec loop i j =
|
||||
if cousins_cnt.(i).(j) <> [] then
|
||||
if j < max_j then loop i (j + 1) else (max_a, j - i)
|
||||
else if i < max_i && j < max_j then loop (i + 1) (j + 1)
|
||||
else (max_a, j - i)
|
||||
in
|
||||
loop 0 0
|
||||
|
||||
let cousins_l1_l2_aux conf base l1 l2 p =
|
||||
let il1 = int_of_string l1 in
|
||||
let il2 = int_of_string l2 in
|
||||
let cousins_cnt, _cousins_dates =
|
||||
match (!cousins_t, !cousins_dates_t) with
|
||||
| Some t, Some d_t -> (t, d_t)
|
||||
| _, _ -> init_cousins_cnt conf base p
|
||||
in
|
||||
if il1 < Array.length cousins_cnt && il2 - il1 < Array.length cousins_cnt.(0)
|
||||
then Some cousins_cnt.(il1).(il2)
|
||||
else None
|
||||
|
||||
(* create a new list of (ip, (ifamll, iancl, cnt), lev) from one_cousin list *)
|
||||
let cousins_fold l =
|
||||
let _same_ifaml ifl1 ifl2 =
|
||||
List.for_all2 (fun if1 if2 -> if1 = if2) ifl1 ifl2
|
||||
in
|
||||
let l = List.sort compare l in
|
||||
let rec loop first acc (ip0, (ifaml0, iancl0, cnt0), lev0) = function
|
||||
| one_cousin :: l ->
|
||||
let ip, ifaml, ianc, lev = one_cousin in
|
||||
if ip = ip0 then
|
||||
loop false acc
|
||||
( ip,
|
||||
( ifaml :: ifaml0,
|
||||
(if List.mem ianc iancl0 then iancl0 else ianc :: iancl0),
|
||||
cnt0 + 1 ),
|
||||
lev :: lev0 )
|
||||
l
|
||||
else
|
||||
loop false
|
||||
(if first || cnt0 = 0 then acc
|
||||
else (ip0, (ifaml0, iancl0, cnt0), lev0) :: acc)
|
||||
(ip, ([ ifaml ], [ ianc ], 1), [ lev ])
|
||||
l
|
||||
| [] ->
|
||||
if first || cnt0 = 0 then acc
|
||||
else (ip0, (ifaml0, iancl0, cnt0), lev0) :: acc
|
||||
in
|
||||
loop false [] (Gwdb.dummy_iper, ([], [], 0), [ 0 ]) l
|
||||
|
||||
let cousins_implex_cnt conf base l1 l2 p =
|
||||
(* warning, this is expensive: two nested loops *)
|
||||
let il1 = int_of_string l1 in
|
||||
let il2 = int_of_string l2 in
|
||||
let cousins_cnt, _cousins_dates =
|
||||
match (!cousins_t, !cousins_dates_t) with
|
||||
| Some t, Some d_t -> (t, d_t)
|
||||
| _, _ -> init_cousins_cnt conf base p
|
||||
in
|
||||
let cousl0 = cousins_fold cousins_cnt.(il1).(il2) in
|
||||
let rec loop0 cousl cnt =
|
||||
match cousl with
|
||||
| [] -> cnt
|
||||
| (ip, _, _) :: cousl ->
|
||||
loop0 cousl
|
||||
(let rec loop1 cnt j =
|
||||
if j = 0 then cnt
|
||||
else
|
||||
loop1
|
||||
(let cousl_j = cousins_cnt.(il1).(j) in
|
||||
let rec loop2 cousl_j cnt =
|
||||
match cousl_j with
|
||||
| [] -> cnt
|
||||
| one_cousin :: cousl_j ->
|
||||
let ipj, _, _, _ = one_cousin in
|
||||
if ip = ipj then loop2 cousl_j (cnt + 1)
|
||||
else loop2 cousl_j cnt
|
||||
in
|
||||
loop2 cousl_j cnt)
|
||||
(j - 1)
|
||||
in
|
||||
loop1 cnt (il2 - 1))
|
||||
in
|
||||
loop0 cousl0 0
|
||||
|
||||
let asc_cnt_t = ref None
|
||||
let desc_cnt_t = ref None
|
||||
|
||||
(* tableau des ascendants de p *)
|
||||
let init_asc_cnt conf base p =
|
||||
let max_a_l = max_ancestor_level conf base (get_iper p) mal in
|
||||
match !asc_cnt_t with
|
||||
| Some t -> t
|
||||
| None ->
|
||||
let t' =
|
||||
let asc_cnt = Array.make (max_a_l + 2) [] in
|
||||
asc_cnt.(0) <- [ (get_iper p, [ Gwdb.dummy_ifam ], Gwdb.dummy_iper, 0) ];
|
||||
for i = 1 to max_a_l do
|
||||
asc_cnt.(i) <- ascendants base [] asc_cnt.(i - 1)
|
||||
done;
|
||||
asc_cnt
|
||||
in
|
||||
asc_cnt_t := Some t';
|
||||
t'
|
||||
|
||||
(* tableau des ascendants de p *)
|
||||
let init_desc_cnt conf base p =
|
||||
let max_d_l = max_descendant_level conf base (get_iper p) mdl in
|
||||
match !desc_cnt_t with
|
||||
| Some t -> t
|
||||
| None ->
|
||||
let t' =
|
||||
let desc_cnt = Array.make (max_d_l + 2) [] in
|
||||
desc_cnt.(0) <-
|
||||
[ (get_iper p, [ Gwdb.dummy_ifam ], Gwdb.dummy_iper, 0) ];
|
||||
for i = 1 to min max_d_l (Array.length desc_cnt - 1) do
|
||||
desc_cnt.(i) <- descendants_aux base desc_cnt.(i - 1) []
|
||||
done;
|
||||
desc_cnt
|
||||
in
|
||||
desc_cnt_t := Some t';
|
||||
t'
|
||||
|
||||
let anc_cnt_aux conf base lev at_to p =
|
||||
let asc_cnt =
|
||||
match !asc_cnt_t with Some t -> t | None -> init_asc_cnt conf base p
|
||||
in
|
||||
if at_to then if lev < Array.length asc_cnt then Some asc_cnt.(lev) else None
|
||||
else
|
||||
let rec loop acc i =
|
||||
if i > lev || i >= Array.length asc_cnt - 1 then Some acc
|
||||
else loop (asc_cnt.(i) @ acc) (i + 1)
|
||||
in
|
||||
loop [] 1
|
||||
|
||||
let desc_cnt_aux conf base lev at_to p =
|
||||
let desc_cnt =
|
||||
match !desc_cnt_t with Some t -> t | None -> init_desc_cnt conf base p
|
||||
in
|
||||
if at_to then
|
||||
if lev < Array.length desc_cnt then Some desc_cnt.(lev) else None
|
||||
else
|
||||
let rec loop acc i =
|
||||
if i > lev || i > Array.length desc_cnt - 1 then Some acc
|
||||
else loop (desc_cnt.(i) @ acc) (i + 1)
|
||||
in
|
||||
loop [] 0
|
||||
|
||||
(* end cousins *)
|
||||
134
lib/cousins.mli
Normal file
134
lib/cousins.mli
Normal file
@@ -0,0 +1,134 @@
|
||||
open Gwdb
|
||||
open Config
|
||||
|
||||
type one_cousin =
|
||||
Gwdb_driver.iper * Gwdb_driver.ifam list * Gwdb_driver.iper * int
|
||||
|
||||
type cousins_i_j = one_cousin list
|
||||
|
||||
val cousins_table : cousins_i_j array array
|
||||
val cousins_dates_t : (int * int) array array option ref
|
||||
|
||||
val default_max_cnt : int
|
||||
(** Default number of relatives that could be listed at the same page *)
|
||||
|
||||
val mal : int
|
||||
(** max value of max_ancestor_level *)
|
||||
|
||||
val mdl : int
|
||||
(** max value of max_descendant_level *)
|
||||
|
||||
val max_cousin_level : config -> int
|
||||
val max_ancestor_level : config -> base -> iper -> int -> int
|
||||
val max_descendant_level : config -> base -> iper -> int -> int
|
||||
|
||||
val children_of_fam : base -> ifam -> iper list
|
||||
(** Retruns list of children of the giving family *)
|
||||
|
||||
val siblings : config -> base -> iper -> (iper * (iper * Def.sex)) list
|
||||
(** Returns list of person's siblings that includes also half-blood siblings. Every sibling
|
||||
is annotated with parent's id and parent's sex. For common father's and mother's
|
||||
children father's annotation is preserved. *)
|
||||
|
||||
val has_desc_lev : config -> base -> int -> person -> bool
|
||||
(** [has_desc_lev conf base lev p] tells if person [p] has descendants at the level [lev].
|
||||
[lev] 2 represents his children, 3 represents grandchildren, etc. *)
|
||||
|
||||
val br_inter_is_empty : ('a * 'b) list -> ('a * 'c) list -> bool
|
||||
(** Tells if two family branches don't itersect *)
|
||||
|
||||
val sibling_has_desc_lev : config -> base -> int -> iper * 'a -> bool
|
||||
(** Same as [has_desc_lev] but used for a person's sibling as returned by [siblings]. *)
|
||||
|
||||
(* Functions to obtain info about cousins of a person *)
|
||||
|
||||
(* The various functions are typically called with two parameters:
|
||||
- l1 : number of generations up
|
||||
- l2 : number of generations down
|
||||
0, 0 is myself
|
||||
1, 1 is my brothers ans sisters
|
||||
2, 1 is my uncle and aunts
|
||||
2, 2 is my cousins
|
||||
etc
|
||||
*)
|
||||
|
||||
val init_cousins_cnt :
|
||||
config ->
|
||||
base ->
|
||||
person ->
|
||||
(Gwdb_driver.iper * Gwdb_driver.ifam list * Gwdb_driver.iper * int) list array
|
||||
array
|
||||
* (int * int) array array
|
||||
(** initialise
|
||||
- a 2D array of lists of cousins at l1, l2
|
||||
for each cousin, record
|
||||
- his family,
|
||||
- his parent (through which he is reached),
|
||||
- the level (possibly multiple levels dut to implex)
|
||||
- a 2D array of tuples (min, max) for dates of cousins at l1, l2
|
||||
*)
|
||||
|
||||
val min_max_date :
|
||||
config -> base -> person -> bool -> string -> string -> int option
|
||||
(** for cousins_dates.(l1).(l2) determine min or max date *)
|
||||
|
||||
val max_l1_l2 : config -> base -> person -> int * int
|
||||
(** determine non empty max ancestor level (l1)
|
||||
and non empty max descendant level *)
|
||||
|
||||
val cousins_l1_l2_aux :
|
||||
config ->
|
||||
base ->
|
||||
string ->
|
||||
(* up l1 generations *)
|
||||
string ->
|
||||
(* down l2 generations *)
|
||||
person ->
|
||||
(Gwdb_driver.iper * Gwdb_driver.ifam list * Gwdb_driver.iper * int) list
|
||||
option
|
||||
|
||||
val cousins_implex_cnt :
|
||||
config ->
|
||||
base ->
|
||||
string ->
|
||||
(* up l1 generations *)
|
||||
string ->
|
||||
(* down l2 generations *)
|
||||
person ->
|
||||
int
|
||||
(** for a list of "cousins" at level l1 l2,
|
||||
cousins_implex computes cousins already seen at levels l < l2. *)
|
||||
|
||||
val cousins_fold :
|
||||
(Gwdb_driver.iper * Gwdb_driver.ifam list * Gwdb_driver.iper * int) list ->
|
||||
(Gwdb_driver.iper
|
||||
* (Gwdb_driver.ifam list list * Gwdb_driver.iper list * int)
|
||||
* int list)
|
||||
list
|
||||
(** create a new list of (ip, (ifamll, iancl, cnt), lev) from list of (ip, ifaml, ianc, lev)
|
||||
The effect is to assemble multiple items under a single ip
|
||||
*)
|
||||
|
||||
val anc_cnt_aux :
|
||||
config ->
|
||||
base ->
|
||||
int ->
|
||||
(* level *)
|
||||
bool ->
|
||||
(* up to ot at *)
|
||||
person ->
|
||||
(Gwdb_driver.iper * Gwdb_driver.ifam list * Gwdb_driver.iper * int) list
|
||||
option
|
||||
(** Get the list of ancestors up to or at level *)
|
||||
|
||||
val desc_cnt_aux :
|
||||
config ->
|
||||
base ->
|
||||
int ->
|
||||
(* level *)
|
||||
bool ->
|
||||
(* up to ot at *)
|
||||
person ->
|
||||
(Gwdb_driver.iper * Gwdb_driver.ifam list * Gwdb_driver.iper * int) list
|
||||
option
|
||||
(** Get the list of descendants up to or at level *)
|
||||
425
lib/cousinsDisplay.ml
Normal file
425
lib/cousinsDisplay.ml
Normal file
@@ -0,0 +1,425 @@
|
||||
(* Copyright (c) 1998-2007 INRIA *)
|
||||
|
||||
open Config
|
||||
open Def
|
||||
open Gwdb
|
||||
open Util
|
||||
open Cousins
|
||||
|
||||
let default_max_cnt = Cousins.default_max_cnt
|
||||
|
||||
(* there is a mismatch between cousins degree and "v1" parameter
|
||||
which is the number of generation we go back to find a common ancestor *)
|
||||
let _brother_label conf x =
|
||||
match x with
|
||||
| 1 -> transl conf "siblings"
|
||||
| 2 -> transl conf "cousins"
|
||||
| 3 -> transl conf "2nd cousins"
|
||||
| 4 -> transl conf "3rd cousins"
|
||||
| n ->
|
||||
Printf.sprintf
|
||||
(ftransl conf "%s cousins")
|
||||
(transl_nth conf "nth (cousin)" (n - 1))
|
||||
|
||||
let cnt = ref 0
|
||||
let cnt_sp = ref 0
|
||||
|
||||
let give_access conf base ~cnt_sp ia_asex p1 b1 p2 b2 =
|
||||
let sps = Util.get_opt conf "sp" true in
|
||||
let img = Util.get_opt conf "im" true in
|
||||
let reference _ _ p (s : Adef.safe_string) =
|
||||
if is_hidden p then s
|
||||
else
|
||||
Printf.sprintf {|<a href="%sm=RL&%s&b1=%s&%s&b2=%s%s%s&bd=%s">%s</a>|}
|
||||
(commd conf :> string)
|
||||
(acces_n conf base (Adef.escaped "1") p1 :> string)
|
||||
(Sosa.to_string (Util.old_sosa_of_branch conf base (ia_asex :: b1)))
|
||||
(acces_n conf base (Adef.escaped "2") p2 :> string)
|
||||
(Sosa.to_string (Util.old_sosa_of_branch conf base (ia_asex :: b2)))
|
||||
(if sps then "" else "&sp=0")
|
||||
(if img then "" else "&im=0")
|
||||
(Option.value ~default:(Adef.encoded "0") (List.assoc_opt "bd" conf.env)
|
||||
:> string)
|
||||
(s :> string)
|
||||
|> Adef.safe
|
||||
in
|
||||
|
||||
let reference_sp p3 _ _ p (s : Adef.safe_string) =
|
||||
if is_hidden p then s
|
||||
else
|
||||
Printf.sprintf {|<a href="%sm=RL&%s&b1=%s&%s&b2=%s&%s%s%s&bd=%s">%s</a>|}
|
||||
(commd conf :> string)
|
||||
(acces_n conf base (Adef.escaped "1") p1 :> string)
|
||||
(Sosa.to_string (Util.old_sosa_of_branch conf base (ia_asex :: b1)))
|
||||
(acces_n conf base (Adef.escaped "2") p2 :> string)
|
||||
(Sosa.to_string (Util.old_sosa_of_branch conf base (ia_asex :: b2)))
|
||||
(acces_n conf base (Adef.escaped "4") p3 :> string)
|
||||
(if sps then "" else "&sp=0")
|
||||
(if img then "" else "&im=0")
|
||||
(Option.value ~default:(Adef.encoded "0") (List.assoc_opt "bd" conf.env)
|
||||
:> string)
|
||||
(s :> string)
|
||||
|> Adef.safe
|
||||
in
|
||||
let print_nospouse _ =
|
||||
SosaCache.print_sosa conf base p2 true;
|
||||
Output.print_string conf (gen_person_title_text reference conf base p2);
|
||||
Output.print_string conf (DateDisplay.short_dates_text conf base p2)
|
||||
in
|
||||
let print_spouse sp first =
|
||||
incr cnt_sp;
|
||||
if first then (
|
||||
SosaCache.print_sosa conf base p2 true;
|
||||
Output.print_string conf (gen_person_title_text reference conf base p2))
|
||||
else (
|
||||
Output.print_sstring conf "<br>";
|
||||
Output.print_string conf (person_title_text conf base p2));
|
||||
Output.print_string conf (DateDisplay.short_dates_text conf base p2);
|
||||
Output.print_sstring conf " & ";
|
||||
SosaCache.print_sosa conf base sp true;
|
||||
Output.print_string conf
|
||||
(gen_person_title_text (reference_sp sp) conf base sp);
|
||||
Output.print_string conf (DateDisplay.short_dates_text conf base sp)
|
||||
in
|
||||
if p_getenv conf.env "spouse" = Some "on" then
|
||||
match get_family p2 with
|
||||
| [||] -> print_nospouse ()
|
||||
| u ->
|
||||
Array.iteri
|
||||
(fun i ifam ->
|
||||
let cpl = foi base ifam in
|
||||
let sp =
|
||||
if get_sex p2 = Female then pget conf base (get_father cpl)
|
||||
else pget conf base (get_mother cpl)
|
||||
in
|
||||
print_spouse sp (i = 0))
|
||||
u
|
||||
else print_nospouse ()
|
||||
|
||||
let rec print_descend_upto conf base max_cnt ini_p ini_br lev children =
|
||||
if lev > 0 && !cnt < max_cnt then (
|
||||
Output.print_sstring conf "<ul>\n";
|
||||
List.iter
|
||||
(fun (ip, ia_asex, rev_br) ->
|
||||
let p = pget conf base ip in
|
||||
(* détecter l'époux de p, parent des enfants qui seront listés *)
|
||||
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
|
||||
(* if more than one spouse, this will be split on multiple lines *)
|
||||
(* we ignore the case where two spouses, but only one with descendants! *)
|
||||
let with_sp =
|
||||
if Array.length (get_family p) = 1 then
|
||||
let sp = get_spouse base ip (get_family p).(0) in
|
||||
" " ^<^ Util.transl conf "with" ^<^ " "
|
||||
^<^ person_title_text conf base sp
|
||||
else Adef.safe ""
|
||||
in
|
||||
let br = List.rev ((ip, get_sex p) :: rev_br) in
|
||||
let is_valid_rel = br_inter_is_empty ini_br br in
|
||||
if is_valid_rel && !cnt < max_cnt && has_desc_lev conf base lev p then (
|
||||
if lev <= 2 then (
|
||||
Output.print_sstring conf "<li>";
|
||||
if lev = 1 then (
|
||||
give_access conf base ~cnt_sp ia_asex ini_p ini_br p br;
|
||||
incr cnt)
|
||||
else
|
||||
let s : Adef.safe_string = person_title_text conf base p in
|
||||
transl_a_of_gr_eq_gen_lev conf
|
||||
(transl_nth conf "child/children" 1)
|
||||
(s :> string)
|
||||
(s :> string)
|
||||
|> Util.translate_eval |> Utf8.capitalize_fst
|
||||
|> Output.print_sstring conf;
|
||||
Output.print_string conf with_sp;
|
||||
Output.print_sstring conf (Util.transl conf ":");
|
||||
Output.print_sstring conf
|
||||
(if (with_sp :> string) = "" then "<br>" else " "));
|
||||
(* the function children_of returns *all* the children of ip *)
|
||||
Array.iter
|
||||
(fun ifam ->
|
||||
let children =
|
||||
List.map
|
||||
(fun i -> (i, ia_asex, (get_iper p, get_sex p) :: rev_br))
|
||||
(children_of_fam base ifam)
|
||||
in
|
||||
let sp = get_spouse base ip ifam in
|
||||
if
|
||||
Array.length (get_family p) > 1
|
||||
&& lev >= 2
|
||||
&& List.length children > 0
|
||||
&& has_desc_lev conf base lev sp
|
||||
then (
|
||||
Output.print_sstring conf (Util.transl conf "with");
|
||||
Output.print_sstring conf " ";
|
||||
Output.print_string conf (person_title_text conf base sp);
|
||||
Output.print_sstring conf (Util.transl conf ":"));
|
||||
print_descend_upto conf base max_cnt ini_p ini_br (lev - 1)
|
||||
children)
|
||||
(get_family p);
|
||||
if lev <= 2 then Output.print_sstring conf "</li>"))
|
||||
children;
|
||||
Output.print_sstring conf "</ul>")
|
||||
|
||||
let print_cousins_side_of conf base max_cnt a ini_p ini_br lev1 lev2 =
|
||||
let sib = siblings conf base (get_iper a) in
|
||||
if List.exists (sibling_has_desc_lev conf base lev2) sib then (
|
||||
if lev1 > 1 then (
|
||||
Output.print_sstring conf "<li>";
|
||||
[
|
||||
(gen_person_title_text no_reference conf base a
|
||||
: Adef.safe_string
|
||||
:> string);
|
||||
]
|
||||
|> cftransl conf "on %s's siblings side"
|
||||
|> Utf8.capitalize_fst |> Output.print_sstring conf;
|
||||
Output.print_sstring conf (Util.transl conf ":"));
|
||||
let sib = List.map (fun (ip, ia_asex) -> (ip, ia_asex, [])) sib in
|
||||
print_descend_upto conf base max_cnt ini_p ini_br lev2 sib;
|
||||
if lev1 > 1 then Output.print_sstring conf "</li>";
|
||||
true)
|
||||
else false
|
||||
|
||||
let print_cousins_lev conf base max_cnt p lev1 lev2 =
|
||||
let first_sosa =
|
||||
let rec loop sosa lev =
|
||||
if lev <= 1 then sosa else loop (Sosa.twice sosa) (lev - 1)
|
||||
in
|
||||
loop Sosa.one lev1
|
||||
in
|
||||
let last_sosa = Sosa.twice first_sosa in
|
||||
Util.print_tips_relationship conf;
|
||||
if lev1 > 1 then Output.print_sstring conf "<ul>";
|
||||
let some =
|
||||
let rec loop sosa some =
|
||||
if !cnt < max_cnt && Sosa.gt last_sosa sosa then
|
||||
let some =
|
||||
match Util.old_branch_of_sosa conf base (get_iper p) sosa with
|
||||
| Some ((ia, _) :: _ as br) ->
|
||||
print_cousins_side_of conf base max_cnt (pget conf base ia) p br
|
||||
lev1 lev2
|
||||
|| some
|
||||
| _ -> some
|
||||
in
|
||||
loop (Sosa.inc sosa 1) some
|
||||
else some
|
||||
in
|
||||
loop first_sosa false
|
||||
in
|
||||
if not some then (
|
||||
Output.print_sstring conf (Utf8.capitalize_fst (transl conf "no match"));
|
||||
Output.print_sstring conf ". ");
|
||||
if lev1 > 1 then Output.print_sstring conf "</ul>"
|
||||
|
||||
(* HTML main *)
|
||||
|
||||
let print_cousins conf base p lev1 lev2 =
|
||||
let title _h =
|
||||
let cous12 = Format.sprintf "cousins.%d.%d" lev1 lev2 in
|
||||
let cous_transl = Utf8.capitalize_fst (transl_nth conf cous12 1) in
|
||||
if String.length cous_transl > 0 && cous_transl.[0] <> '[' then
|
||||
Output.print_sstring conf cous_transl
|
||||
else
|
||||
Output.printf conf "%s %s / %s %s" (string_of_int lev1)
|
||||
(transl_nth conf "ascending/descending (degree)"
|
||||
(if lev1 = 1 then 0 else 2))
|
||||
(string_of_int lev2)
|
||||
(transl_nth conf "ascending/descending (degree)"
|
||||
(if lev2 = 1 then 1 else 3))
|
||||
in
|
||||
|
||||
let max_cnt =
|
||||
try int_of_string (List.assoc "max_cousins" conf.base_env)
|
||||
with Not_found | Failure _ -> default_max_cnt
|
||||
in
|
||||
Perso.interp_notempl_with_menu title "perso_header" conf base p;
|
||||
Output.print_sstring conf "<div>";
|
||||
(*include_templ conf "cousins_tools";*)
|
||||
Output.print_sstring conf "<h3>";
|
||||
title false;
|
||||
Output.print_sstring conf "</h3>";
|
||||
Output.print_sstring conf "</div>";
|
||||
cnt := 0;
|
||||
(* Construction de la table des sosa de la base *)
|
||||
let () = SosaCache.build_sosa_ht conf base in
|
||||
print_cousins_lev conf base max_cnt p lev1 lev2;
|
||||
Output.print_sstring conf "<div><p>";
|
||||
if !cnt >= max_cnt then Output.print_sstring conf "etc... "
|
||||
else if !cnt > 1 then (
|
||||
Output.print_sstring conf (Utf8.capitalize_fst (transl conf "total"));
|
||||
Output.print_sstring conf (Util.transl conf ":");
|
||||
Output.print_sstring conf " ";
|
||||
Output.print_sstring conf (string_of_int !cnt);
|
||||
Output.print_sstring conf " ";
|
||||
Output.print_sstring conf
|
||||
(Util.translate_eval ("@(c)" ^ transl_nth conf "person/persons" 1)));
|
||||
if p_getenv conf.env "spouse" = Some "on" then (
|
||||
Output.print_sstring conf " ";
|
||||
Output.print_sstring conf (transl conf "and");
|
||||
Output.print_sstring conf " ";
|
||||
Output.print_sstring conf (string_of_int !cnt_sp);
|
||||
Output.print_sstring conf " ";
|
||||
Output.print_sstring conf
|
||||
(Util.translate_eval ("@(c)" ^ transl_nth conf "spouse/spouses" 1));
|
||||
Output.print_sstring conf ". ")
|
||||
else Output.print_sstring conf ". ";
|
||||
Output.print_sstring conf "</p></div>";
|
||||
Hutil.trailer conf
|
||||
|
||||
(* TODO use Sosa module instead *)
|
||||
let sosa_of_persons conf base =
|
||||
let rec loop n = function
|
||||
| [] -> n
|
||||
| ip :: list ->
|
||||
(* do no works if sex = Neuter *)
|
||||
loop
|
||||
(if get_sex (pget conf base ip) = Male then 2 * n else (2 * n) + 1)
|
||||
list
|
||||
in
|
||||
loop 1
|
||||
|
||||
let print_anniv conf base p dead_people level =
|
||||
let module S = Map.Make (struct
|
||||
type t = iper
|
||||
|
||||
let compare = compare
|
||||
end) in
|
||||
let s_mem x m =
|
||||
try
|
||||
let _ = S.find x m in
|
||||
true
|
||||
with Not_found -> false
|
||||
in
|
||||
let rec insert_desc set up_sosa down_br n ip =
|
||||
if s_mem ip set then set
|
||||
else
|
||||
let set = S.add ip (up_sosa, down_br) set in
|
||||
if n = 0 then set
|
||||
else
|
||||
let u = get_family (pget conf base ip) in
|
||||
let down_br = ip :: down_br in
|
||||
let rec loop set i =
|
||||
if i = Array.length u then set
|
||||
else
|
||||
let chil = get_children (foi base u.(i)) in
|
||||
let set =
|
||||
let rec loop set i =
|
||||
if i = Array.length chil then set
|
||||
else
|
||||
let set = insert_desc set up_sosa down_br (n - 1) chil.(i) in
|
||||
loop set (i + 1)
|
||||
in
|
||||
loop set 0
|
||||
in
|
||||
loop set (i + 1)
|
||||
in
|
||||
loop set 0
|
||||
in
|
||||
let set =
|
||||
let module P = Pqueue.Make (struct
|
||||
type t = iper * int * int
|
||||
|
||||
let leq (_, lev1, _) (_, lev2, _) = lev1 <= lev2
|
||||
end) in
|
||||
let a = P.add (get_iper p, 0, 1) P.empty in
|
||||
let rec loop set a =
|
||||
if P.is_empty a then set
|
||||
else
|
||||
let (ip, n, up_sosa), a = P.take a in
|
||||
let set = insert_desc set up_sosa [] (n + 3) ip in
|
||||
if n >= level then set
|
||||
else
|
||||
let a =
|
||||
match get_parents (pget conf base ip) with
|
||||
| Some ifam ->
|
||||
let cpl = foi base ifam in
|
||||
let n = n + 1 in
|
||||
let up_sosa = 2 * up_sosa in
|
||||
let a = P.add (get_father cpl, n, up_sosa) a in
|
||||
P.add (get_mother cpl, n, up_sosa + 1) a
|
||||
| None -> a
|
||||
in
|
||||
loop set a
|
||||
in
|
||||
loop S.empty a
|
||||
in
|
||||
let set =
|
||||
S.fold
|
||||
(fun ip (up_sosa, down_br) set ->
|
||||
let u = get_family (pget conf base ip) in
|
||||
let set = S.add ip (up_sosa, down_br, None) set in
|
||||
if Array.length u = 0 then set
|
||||
else
|
||||
let rec loop set i =
|
||||
if i = Array.length u then set
|
||||
else
|
||||
let cpl = foi base u.(i) in
|
||||
let c = Gutil.spouse ip cpl in
|
||||
loop (S.add c (up_sosa, down_br, Some ip) set) (i + 1)
|
||||
in
|
||||
loop set 0)
|
||||
set S.empty
|
||||
in
|
||||
let txt_of (up_sosa, down_br, spouse) conf base c =
|
||||
Printf.sprintf {|<a href="m=RL&%s&b1=%d&%s&b2=%d%s">%s</a>|}
|
||||
(acces_n conf base (Adef.escaped "1") p :> string)
|
||||
up_sosa
|
||||
(acces_n conf base (Adef.escaped "2")
|
||||
(Option.fold ~none:c ~some:(pget conf base) spouse)
|
||||
:> string)
|
||||
(sosa_of_persons conf base down_br)
|
||||
(if spouse = None then
|
||||
"&" ^ (acces_n conf base (Adef.escaped "4") c :> string)
|
||||
else "")
|
||||
(person_title_text conf base c :> string)
|
||||
|> Adef.safe
|
||||
in
|
||||
let f_scan =
|
||||
let list = ref (S.fold (fun ip b list -> (ip, b) :: list) set []) in
|
||||
fun () ->
|
||||
match !list with
|
||||
| (x, b) :: l ->
|
||||
list := l;
|
||||
(pget conf base x, txt_of b)
|
||||
| [] -> raise Not_found
|
||||
in
|
||||
let mode () =
|
||||
Util.hidden_input conf "m" (Adef.encoded "C");
|
||||
Util.hidden_input conf "i" (get_iper p |> string_of_iper |> Adef.encoded);
|
||||
Util.hidden_input conf "t"
|
||||
(Adef.encoded (if dead_people then "AD" else "AN"))
|
||||
in
|
||||
match p_getint conf.env "v" with
|
||||
| Some i -> BirthdayDisplay.gen_print conf base i f_scan dead_people
|
||||
| _ ->
|
||||
if dead_people then
|
||||
BirthdayDisplay.gen_print_menu_dead conf base f_scan mode
|
||||
else BirthdayDisplay.gen_print_menu_birth conf base f_scan mode
|
||||
|
||||
let cousmenu_print = Perso.interp_templ "cousmenu"
|
||||
|
||||
let print conf base p =
|
||||
let max_lvl = max_cousin_level conf in
|
||||
(* v1 is the number of generation we go up to get a common ancestor,
|
||||
v2 is the number of generation we go down from the ancestor.
|
||||
e.g.
|
||||
(v1,v2) = (1,1) are their sisters/brothers
|
||||
(v1,v2) = (2,2) are their "cousins" *)
|
||||
match
|
||||
(p_getint conf.env "v1", p_getint conf.env "v2", p_getenv conf.env "t")
|
||||
with
|
||||
| Some 1, Some 1, _ | Some 0, _, _ | _, Some 0, _ ->
|
||||
Perso.interp_templ "cousins" conf base p
|
||||
| Some lvl1, _, _ ->
|
||||
let lvl1 = min (max 1 lvl1) max_lvl in
|
||||
let lvl2 =
|
||||
match p_getint conf.env "v2" with
|
||||
| Some lvl2 -> min (max 1 lvl2) max_lvl
|
||||
| None -> lvl1
|
||||
in
|
||||
print_cousins conf base p lvl1 lvl2
|
||||
| _, _, Some (("AN" | "AD") as t) when conf.wizard || conf.friend ->
|
||||
print_anniv conf base p (t = "AD") max_lvl
|
||||
| _ -> cousmenu_print conf base p
|
||||
14
lib/cousinsDisplay.mli
Normal file
14
lib/cousinsDisplay.mli
Normal file
@@ -0,0 +1,14 @@
|
||||
val print : Config.config -> Gwdb.base -> Gwdb.person -> unit
|
||||
(** Displays the menu that lists all person's relatives depending on ancestor and his descandant levels
|
||||
specified by [conf.env] variables {i v1} (for ancestor) and {i v2} for his descandant. For exemple :
|
||||
|
||||
"v1" = 1, "v2" = 1 - Displays all person's siblings (mount to the person's parent (ancestor of level 1)
|
||||
and lists all his children (descandant of level 1));
|
||||
"v1" = 2, "v2" = 2 - Displays all cousins;
|
||||
"v1" = 2, "v2" = 1 - Displays all uncles/aunts;
|
||||
"v1" = 1, "v2" = 2 - Displays all nieces/nephews;
|
||||
etc.
|
||||
|
||||
Variable "t" is used to display anniversaries for relatives like [BirthdayDisplay.gen_print].
|
||||
If nor of those variables are defined, prints menu that allows to access the most common relatives (except for direct relatives)
|
||||
like cousins, siblings, uncles/aunts, etc. *)
|
||||
141
lib/dag.ml
Normal file
141
lib/dag.ml
Normal file
@@ -0,0 +1,141 @@
|
||||
open Config
|
||||
open Dag2html
|
||||
open Def
|
||||
open Gwdb
|
||||
open Util
|
||||
|
||||
(* TODO use a set.. *)
|
||||
module Pset = struct
|
||||
type t = iper list
|
||||
type elt = iper
|
||||
|
||||
let add e s = if List.mem e s then s else e :: s
|
||||
let empty = []
|
||||
let elements s = List.rev s
|
||||
let mem = List.mem
|
||||
end
|
||||
|
||||
(* input dag *)
|
||||
|
||||
let get_dag_elems conf base =
|
||||
let rec loop prev_po set i =
|
||||
let s = string_of_int i in
|
||||
let po = Util.find_person_in_env conf base s in
|
||||
let po = match po with None -> prev_po | x -> x in
|
||||
let so = Util.p_getenv conf.env ("s" ^ s) in
|
||||
match (po, so) with
|
||||
| Some p, Some s ->
|
||||
let set =
|
||||
match Util.branch_of_sosa conf base (Sosa.of_string s) p with
|
||||
| Some ipsl ->
|
||||
List.fold_left (fun set p -> Pset.add (get_iper p) set) set ipsl
|
||||
| None -> set
|
||||
in
|
||||
loop po set (i + 1)
|
||||
| _ -> set
|
||||
in
|
||||
loop None Pset.empty 1
|
||||
|
||||
type ('a, 'b) sum = ('a, 'b) Def.choice
|
||||
|
||||
let make_dag conf base set =
|
||||
let list = Pset.elements set in
|
||||
let module O = struct
|
||||
type t = iper
|
||||
|
||||
let compare = compare
|
||||
end in
|
||||
let module M = Map.Make (O) in
|
||||
let nodes = Array.of_list list in
|
||||
let map =
|
||||
let rec loop map i =
|
||||
if i = Array.length nodes then map
|
||||
else loop (M.add nodes.(i) (idag_of_int i) map) (i + 1)
|
||||
in
|
||||
loop M.empty 0
|
||||
in
|
||||
let nodes =
|
||||
Array.map
|
||||
(fun ip ->
|
||||
let pare =
|
||||
match get_parents (pget conf base ip) with
|
||||
| Some ifam -> (
|
||||
let c = foi base ifam in
|
||||
let l =
|
||||
try [ M.find (get_mother c) map ] with Not_found -> []
|
||||
in
|
||||
try M.find (get_father c) map :: l with Not_found -> l)
|
||||
| None -> []
|
||||
in
|
||||
let chil =
|
||||
let u = pget conf base ip in
|
||||
Array.fold_left
|
||||
(fun chil ifam ->
|
||||
let des = foi base ifam in
|
||||
Array.fold_left
|
||||
(fun chil ip ->
|
||||
try M.find ip map :: chil with Not_found -> chil)
|
||||
chil (get_children des))
|
||||
[] (get_family u)
|
||||
in
|
||||
let chil = List.rev chil in
|
||||
{ pare; valu = Left ip; chil })
|
||||
nodes
|
||||
in
|
||||
let nodes =
|
||||
let rec loop nodes n i =
|
||||
if i = Array.length nodes then nodes
|
||||
else
|
||||
match nodes.(i) with
|
||||
| { valu = Left ip; chil } ->
|
||||
let ifaml = Array.to_list (get_family (pget conf base ip)) in
|
||||
let nodes, n =
|
||||
let rec loop nodes = function
|
||||
| ifam :: ifaml -> (
|
||||
let cpl = foi base ifam in
|
||||
let isp = Gutil.spouse ip cpl in
|
||||
let jdo =
|
||||
try Some (M.find isp map) with Not_found -> None
|
||||
in
|
||||
match jdo with
|
||||
| Some jd ->
|
||||
let j = int_of_idag jd in
|
||||
if chil = [] && nodes.(j).chil = [] then (
|
||||
let pare = [ idag_of_int i; jd ] in
|
||||
let d = { pare; valu = Right n; chil = [] } in
|
||||
let nodes = Array.append nodes [| d |] in
|
||||
let nd = idag_of_int n in
|
||||
nodes.(i).chil <- [ nd ];
|
||||
nodes.(j).chil <- [ nd ];
|
||||
(nodes, n + 1))
|
||||
else if chil <> nodes.(j).chil then (
|
||||
List.iter
|
||||
(fun nd ->
|
||||
if List.mem nd nodes.(j).chil then ()
|
||||
else
|
||||
let n = int_of_idag nd in
|
||||
nodes.(j).chil <- nd :: nodes.(j).chil;
|
||||
nodes.(n).pare <- jd :: nodes.(n).pare)
|
||||
chil;
|
||||
List.iter
|
||||
(fun nd ->
|
||||
if List.mem nd chil then ()
|
||||
else
|
||||
let id = idag_of_int i in
|
||||
let n = int_of_idag nd in
|
||||
nodes.(i).chil <- nd :: chil;
|
||||
nodes.(n).pare <- id :: nodes.(n).pare)
|
||||
nodes.(j).chil;
|
||||
loop nodes ifaml)
|
||||
else loop nodes ifaml
|
||||
| None -> loop nodes ifaml)
|
||||
| [] -> (nodes, n)
|
||||
in
|
||||
loop nodes ifaml
|
||||
in
|
||||
loop nodes n (i + 1)
|
||||
| _ -> loop nodes n (i + 1)
|
||||
in
|
||||
loop nodes (Array.length nodes) 0
|
||||
in
|
||||
{ dag = nodes }
|
||||
20
lib/dag.mli
Normal file
20
lib/dag.mli
Normal file
@@ -0,0 +1,20 @@
|
||||
(* TODOCP *)
|
||||
module Pset : sig
|
||||
type t = Gwdb.iper list
|
||||
type elt = Gwdb.iper
|
||||
|
||||
val add : 'a -> 'a list -> 'a list
|
||||
val empty : 'a list
|
||||
val elements : 'a list -> 'a list
|
||||
val mem : 'a -> 'a list -> bool
|
||||
end
|
||||
|
||||
val get_dag_elems : Config.config -> Gwdb.base -> Gwdb.iper list
|
||||
|
||||
type ('a, 'b) sum = ('a, 'b) Def.choice
|
||||
|
||||
val make_dag :
|
||||
Config.config ->
|
||||
Gwdb.base ->
|
||||
Gwdb.iper list ->
|
||||
(Gwdb.iper, int) Def.choice Dag2html.dag
|
||||
1417
lib/dag2html.ml
Normal file
1417
lib/dag2html.ml
Normal file
File diff suppressed because it is too large
Load Diff
40
lib/dag2html.mli
Normal file
40
lib/dag2html.mli
Normal file
@@ -0,0 +1,40 @@
|
||||
(* $Id: dag2html.mli,v 5.0 2005-12-13 11:51:26 ddr Exp $ *)
|
||||
open Gwdb
|
||||
|
||||
(* TODOCP *)
|
||||
type 'a dag = { mutable dag : 'a node array }
|
||||
and 'a node = { mutable pare : idag list; valu : 'a; mutable chil : idag list }
|
||||
and idag
|
||||
|
||||
external int_of_idag : idag -> int = "%identity"
|
||||
external idag_of_int : int -> idag = "%identity"
|
||||
|
||||
type 'a table = { mutable table : 'a data array array }
|
||||
and 'a data = { mutable elem : 'a elem; mutable span : span_id }
|
||||
and 'a elem = Elem of 'a | Ghost of ghost_id | Nothing
|
||||
and span_id
|
||||
and ghost_id
|
||||
|
||||
type align = LeftA | CenterA | RightA
|
||||
|
||||
type 'a table_data =
|
||||
| TDitem of iper * 'a * Adef.safe_string
|
||||
| TDtext of iper * Adef.safe_string
|
||||
| TDhr of align
|
||||
| TDbar of Adef.escaped_string option
|
||||
| TDnothing
|
||||
|
||||
type 'a html_table_line = (int * align * 'a table_data) array
|
||||
type 'a html_table = 'a html_table_line array
|
||||
|
||||
val html_table_struct :
|
||||
('a node -> iper) ->
|
||||
('a node -> 'b) ->
|
||||
('a node -> Adef.escaped_string) ->
|
||||
('a node -> bool) ->
|
||||
'a dag ->
|
||||
idag table ->
|
||||
(int * align * 'b table_data) array array
|
||||
|
||||
val table_of_dag :
|
||||
('a node -> bool) -> bool -> bool -> bool -> 'a dag -> idag table
|
||||
1373
lib/dagDisplay.ml
Normal file
1373
lib/dagDisplay.ml
Normal file
File diff suppressed because it is too large
Load Diff
40
lib/dagDisplay.mli
Normal file
40
lib/dagDisplay.mli
Normal file
@@ -0,0 +1,40 @@
|
||||
(* TODOOCP *)
|
||||
val image_txt : Config.config -> Gwdb.base -> Gwdb.person -> Adef.safe_string
|
||||
|
||||
type item = Item of Gwdb.person * Adef.safe_string
|
||||
|
||||
val make_tree_hts :
|
||||
Config.config ->
|
||||
Gwdb.base ->
|
||||
(Gwdb.person -> item) ->
|
||||
(Gwdb.iper -> Adef.escaped_string) ->
|
||||
bool ->
|
||||
Gwdb.iper list ->
|
||||
(Gwdb.iper * (Gwdb.iper * Gwdb.ifam option)) list ->
|
||||
(Gwdb.iper, 'a) Def.choice Dag2html.dag ->
|
||||
(int * Dag2html.align * Adef.safe_string Dag2html.table_data) array array
|
||||
(** [make_tree_hts conf base elem_txt vbar_txt invert set spl d] *)
|
||||
|
||||
val print_slices_menu_or_dag_page :
|
||||
Config.config ->
|
||||
Gwdb.base ->
|
||||
Adef.safe_string ->
|
||||
(int * Dag2html.align * Adef.safe_string Dag2html.table_data) array array ->
|
||||
Adef.escaped_string ->
|
||||
unit
|
||||
(** [print_slices_menu_or_dag_page conf page_title hts next_txt] *)
|
||||
|
||||
val make_and_print_dag :
|
||||
Config.config ->
|
||||
Gwdb.base ->
|
||||
(Gwdb.person -> item) ->
|
||||
(Gwdb.iper -> Adef.escaped_string) ->
|
||||
bool ->
|
||||
Gwdb.iper list ->
|
||||
(Gwdb.iper * (Gwdb.iper * Gwdb.ifam option)) list ->
|
||||
Adef.safe_string ->
|
||||
Adef.escaped_string ->
|
||||
unit
|
||||
(** [make_and_print_dag conf base elem_txt vbar_txt invert set spl page_title next_txt] *)
|
||||
|
||||
val print : Config.config -> Gwdb.base -> unit
|
||||
675
lib/dateDisplay.ml
Normal file
675
lib/dateDisplay.ml
Normal file
@@ -0,0 +1,675 @@
|
||||
(* Copyright (c) 1998-2007 INRIA *)
|
||||
|
||||
open Config
|
||||
open Def
|
||||
open Util
|
||||
open Gwdb
|
||||
|
||||
let get_wday conf = function
|
||||
| Dgreg (({ prec = Sure; delta = 0 } as d), _) when d.day <> 0 && d.month <> 0
|
||||
->
|
||||
let jd = Calendar.sdn_of_gregorian d in
|
||||
let wday =
|
||||
let jd_today = Calendar.sdn_of_gregorian conf.today in
|
||||
let x = conf.today_wd - jd_today + jd in
|
||||
if x < 0 then 6 + ((x + 1) mod 7) else x mod 7
|
||||
in
|
||||
" (" ^ transl_nth conf "(week day)" wday ^ ")"
|
||||
| _ -> ""
|
||||
|
||||
let death_symbol conf =
|
||||
try List.assoc "death_symbol" conf.base_env with Not_found -> "†"
|
||||
|
||||
let code_date conf encoding d m y =
|
||||
let apply_date_code = function
|
||||
| 'd' -> string_of_int d
|
||||
| 'm' -> transl_nth conf "(month)" (m - 1)
|
||||
| 'y' -> string_of_int y
|
||||
| c -> "%" ^ String.make 1 c
|
||||
in
|
||||
let rec loop i =
|
||||
if i = String.length encoding then ""
|
||||
else
|
||||
let s, i =
|
||||
match encoding.[i] with
|
||||
| '%' when i + 1 < String.length encoding ->
|
||||
let s = apply_date_code encoding.[i + 1] in
|
||||
(s, i + 1)
|
||||
| '[' -> (
|
||||
try
|
||||
(* code similar to Util.gen_decline *)
|
||||
let len = String.length encoding in
|
||||
let j = String.index_from encoding i ']' in
|
||||
let k = String.index_from encoding i '|' in
|
||||
if k < j && j + 2 < len && encoding.[j + 1] = '%' then
|
||||
let s = apply_date_code encoding.[j + 2] in
|
||||
let s1 =
|
||||
if start_with_vowel conf s then
|
||||
String.sub encoding (k + 1) (j - k - 1)
|
||||
else String.sub encoding (i + 1) (k - i - 1)
|
||||
in
|
||||
(s1 ^ s, j + 2)
|
||||
else (String.make 1 '[', i)
|
||||
with Not_found -> (String.make 1 '[', i))
|
||||
| c -> (String.make 1 c, i)
|
||||
in
|
||||
s ^ loop (i + 1)
|
||||
in
|
||||
loop 0
|
||||
|
||||
let code_dmy conf d =
|
||||
let encoding =
|
||||
let n =
|
||||
if d.day = 1 then 0
|
||||
else if d.day != 0 then 1
|
||||
else if d.month != 0 then 2
|
||||
else 3
|
||||
in
|
||||
transl_nth conf "(date)" n
|
||||
in
|
||||
code_date conf encoding d.day d.month d.year
|
||||
|
||||
let default_french_month =
|
||||
let tab =
|
||||
[|
|
||||
"Vendemiaire";
|
||||
"Brumaire";
|
||||
"Frimaire";
|
||||
"Nivose";
|
||||
"Pluviose";
|
||||
"Ventose";
|
||||
"Germinal";
|
||||
"Floreal";
|
||||
"Prairial";
|
||||
"Messidor";
|
||||
"Thermidor";
|
||||
"Fructidor";
|
||||
"Extra";
|
||||
|]
|
||||
in
|
||||
fun m -> tab.(m)
|
||||
|
||||
let default_hebrew_month =
|
||||
let tab =
|
||||
[|
|
||||
"Tishri";
|
||||
"Heshvan";
|
||||
"Kislev";
|
||||
"Tevet";
|
||||
"Shevat";
|
||||
"AdarI";
|
||||
"AdarII";
|
||||
"Nisan";
|
||||
"Iyyar";
|
||||
"Sivan";
|
||||
"Tammuz";
|
||||
"Av";
|
||||
"Elul";
|
||||
|]
|
||||
in
|
||||
fun m -> tab.(m)
|
||||
|
||||
let french_month conf m =
|
||||
let r = transl_nth conf "(french revolution month)" m in
|
||||
if r = "[(french revolution month)]" then "[" ^ default_french_month m ^ "]"
|
||||
else r
|
||||
|
||||
let hebrew_month conf m =
|
||||
let r = transl_nth conf "(hebrew month)" m in
|
||||
if r = "[(hebrew month)]" then "[" ^ default_hebrew_month m ^ "]" else r
|
||||
|
||||
let code_french_year conf y =
|
||||
transl_nth conf "year/month/day" 3
|
||||
^ " "
|
||||
^ if y >= 1 && y < 4000 then Mutil.roman_of_arabian y else string_of_int y
|
||||
|
||||
let code_french_date conf d m y =
|
||||
let s =
|
||||
if d = 0 then ""
|
||||
else string_of_int d ^ if d = 1 then "<sup>er</sup>" else ""
|
||||
in
|
||||
let s =
|
||||
if m = 0 then ""
|
||||
else s ^ (if s = "" then "" else " ") ^ french_month conf (m - 1)
|
||||
in
|
||||
s ^ (if s = "" then "" else " ") ^ code_french_year conf y
|
||||
|
||||
let code_hebrew_date conf d m y =
|
||||
let s = if d = 0 then "" else string_of_int d in
|
||||
let s =
|
||||
if m = 0 then ""
|
||||
else s ^ (if s = "" then "" else " ") ^ hebrew_month conf (m - 1)
|
||||
in
|
||||
s ^ (if s = "" then "" else " ") ^ string_of_int y
|
||||
|
||||
let string_of_on_prec_dmy_aux conf sy sy2 d =
|
||||
match d.prec with
|
||||
| Sure ->
|
||||
if d.day = 0 && d.month = 0 then transl conf "in (year)" ^ " " ^ sy
|
||||
else if d.day = 0 then transl_decline conf "in (month year)" sy
|
||||
else transl_decline conf "on (day month year)" sy
|
||||
| About | Before | After ->
|
||||
let s = sy in
|
||||
if d.prec = About then transl_decline conf "about (date)" s
|
||||
else if d.prec = Before then transl_decline conf "before (date)" s
|
||||
else transl_decline conf "after (date)" s
|
||||
| Maybe ->
|
||||
let s =
|
||||
if d.day = 0 && d.month = 0 then transl conf "in (year)" ^ " " ^ sy
|
||||
else if d.day = 0 then transl_decline conf "in (month year)" sy
|
||||
else transl_decline conf "on (day month year)" sy
|
||||
in
|
||||
transl_decline conf "possibly (date)" s
|
||||
| OrYear d2 ->
|
||||
let s =
|
||||
if d.day = 0 && d.month = 0 then transl conf "in (year)" ^ " " ^ sy
|
||||
else if d.day = 0 then transl_decline conf "in (month year)" sy
|
||||
else transl_decline conf "on (day month year)" sy
|
||||
in
|
||||
let s2 =
|
||||
if d2.day2 = 0 && d2.month2 = 0 then transl conf "in (year)" ^ " " ^ sy2
|
||||
else if d2.day2 = 0 then transl_decline conf "in (month year)" sy2
|
||||
else transl_decline conf "on (day month year)" sy2
|
||||
in
|
||||
s ^ " " ^ transl conf "or" ^ " " ^ Mutil.nominative s2
|
||||
| YearInt d2 ->
|
||||
let s =
|
||||
if d.day = 0 && d.month = 0 then sy
|
||||
else if d.day = 0 then sy
|
||||
else transl_decline conf "on (day month year)" sy
|
||||
in
|
||||
let s2 =
|
||||
if d2.day2 = 0 && d2.month2 = 0 then sy2
|
||||
else if d2.day2 = 0 then sy2
|
||||
else transl_decline conf "on (day month year)" sy2
|
||||
in
|
||||
transl conf "between (date)"
|
||||
^ " " ^ s ^ " " ^ transl_nth conf "and" 0 ^ " " ^ Mutil.nominative s2
|
||||
|
||||
let replace_spaces_by_nbsp s =
|
||||
let rec loop i len =
|
||||
if i = String.length s then Buff.get len
|
||||
else if s.[i] = ' ' then loop (i + 1) (Buff.mstore len " ")
|
||||
else loop (i + 1) (Buff.store len s.[i])
|
||||
in
|
||||
loop 0 0
|
||||
|
||||
let string_of_on_prec_dmy conf sy sy2 d =
|
||||
Adef.safe
|
||||
@@
|
||||
let r = string_of_on_prec_dmy_aux conf sy sy2 d in
|
||||
replace_spaces_by_nbsp r
|
||||
|
||||
let string_of_on_french_dmy conf d =
|
||||
let sy = code_french_date conf d.day d.month d.year in
|
||||
let sy2 =
|
||||
match d.prec with
|
||||
| OrYear d2 | YearInt d2 -> code_french_date conf d2.day2 d2.month2 d2.year2
|
||||
| _ -> ""
|
||||
in
|
||||
string_of_on_prec_dmy conf sy sy2 d
|
||||
|
||||
let string_of_on_hebrew_dmy conf d =
|
||||
let sy = code_hebrew_date conf d.day d.month d.year in
|
||||
let sy2 =
|
||||
match d.prec with
|
||||
| OrYear d2 | YearInt d2 -> code_hebrew_date conf d2.day2 d2.month2 d2.year2
|
||||
| _ -> ""
|
||||
in
|
||||
string_of_on_prec_dmy conf sy sy2 d
|
||||
|
||||
let string_of_prec_dmy conf s s2 d =
|
||||
Adef.safe
|
||||
@@
|
||||
match d.prec with
|
||||
| Sure -> Mutil.nominative s
|
||||
| About -> transl_decline conf "about (date)" s
|
||||
| Before -> transl_decline conf "before (date)" s
|
||||
| After -> transl_decline conf "after (date)" s
|
||||
| Maybe -> transl_decline conf "possibly (date)" s
|
||||
| OrYear _ -> s ^ " " ^ transl conf "or" ^ " " ^ Mutil.nominative s2
|
||||
| YearInt _ ->
|
||||
transl conf "between (date)"
|
||||
^ " " ^ s ^ " " ^ transl_nth conf "and" 0 ^ " " ^ Mutil.nominative s2
|
||||
|
||||
let string_of_dmy_aux fn conf d =
|
||||
let sy = code_dmy conf d in
|
||||
let sy2 =
|
||||
match d.prec with
|
||||
| OrYear d2 | YearInt d2 -> code_dmy conf (Date.dmy_of_dmy2 d2)
|
||||
| _ -> ""
|
||||
in
|
||||
fn conf sy sy2 d
|
||||
|
||||
let string_of_on_dmy conf d = string_of_dmy_aux string_of_on_prec_dmy conf d
|
||||
let string_of_dmy conf d = string_of_dmy_aux string_of_prec_dmy conf d
|
||||
|
||||
(* ************************************************************************ *)
|
||||
(* [Fonc] translate_dmy : config -> (string * string * string) ->
|
||||
calendar -> bool -> (string * string * string) *)
|
||||
|
||||
(* ************************************************************************ *)
|
||||
|
||||
(** [Description] : Traduit en fonction du calendrier, le mois et/ou l'année
|
||||
d'une date et renvoie le triplet conformément au format
|
||||
de la date.
|
||||
[Args] :
|
||||
- conf : configuration de la base
|
||||
- (fst, snd, trd) : la date au bon format
|
||||
- cal : calendar
|
||||
- short : booléen pour savoir si on affiche au format court, e.g.
|
||||
VD/Vendémiaire
|
||||
[Retour] : (string * string * string) : date traduite
|
||||
[Rem] : Non exporté en clair hors de ce module. *)
|
||||
let translate_dmy conf (fst, snd, trd) cal short =
|
||||
let translate_month m =
|
||||
match cal with
|
||||
| Dfrench when m <> "" ->
|
||||
if short then Util.short_f_month (int_of_string m)
|
||||
else french_month conf (int_of_string m)
|
||||
| Dhebrew when m <> "" ->
|
||||
if short then
|
||||
String.uppercase_ascii
|
||||
(String.sub (hebrew_month conf (int_of_string m)) 0 2)
|
||||
else hebrew_month conf (int_of_string m)
|
||||
| _ -> m
|
||||
in
|
||||
let translate_year y =
|
||||
match cal with
|
||||
| Dfrench ->
|
||||
let y1 = int_of_string y in
|
||||
if y1 >= 1 && y1 < 4000 then Mutil.roman_of_arabian y1 else y
|
||||
| _ -> y
|
||||
in
|
||||
match transl conf "!dates order" with
|
||||
| "yymmdd" | "yyyymmdd" -> (translate_year fst, translate_month snd, trd)
|
||||
| "mmddyyyy" -> (translate_month fst, snd, translate_year trd)
|
||||
| _ -> (fst, translate_month snd, translate_year trd)
|
||||
|
||||
(** [decode_dmy conf dmy]
|
||||
Returns a triplet corresponding to day/month/year, arranged in
|
||||
the order defined by [!dates order] keyword in the lexicon.
|
||||
Supported formats are: "dmyyyy" / "mmddyyyy" / "yyyymmdd" / "ddmmyyyy" and "ddmmyy".
|
||||
NB: "yy" and "yyyy" variants will produce the same output ([string_of_int] without padding)
|
||||
If the format is not supported "ddmmyyyy" is used.
|
||||
*)
|
||||
let decode_dmy conf d =
|
||||
match transl conf "!dates order" with
|
||||
| "dmyyyy" ->
|
||||
(string_of_int d.day, string_of_int d.month, string_of_int d.year)
|
||||
| "mmddyyyy" -> (
|
||||
(* Si le jour et/ou le mois n'est pas sur 2 caractères, *)
|
||||
(* on rajoute les 0 nécessaires. *)
|
||||
match (d.day, d.month, d.year) with
|
||||
| 0, 0, year -> ("", "", string_of_int year)
|
||||
| 0, month, year ->
|
||||
let m = Printf.sprintf "%02d" month in
|
||||
(m, "", string_of_int year)
|
||||
| day, month, year ->
|
||||
let d = Printf.sprintf "%02d" day in
|
||||
let m = Printf.sprintf "%02d" month in
|
||||
(m, d, string_of_int year))
|
||||
| "yyyymmdd" | "yymmdd" -> (
|
||||
(* Si le jour et/ou le mois n'est pas sur 2 caractères, *)
|
||||
(* on rajoute les 0 nécessaires. *)
|
||||
match (d.day, d.month, d.year) with
|
||||
| 0, 0, year -> (string_of_int year, "", "")
|
||||
| 0, month, year ->
|
||||
let m = Printf.sprintf "%02d" month in
|
||||
(string_of_int year, m, "")
|
||||
| day, month, year ->
|
||||
let d = Printf.sprintf "%02d" day in
|
||||
let m = Printf.sprintf "%02d" month in
|
||||
(string_of_int year, m, d))
|
||||
| "ddmmyyyy" | "ddmmyy" | _ -> (
|
||||
(* Si le jour et/ou le mois n'est pas sur 2 caractères, *)
|
||||
(* on rajoute les 0 nécessaires. *)
|
||||
match (d.day, d.month, d.year) with
|
||||
| 0, 0, year -> ("", "", string_of_int year)
|
||||
| 0, month, year ->
|
||||
let m = Printf.sprintf "%02d" month in
|
||||
("", m, string_of_int year)
|
||||
| day, month, year ->
|
||||
let d = Printf.sprintf "%02d" day in
|
||||
let m = Printf.sprintf "%02d" month in
|
||||
(d, m, string_of_int year))
|
||||
|
||||
let gregorian_precision conf d =
|
||||
if d.delta = 0 then string_of_dmy conf d
|
||||
else
|
||||
let d2 =
|
||||
Calendar.gregorian_of_sdn d.prec (Calendar.sdn_of_gregorian d + d.delta)
|
||||
in
|
||||
Adef.safe
|
||||
@@ transl conf "between (date)"
|
||||
^ " "
|
||||
^ (string_of_on_dmy conf d :> string)
|
||||
^ " " ^ transl_nth conf "and" 0 ^ " "
|
||||
^ (string_of_on_dmy conf d2 :> string)
|
||||
|
||||
let string_of_date_aux ?(link = true) ?(dmy = string_of_dmy)
|
||||
?(sep = Adef.safe " ") conf =
|
||||
let mk_link c d (s : Adef.safe_string) =
|
||||
Adef.safe
|
||||
@@ Printf.sprintf
|
||||
{|<a href="%sm=CAL&y%c=%d&m%c=%d&d%c=%d&t%c=1" class="date">%s</a>|}
|
||||
(commd conf :> string)
|
||||
c d.year c d.month c d.day c
|
||||
(s :> string)
|
||||
in
|
||||
function
|
||||
| Dgreg (d, Dgregorian) ->
|
||||
let s = dmy conf d in
|
||||
if link && d.day > 0 then mk_link 'g' d s else s
|
||||
| Dgreg (d, Djulian) ->
|
||||
let cal_prec =
|
||||
if d.year < 1582 then Adef.safe ""
|
||||
else " (" ^<^ gregorian_precision conf d ^>^ ")"
|
||||
in
|
||||
let d1 = Calendar.julian_of_gregorian d in
|
||||
let year_prec =
|
||||
if
|
||||
(d1.month > 0 && d1.month < 3)
|
||||
|| (d1.month = 3 && d1.day > 0 && d1.day < 25)
|
||||
then Printf.sprintf " (%d/%d)" (d1.year - 1) (d1.year mod 10)
|
||||
else ""
|
||||
in
|
||||
let s =
|
||||
dmy conf d1 ^^^ year_prec ^<^ sep
|
||||
^^^ transl_nth conf "gregorian/julian/french/hebrew" 1
|
||||
^<^ cal_prec
|
||||
in
|
||||
if link && d1.day > 0 then mk_link 'j' d1 s else s
|
||||
| Dgreg (d, Dfrench) -> (
|
||||
let d1 = Calendar.french_of_gregorian d in
|
||||
let s = string_of_on_french_dmy conf d1 in
|
||||
let s = if link && d1.day > 0 then mk_link 'f' d1 s else s in
|
||||
match d.prec with
|
||||
| Sure | About | Before | After | Maybe ->
|
||||
s ^^^ sep ^^^ " (" ^<^ gregorian_precision conf d ^>^ ")"
|
||||
| OrYear _ | YearInt _ -> s)
|
||||
| Dgreg (d, Dhebrew) -> (
|
||||
let d1 = Calendar.hebrew_of_gregorian d in
|
||||
let s = string_of_on_hebrew_dmy conf d1 in
|
||||
match d.prec with
|
||||
| Sure | About | Before | After | Maybe ->
|
||||
s ^^^ sep ^^^ " (" ^<^ gregorian_precision conf d ^>^ ")"
|
||||
| OrYear _ | YearInt _ -> s)
|
||||
| Dtext t -> "(" ^<^ (Util.escape_html t :> Adef.safe_string) ^>^ ")"
|
||||
|
||||
let string_of_ondate ?link conf d =
|
||||
(string_of_date_aux ?link ~dmy:string_of_on_dmy conf d :> string)
|
||||
|> Util.translate_eval |> Adef.safe
|
||||
|
||||
let string_of_date conf = function
|
||||
| Dgreg (d, _) -> string_of_dmy conf d
|
||||
| Dtext t -> (Util.escape_html t :> Adef.safe_string)
|
||||
|
||||
let string_slash_of_date conf date =
|
||||
let rec slashify_dmy (fst, snd, trd) d =
|
||||
let code fst snd trd =
|
||||
List.fold_right
|
||||
(fun s accu -> if s <> "" then s ^ "/" ^ accu else accu)
|
||||
[ fst; snd ] trd
|
||||
in
|
||||
match d.prec with
|
||||
| OrYear d2 ->
|
||||
let sy = code fst snd trd in
|
||||
let d2 = Date.dmy_of_dmy2 d2 in
|
||||
let sy2 = slashify_dmy (decode_dmy conf d2) d2 in
|
||||
sy ^ " " ^ transl conf "or" ^ " " ^ sy2
|
||||
| YearInt d2 ->
|
||||
let sy = code fst snd trd in
|
||||
let d2 = Date.dmy_of_dmy2 d2 in
|
||||
let sy2 = slashify_dmy (decode_dmy conf d2) d2 in
|
||||
transl conf "between (date)"
|
||||
^ " " ^ sy ^ " " ^ transl_nth conf "and" 0 ^ " " ^ sy2
|
||||
| _ ->
|
||||
let sy = code fst snd trd in
|
||||
(string_of_prec_dmy conf sy "" d :> string)
|
||||
in
|
||||
match date with
|
||||
| Dtext t -> (Util.escape_html t :> Adef.safe_string)
|
||||
| Dgreg (d, cal) -> (
|
||||
Adef.safe
|
||||
@@
|
||||
match cal with
|
||||
| Dgregorian -> slashify_dmy (decode_dmy conf d) d
|
||||
| Djulian ->
|
||||
let d1 = Calendar.julian_of_gregorian d in
|
||||
slashify_dmy (translate_dmy conf (decode_dmy conf d1) Djulian true) d1
|
||||
^ " ("
|
||||
^ transl_nth conf "gregorian/julian/french/hebrew" 1
|
||||
^ ")"
|
||||
| Dfrench ->
|
||||
let d1 = Calendar.french_of_gregorian d in
|
||||
slashify_dmy (translate_dmy conf (decode_dmy conf d1) Dfrench true) d1
|
||||
| Dhebrew ->
|
||||
let d1 = Calendar.french_of_gregorian d in
|
||||
slashify_dmy (translate_dmy conf (decode_dmy conf d1) Dhebrew true) d1
|
||||
^ " ("
|
||||
^ transl_nth conf "gregorian/julian/french/hebrew" 3
|
||||
^ ")")
|
||||
|
||||
let string_of_age conf a =
|
||||
Adef.safe
|
||||
@@
|
||||
match a with
|
||||
| { day = 0; month = 0; year = y } ->
|
||||
if y > 1 then string_of_int y ^ " " ^ transl conf "years old"
|
||||
else if y = 1 then transl conf "one year old"
|
||||
else transl conf "birth"
|
||||
| { day = 0; month = m; year = y } ->
|
||||
if y >= 2 then string_of_int y ^ " " ^ transl conf "years old"
|
||||
else if y > 0 || m > 1 then
|
||||
string_of_int ((y * 12) + m) ^ " " ^ transl conf "months old"
|
||||
else if m = 1 then transl conf "one month old"
|
||||
else transl conf "less than one month old"
|
||||
| { day = d; month = m; year = y } ->
|
||||
if y >= 2 then string_of_int y ^ " " ^ transl conf "years old"
|
||||
else if y > 0 || m > 1 then
|
||||
string_of_int ((y * 12) + m) ^ " " ^ transl conf "months old"
|
||||
else if m = 1 then transl conf "one month old"
|
||||
else if d >= 2 then string_of_int d ^ " " ^ transl conf "days old"
|
||||
else if d = 1 then transl conf "one day old"
|
||||
else "0"
|
||||
|
||||
(* ************************************************************************ *)
|
||||
(* [Fonc] prec_text : config -> Def.dmy -> string *)
|
||||
|
||||
(* ************************************************************************ *)
|
||||
|
||||
(** [Description] : Renvoie la précision d'une date.
|
||||
[Args] :
|
||||
- conf : configuration de la base
|
||||
- d : Def.dmy
|
||||
[Retour] : string
|
||||
[Rem] : Exporté en clair hors de ce module. *)
|
||||
let prec_text conf d =
|
||||
match d.prec with
|
||||
| About -> (
|
||||
(* On utilise le dictionnaire pour être sur *)
|
||||
(* que ce soit compréhensible de tous. *)
|
||||
match transl conf "about (short date)" with "ca" -> "ca " | s -> s)
|
||||
| Maybe -> "?"
|
||||
| Before -> "<"
|
||||
| After -> ">"
|
||||
| OrYear _ -> "|"
|
||||
| YearInt _ -> ".."
|
||||
| Sure -> ""
|
||||
|
||||
(* ************************************************************************ *)
|
||||
(* [Fonc] month_text : Def.dmy -> string *)
|
||||
|
||||
(* ************************************************************************ *)
|
||||
|
||||
(** [Description] : Renvoie le mois d'une date.
|
||||
[Args] :
|
||||
- d : Def.dmy
|
||||
[Retour] : string
|
||||
[Rem] : Exporté en clair hors de ce module. *)
|
||||
let month_text d = if d.month = 0 then "" else string_of_int d.month
|
||||
|
||||
(* ************************************************************************ *)
|
||||
(* [Fonc] year_text : Def.dmy -> string *)
|
||||
|
||||
(* ************************************************************************ *)
|
||||
|
||||
(** [Description] : Renvoie l'année d'une date.
|
||||
[Args] :
|
||||
- d : Def.dmy
|
||||
[Retour] : string
|
||||
[Rem] : Exporté en clair hors de ce module. *)
|
||||
let year_text d =
|
||||
match d.prec with
|
||||
| OrYear d2 when d.year <> d2.year2 ->
|
||||
string_of_int d.year ^ "/" ^ string_of_int d2.year2
|
||||
| YearInt d2 when d.year <> d2.year2 ->
|
||||
string_of_int d.year ^ ".." ^ string_of_int d2.year2
|
||||
| _ -> string_of_int d.year
|
||||
|
||||
(* ************************************************************************ *)
|
||||
(* [Fonc] prec_year_text : config -> Def.dmy -> string *)
|
||||
|
||||
(* ************************************************************************ *)
|
||||
|
||||
(** [Description] : Renvoie la précision d'une date et l'année de la date.
|
||||
[Args] :
|
||||
- conf : configuration de la base
|
||||
- d : Def.dmy
|
||||
[Retour] : string
|
||||
[Rem] : Exporté en clair hors de ce module. *)
|
||||
let prec_year_text conf d =
|
||||
let s =
|
||||
match d.prec with
|
||||
| About -> (
|
||||
(* On utilise le dictionnaire pour être sur *)
|
||||
(* que ce soit compréhensible de tous. *)
|
||||
match transl conf "about (short date)" with "ca" -> "ca " | s -> s)
|
||||
| Maybe -> "?"
|
||||
| Before -> "/"
|
||||
| _ -> ""
|
||||
in
|
||||
let s = s ^ year_text d in
|
||||
match d.prec with After -> s ^ "/" | _ -> s
|
||||
|
||||
(* ********************************************************************** *)
|
||||
(* [Fonc] short_dates_text : config -> base -> person -> string *)
|
||||
|
||||
(* ********************************************************************** *)
|
||||
|
||||
(** [Description] : Renvoie la concatenation de l'année de naissance et
|
||||
l'année de décès (si trouvée par get_birth_death_date). La précision
|
||||
de la date est ajoutée pour chaque année.
|
||||
L'affichage est le suivant :
|
||||
* 1700-1780 (date naissance - date décès)
|
||||
* 1700- (naissance - décédé)
|
||||
* 1700 (naissance - vivant)
|
||||
* †1780 (pas date naissance - date décès)
|
||||
* † (pas date naissance - décédé)
|
||||
[Args] :
|
||||
- conf : configuration de la base
|
||||
- base : base de donnée
|
||||
- p : person
|
||||
[Retour] : string
|
||||
[Rem] : Exporté en clair hors de ce module. *)
|
||||
let short_dates_text conf base p =
|
||||
Adef.safe
|
||||
@@
|
||||
if authorized_age conf base p then
|
||||
let birth_date, death_date, _ = Gutil.get_birth_death_date p in
|
||||
let s =
|
||||
match (birth_date, death_date) with
|
||||
| Some (Dgreg (b, _)), Some (Dgreg (d, _)) ->
|
||||
prec_year_text conf b ^ "-" ^ prec_year_text conf d
|
||||
| Some (Dgreg (b, _)), _ -> (
|
||||
(* La personne peut être décédée mais ne pas avoir de date. *)
|
||||
match get_death p with
|
||||
| Death (_, _) | DeadDontKnowWhen | DeadYoung ->
|
||||
prec_year_text conf b ^ "-"
|
||||
| _ -> prec_year_text conf b)
|
||||
| _, Some (Dgreg (d, _)) -> death_symbol conf ^ prec_year_text conf d
|
||||
| _, _ -> (
|
||||
(* La personne peut être décédée mais ne pas avoir de date. *)
|
||||
match get_death p with
|
||||
| Death (_, _) | DeadDontKnowWhen | DeadYoung -> death_symbol conf
|
||||
| _ -> "")
|
||||
in
|
||||
if s <> "" then " <bdo dir=\"ltr\">" ^ s ^ "</bdo>" else s
|
||||
else ""
|
||||
|
||||
(* ********************************************************************** *)
|
||||
(* [Fonc] short_marriage_date_text :
|
||||
config -> base -> person -> person -> string *)
|
||||
|
||||
(* ********************************************************************** *)
|
||||
|
||||
(** [Description] : Renvoie l'année de la date de mariage ansi que la
|
||||
précision de la date.
|
||||
[Args] :
|
||||
- conf : configuration de la base
|
||||
- base : base de donnée
|
||||
- p1 : conjoint 1
|
||||
- p2 : conjoint 2
|
||||
[Retour] : string
|
||||
[Rem] : Exporté en clair hors de ce module. *)
|
||||
let short_marriage_date_text conf base fam p1 p2 =
|
||||
Adef.safe
|
||||
@@
|
||||
if authorized_age conf base p1 && authorized_age conf base p2 then
|
||||
match Date.cdate_to_dmy_opt (get_marriage fam) with
|
||||
| Some d ->
|
||||
"<span style=\"font-size:70%\">" ^ prec_year_text conf d ^ "</span>"
|
||||
| None -> ""
|
||||
else ""
|
||||
|
||||
(* ********************************************************************** *)
|
||||
(* [Fonc] short_family_date_text :
|
||||
config -> base -> bool -> family -> string *)
|
||||
|
||||
(* ********************************************************************** *)
|
||||
|
||||
(** [Description] : Renvoie l'année marriage - séparation
|
||||
ou uniquement l'année de la séparation.
|
||||
[Args] :
|
||||
- conf : configuration de la base
|
||||
- base : base de donnée
|
||||
- bool : si faux, uniquement dates de la séparation
|
||||
- family : famille
|
||||
[Retour] : string
|
||||
[Rem] : Exporté en clair hors de ce module. *)
|
||||
let short_family_dates_text conf _base marr_sep fam =
|
||||
let marr_dates_aux =
|
||||
match Date.cdate_to_dmy_opt (Gwdb.get_marriage fam) with
|
||||
| Some dmy -> Some (prec_year_text conf dmy)
|
||||
| None -> Some ""
|
||||
in
|
||||
let sep_dates_aux =
|
||||
match
|
||||
List.find_opt
|
||||
(fun e ->
|
||||
e.efam_name = Efam_Divorce
|
||||
|| e.efam_name = Efam_Annulation
|
||||
|| e.efam_name = Efam_Separated)
|
||||
(Gwdb.get_fevents fam)
|
||||
with
|
||||
| None -> None
|
||||
| Some e -> (
|
||||
match Date.cdate_to_dmy_opt e.efam_date with
|
||||
| None -> Some ""
|
||||
| Some dmy -> Some (prec_year_text conf dmy))
|
||||
in
|
||||
Adef.safe
|
||||
@@
|
||||
if marr_sep then
|
||||
match (marr_dates_aux, sep_dates_aux) with
|
||||
| Some m, Some s -> m ^ "-" ^ s
|
||||
| Some m, None -> m
|
||||
| None, _ -> ""
|
||||
else Option.value ~default:"" sep_dates_aux
|
||||
|
||||
(* For public interfce, force [string_of_prec_dmy] args to be safe strings *)
|
||||
let string_of_prec_dmy conf s s2 d =
|
||||
let s = (s : Adef.safe_string :> string) in
|
||||
let s2 = (s2 : Adef.safe_string :> string) in
|
||||
string_of_prec_dmy conf s s2 d
|
||||
118
lib/dateDisplay.mli
Normal file
118
lib/dateDisplay.mli
Normal file
@@ -0,0 +1,118 @@
|
||||
(* Copyright (c) 1998-2007 INRIA *)
|
||||
|
||||
open Config
|
||||
open Def
|
||||
open Gwdb
|
||||
|
||||
val get_wday : config -> date -> string
|
||||
(** [get_wday conf date]
|
||||
Return the day of the week for this [date] *)
|
||||
|
||||
val code_dmy : config -> dmy -> string
|
||||
(** Returns textual representation of the date translated to the current language.
|
||||
Uses different encodings depending on day's, month's and year's accessibility.
|
||||
Doesn't consider precision. *)
|
||||
|
||||
val code_hebrew_date : config -> int -> int -> int -> string
|
||||
(** Returns textual representation of a day / month / year in the hebrew calendar
|
||||
translated to the current language.
|
||||
*)
|
||||
|
||||
val string_of_dmy : Config.config -> Def.dmy -> Adef.safe_string
|
||||
(** Converts and translate date to the textual representation for the giving language. Considers precision. *)
|
||||
|
||||
val string_of_date : config -> date -> Adef.safe_string
|
||||
(** If date is [Dgreg] calls for [string_of_dmy] to convert date to the string else returns content of [Dtext].
|
||||
Difference between calendars is not taken into the acount. *)
|
||||
|
||||
val string_of_ondate : ?link:bool -> config -> date -> Adef.safe_string
|
||||
(** Converts and translate date with considering different calendars with prefix "on"
|
||||
before dates (changes for other languages).
|
||||
Date precision is much more verbose then with [string_of_date]. Decline phrase if needed.
|
||||
If [link] is true then encapsulates result in HTML link to the page calendar's date converter. *)
|
||||
|
||||
val string_of_on_french_dmy : config -> dmy -> Adef.safe_string
|
||||
(** Translate a date in the french calendar
|
||||
with prefix "on" before dates (changes for other languages). *)
|
||||
|
||||
val string_of_on_hebrew_dmy : config -> dmy -> Adef.safe_string
|
||||
(** Translate a date in the hebrew calendar
|
||||
with prefix "on" before dates (changes for other languages). *)
|
||||
|
||||
val string_slash_of_date : config -> date -> Adef.safe_string
|
||||
(** Returns date in format dd/mm/yyyy. Format could be different for other languages (defined by [!dates order]
|
||||
keyword in the lexicon). *)
|
||||
|
||||
val string_of_age : config -> dmy -> Adef.safe_string
|
||||
(** Returns textual representation of the age represented by [dmy]. *)
|
||||
|
||||
val prec_year_text : config -> dmy -> string
|
||||
(** Returns textual representation of date's precision and year. *)
|
||||
|
||||
val prec_text : config -> dmy -> string
|
||||
(** Returns textual representation of date's precision *)
|
||||
|
||||
val month_text : dmy -> string
|
||||
(** Returns textual representation of date's month number. *)
|
||||
|
||||
val year_text : dmy -> string
|
||||
(** Returns textual representation of date's year. *)
|
||||
|
||||
val short_dates_text : config -> base -> person -> Adef.safe_string
|
||||
(** Returns concatenation of person's birth and death dates (if exists). Precision is mentionned for each date.
|
||||
For example :
|
||||
|
||||
* 1700-1780 (birth - death)
|
||||
* 1700- (birth - death but don't know when)
|
||||
* 1700 (birth - alive)
|
||||
* †1780 (unknown birth date - death)
|
||||
* † (unknown birth date - death but don't know when) *)
|
||||
|
||||
val short_marriage_date_text :
|
||||
config -> base -> family -> person -> person -> Adef.safe_string
|
||||
(** Retruns year of marriage for given spouses with its precision. *)
|
||||
|
||||
val short_family_dates_text :
|
||||
config -> base -> bool -> family -> Adef.safe_string
|
||||
(** Retruns years of marriage (yyy1-yyy2) for given family taking
|
||||
into account possible separation or divorce. *)
|
||||
|
||||
val death_symbol : config -> string
|
||||
(** [death_symbol conf]
|
||||
Return the value associated to ["death_symbol"] in [.gwf] file
|
||||
if it is defined, or use ["†"] if it is not.
|
||||
*)
|
||||
|
||||
val code_french_year : config -> int -> string
|
||||
(** Returns roman number of the year of French calendar *)
|
||||
|
||||
val string_of_date_aux :
|
||||
?link:bool ->
|
||||
?dmy:(Config.config -> Def.dmy -> Adef.safe_string) ->
|
||||
?sep:Adef.safe_string ->
|
||||
Config.config ->
|
||||
Def.date ->
|
||||
Adef.safe_string
|
||||
(** Same as [string_of_ondate] except :
|
||||
- Conversion function for [Def.dmy] could be passed in in [dmy] argument
|
||||
- Doesn't consider phrase declination as [string_of_ondate] does. *)
|
||||
|
||||
(**/**)
|
||||
|
||||
val hebrew_month : config -> int -> string
|
||||
(** Returns the translation of the month of French calendar
|
||||
First month is [0]
|
||||
*)
|
||||
|
||||
val french_month : config -> int -> string
|
||||
(** Returns the translation of the month of Hebrew calendar
|
||||
First month is [0]
|
||||
*)
|
||||
|
||||
val string_of_prec_dmy :
|
||||
config -> Adef.safe_string -> Adef.safe_string -> dmy -> Adef.safe_string
|
||||
(** [string_of_prec_dmy conf s s2 d]
|
||||
Takes two date representations (as strings) [s] and [s2] and
|
||||
returns translated phrase according to prec of [d].
|
||||
[d] is only used to determine the precision
|
||||
*)
|
||||
79
lib/def/adef.ml
Normal file
79
lib/def/adef.ml
Normal file
@@ -0,0 +1,79 @@
|
||||
(* $Id: adef.ml,v 5.6 2007-02-21 18:14:01 ddr Exp $ *)
|
||||
(* Copyright (c) 1998-2007 INRIA *)
|
||||
|
||||
type fix = int
|
||||
|
||||
let float_of_fix x = float x /. 1000000.0
|
||||
let fix_of_float x = truncate ((x *. 1000000.0) +. 0.5)
|
||||
|
||||
external fix : int -> fix = "%identity"
|
||||
external fix_repr : fix -> int = "%identity"
|
||||
|
||||
let no_consang = fix (-1)
|
||||
|
||||
type date = Dgreg of dmy * calendar | Dtext of string
|
||||
and calendar = Dgregorian | Djulian | Dfrench | Dhebrew
|
||||
and dmy = { day : int; month : int; year : int; prec : precision; delta : int }
|
||||
and dmy2 = { day2 : int; month2 : int; year2 : int; delta2 : int }
|
||||
|
||||
and precision =
|
||||
| Sure
|
||||
| About
|
||||
| Maybe
|
||||
| Before
|
||||
| After
|
||||
| OrYear of dmy2
|
||||
| YearInt of dmy2
|
||||
|
||||
type cdate =
|
||||
| Cgregorian of int
|
||||
| Cjulian of int
|
||||
| Cfrench of int
|
||||
| Chebrew of int
|
||||
| Ctext of string
|
||||
| Cdate of date
|
||||
| Cnone
|
||||
|
||||
type 'person gen_couple = { father : 'person; mother : 'person }
|
||||
and 'person gen_parents = { parent : 'person array }
|
||||
|
||||
let father cpl =
|
||||
if Obj.size (Obj.repr cpl) = 2 then cpl.father else (Obj.magic cpl).parent.(0)
|
||||
|
||||
let mother cpl =
|
||||
if Obj.size (Obj.repr cpl) = 2 then cpl.mother else (Obj.magic cpl).parent.(1)
|
||||
|
||||
let couple father mother = { father; mother }
|
||||
let parent parent = { father = parent.(0); mother = parent.(1) }
|
||||
|
||||
let parent_array cpl =
|
||||
if Obj.size (Obj.repr cpl) = 2 then [| cpl.father; cpl.mother |]
|
||||
else (Obj.magic cpl).parent
|
||||
|
||||
let multi_couple father mother : 'person gen_couple =
|
||||
Obj.magic { parent = [| father; mother |] }
|
||||
|
||||
let multi_parent parent : 'person gen_couple = Obj.magic { parent }
|
||||
|
||||
type 'a astring = string
|
||||
type safe_string = [ `encoded | `escaped | `safe ] astring
|
||||
type escaped_string = [ `encoded | `escaped ] astring
|
||||
type encoded_string = [ `encoded ] astring
|
||||
|
||||
let ( ^^^ ) : 'a astring -> 'a astring -> 'a astring =
|
||||
fun (a : 'a astring) (b : 'a astring) : 'a astring -> a ^ b
|
||||
|
||||
let ( ^>^ ) : 'a astring -> string -> 'a astring =
|
||||
fun (a : 'a astring) (b : string) : 'a astring -> a ^ b
|
||||
|
||||
let ( ^<^ ) : string -> 'a astring -> 'a astring =
|
||||
fun (a : string) (b : 'a astring) : 'a astring -> a ^ b
|
||||
|
||||
let ( <^> ) : 'a astring -> 'a astring -> bool = ( <> )
|
||||
|
||||
external safe : string -> safe_string = "%identity"
|
||||
external escaped : string -> escaped_string = "%identity"
|
||||
external encoded : string -> encoded_string = "%identity"
|
||||
external as_string : 'a astring -> string = "%identity"
|
||||
|
||||
let safe_fn = ( @@ )
|
||||
92
lib/def/adef.mli
Normal file
92
lib/def/adef.mli
Normal file
@@ -0,0 +1,92 @@
|
||||
(* $Id: adef.mli,v 5.6 2007-02-21 18:14:01 ddr Exp $ *)
|
||||
(* Copyright (c) 1998-2007 INRIA *)
|
||||
|
||||
type fix
|
||||
(** Consanguinity rate *)
|
||||
|
||||
val float_of_fix : fix -> float
|
||||
(** Returns float coefficient of consanguinity rate *)
|
||||
|
||||
val fix_of_float : float -> fix
|
||||
(** Returns consanguinity rate from its float coefficient *)
|
||||
|
||||
external fix : int -> fix = "%identity"
|
||||
(** [fix] from int *)
|
||||
|
||||
external fix_repr : fix -> int = "%identity"
|
||||
(** [fix] to int *)
|
||||
|
||||
val no_consang : fix
|
||||
(** No consanguinity *)
|
||||
|
||||
(** Date data type that can be either concrete date associated to a calendar or a textual form of the date. *)
|
||||
type date = Dgreg of dmy * calendar | Dtext of string
|
||||
|
||||
(** Supported calendars *)
|
||||
and calendar = Dgregorian | Djulian | Dfrench | Dhebrew
|
||||
|
||||
and dmy = { day : int; month : int; year : int; prec : precision; delta : int }
|
||||
(** Concrete date with precision. *)
|
||||
|
||||
and dmy2 = { day2 : int; month2 : int; year2 : int; delta2 : int }
|
||||
(** Concrete date without precision. *)
|
||||
|
||||
(** Precision attached to the concrete date. *)
|
||||
and precision =
|
||||
| Sure
|
||||
| About
|
||||
| Maybe
|
||||
| Before
|
||||
| After
|
||||
| OrYear of dmy2
|
||||
| YearInt of dmy2
|
||||
|
||||
(** Compressed date *)
|
||||
type cdate =
|
||||
| Cgregorian of int
|
||||
| Cjulian of int
|
||||
| Cfrench of int
|
||||
| Chebrew of int
|
||||
| Ctext of string
|
||||
| Cdate of date
|
||||
| Cnone
|
||||
|
||||
type 'person gen_couple
|
||||
(** Polymorphic type to represent a family's couple.
|
||||
Couple consists of the father and of the mother. *)
|
||||
|
||||
val father : 'a gen_couple -> 'a
|
||||
(** Get father from couple *)
|
||||
|
||||
val mother : 'a gen_couple -> 'a
|
||||
(** Get mother from couple *)
|
||||
|
||||
val couple : 'a -> 'a -> 'a gen_couple
|
||||
(** [couple f m] creates a couple from father [f] and mother [m] *)
|
||||
|
||||
val parent : 'a array -> 'a gen_couple
|
||||
(** Create [gen_couple] from array. First element of array should be father, second - mother *)
|
||||
|
||||
val parent_array : 'a gen_couple -> 'a array
|
||||
(** Returns array from [gen_couple]. First element of array is father, second - mother *)
|
||||
|
||||
val multi_couple : 'a -> 'a -> 'a gen_couple
|
||||
(** @deprecated Use [couple] instead *)
|
||||
|
||||
val multi_parent : 'a array -> 'a gen_couple
|
||||
(** @deprecated Use [parent] instead *)
|
||||
|
||||
type +'a astring = private string
|
||||
type safe_string = [ `encoded | `escaped | `safe ] astring
|
||||
type escaped_string = [ `encoded | `escaped ] astring
|
||||
type encoded_string = [ `encoded ] astring
|
||||
|
||||
val ( ^^^ ) : 'a astring -> 'a astring -> 'a astring
|
||||
val ( ^>^ ) : 'a astring -> string -> 'a astring
|
||||
val ( ^<^ ) : string -> 'a astring -> 'a astring
|
||||
val ( <^> ) : 'a astring -> 'a astring -> bool
|
||||
val safe : string -> safe_string
|
||||
val escaped : string -> escaped_string
|
||||
val encoded : string -> encoded_string
|
||||
val as_string : 'a astring -> string
|
||||
val safe_fn : (string -> string) -> 'a astring -> 'a astring
|
||||
458
lib/def/def.ml
Normal file
458
lib/def/def.ml
Normal file
@@ -0,0 +1,458 @@
|
||||
(* Copyright (c) 1998-2007 INRIA *)
|
||||
|
||||
(** Http response status *)
|
||||
type httpStatus =
|
||||
| OK (* 200 *)
|
||||
| Moved_Temporarily (* 302 *)
|
||||
| Bad_Request (* 400 *)
|
||||
| Unauthorized (* 401 *)
|
||||
| Forbidden (* 403 *)
|
||||
| Not_Found (* 404 *)
|
||||
| Conflict (* 409 *)
|
||||
| Internal_Server_Error (* 500 *)
|
||||
| Service_Unavailable (* 503 *)
|
||||
|
||||
exception HttpExn of httpStatus * string
|
||||
|
||||
(* TODO OCaml 4.12 : use Either *)
|
||||
|
||||
(** Type that represents 2 possible choices *)
|
||||
type ('a, 'b) choice = Left of 'a | Right of 'b
|
||||
|
||||
type cdate = Adef.cdate
|
||||
(** Alias to [Adef.cdate] *)
|
||||
|
||||
(** Alias to [Adef.date] *)
|
||||
type date = Adef.date =
|
||||
| Dgreg of dmy * calendar
|
||||
(* textual form of the date *)
|
||||
| Dtext of string
|
||||
|
||||
(** Alias to [Adef.calendar] *)
|
||||
and calendar = Adef.calendar = Dgregorian | Djulian | Dfrench | Dhebrew
|
||||
|
||||
and dmy = Adef.dmy = {
|
||||
day : int;
|
||||
month : int;
|
||||
year : int;
|
||||
prec : precision;
|
||||
delta : int;
|
||||
}
|
||||
(** Alias to [Adef.dmy] *)
|
||||
|
||||
and dmy2 = Adef.dmy2 = { day2 : int; month2 : int; year2 : int; delta2 : int }
|
||||
(** Alias to [Adef.dmy2] *)
|
||||
|
||||
(** Alias to [Adef.precision] *)
|
||||
and precision = Adef.precision =
|
||||
| Sure
|
||||
| About
|
||||
| Maybe
|
||||
| Before
|
||||
| After
|
||||
| OrYear of dmy2
|
||||
(* inteval *)
|
||||
| YearInt of dmy2
|
||||
|
||||
(** Relation kind between couple in the family *)
|
||||
type relation_kind =
|
||||
| Married
|
||||
| NotMarried
|
||||
| Engaged
|
||||
| NoSexesCheckNotMarried
|
||||
| NoMention
|
||||
| NoSexesCheckMarried
|
||||
| MarriageBann
|
||||
| MarriageContract
|
||||
| MarriageLicense
|
||||
| Pacs
|
||||
| Residence
|
||||
|
||||
(** Divorce status *)
|
||||
type divorce = NotDivorced | Divorced of cdate | Separated
|
||||
|
||||
(** Death reason *)
|
||||
type death_reason = Killed | Murdered | Executed | Disappeared | Unspecified
|
||||
|
||||
(** Death status *)
|
||||
type death =
|
||||
| NotDead
|
||||
| Death of death_reason * cdate
|
||||
| DeadYoung
|
||||
| DeadDontKnowWhen
|
||||
| DontKnowIfDead
|
||||
| OfCourseDead
|
||||
|
||||
(** Burial information *)
|
||||
type burial = UnknownBurial | Buried of cdate | Cremated of cdate
|
||||
|
||||
(** Rights for access to the personal data *)
|
||||
type access = IfTitles | Public | Private
|
||||
|
||||
(** Title name *)
|
||||
type 'string gen_title_name = Tmain | Tname of 'string | Tnone
|
||||
|
||||
type 'string gen_title = {
|
||||
t_name : 'string gen_title_name;
|
||||
t_ident : 'string;
|
||||
t_place : 'string;
|
||||
t_date_start : cdate;
|
||||
t_date_end : cdate;
|
||||
t_nth : int;
|
||||
}
|
||||
(** Type that represents information about nobility title of a person *)
|
||||
|
||||
(** Witness kind for an event *)
|
||||
type witness_kind =
|
||||
| Witness
|
||||
| Witness_GodParent
|
||||
| Witness_CivilOfficer
|
||||
| Witness_ReligiousOfficer
|
||||
| Witness_Informant
|
||||
| Witness_Attending
|
||||
| Witness_Mentioned
|
||||
| Witness_Other
|
||||
|
||||
(** Personal event name. *)
|
||||
type 'string gen_pers_event_name =
|
||||
| Epers_Birth
|
||||
| Epers_Baptism
|
||||
| Epers_Death
|
||||
| Epers_Burial
|
||||
| Epers_Cremation
|
||||
| Epers_Accomplishment
|
||||
| Epers_Acquisition
|
||||
| Epers_Adhesion
|
||||
| Epers_BaptismLDS
|
||||
| Epers_BarMitzvah
|
||||
| Epers_BatMitzvah
|
||||
| Epers_Benediction
|
||||
| Epers_ChangeName
|
||||
| Epers_Circumcision
|
||||
| Epers_Confirmation
|
||||
| Epers_ConfirmationLDS
|
||||
| Epers_Decoration
|
||||
| Epers_DemobilisationMilitaire
|
||||
| Epers_Diploma
|
||||
| Epers_Distinction
|
||||
| Epers_Dotation
|
||||
| Epers_DotationLDS
|
||||
| Epers_Education
|
||||
| Epers_Election
|
||||
| Epers_Emigration
|
||||
| Epers_Excommunication
|
||||
| Epers_FamilyLinkLDS
|
||||
| Epers_FirstCommunion
|
||||
| Epers_Funeral
|
||||
| Epers_Graduate
|
||||
| Epers_Hospitalisation
|
||||
| Epers_Illness
|
||||
| Epers_Immigration
|
||||
| Epers_ListePassenger
|
||||
| Epers_MilitaryDistinction
|
||||
| Epers_MilitaryPromotion
|
||||
| Epers_MilitaryService
|
||||
| Epers_MobilisationMilitaire
|
||||
| Epers_Naturalisation
|
||||
| Epers_Occupation
|
||||
| Epers_Ordination
|
||||
| Epers_Property
|
||||
| Epers_Recensement
|
||||
| Epers_Residence
|
||||
| Epers_Retired
|
||||
| Epers_ScellentChildLDS
|
||||
| Epers_ScellentParentLDS
|
||||
| Epers_ScellentSpouseLDS
|
||||
| Epers_VenteBien
|
||||
| Epers_Will
|
||||
| Epers_Name of 'string
|
||||
|
||||
type ('person, 'string) gen_pers_event = {
|
||||
epers_name : 'string gen_pers_event_name;
|
||||
epers_date : cdate;
|
||||
epers_place : 'string;
|
||||
epers_reason : 'string;
|
||||
epers_note : 'string;
|
||||
epers_src : 'string;
|
||||
epers_witnesses : ('person * witness_kind) array;
|
||||
}
|
||||
(** Personal event information *)
|
||||
|
||||
(** Event name pertaining a family. *)
|
||||
type 'string gen_fam_event_name =
|
||||
| Efam_Marriage
|
||||
| Efam_NoMarriage
|
||||
| Efam_NoMention
|
||||
| Efam_Engage
|
||||
| Efam_Divorce
|
||||
| Efam_Separated
|
||||
| Efam_Annulation
|
||||
| Efam_MarriageBann
|
||||
| Efam_MarriageContract
|
||||
| Efam_MarriageLicense
|
||||
| Efam_PACS
|
||||
| Efam_Residence
|
||||
| Efam_Name of 'string
|
||||
|
||||
type ('person, 'string) gen_fam_event = {
|
||||
efam_name : 'string gen_fam_event_name;
|
||||
efam_date : cdate;
|
||||
efam_place : 'string;
|
||||
efam_reason : 'string;
|
||||
efam_note : 'string;
|
||||
efam_src : 'string;
|
||||
efam_witnesses : ('person * witness_kind) array;
|
||||
}
|
||||
(** Event information pertaining a family. *)
|
||||
|
||||
(** Relation type with parent (if not native) *)
|
||||
type relation_type =
|
||||
| Adoption
|
||||
| Recognition
|
||||
| CandidateParent
|
||||
| GodParent
|
||||
| FosterParent
|
||||
|
||||
type ('person, 'string) gen_relation = {
|
||||
r_type : relation_type;
|
||||
r_fath : 'person option;
|
||||
r_moth : 'person option;
|
||||
r_sources : 'string;
|
||||
}
|
||||
(** Relation information with parents (if not native) *)
|
||||
|
||||
(** Sex of person *)
|
||||
type sex = Male | Female | Neuter
|
||||
|
||||
type place = {
|
||||
other : string;
|
||||
town : string;
|
||||
township : string;
|
||||
canton : string;
|
||||
district : string;
|
||||
county : string;
|
||||
region : string;
|
||||
country : string;
|
||||
}
|
||||
(** Place information *)
|
||||
|
||||
type ('iper, 'person, 'string) gen_person = {
|
||||
first_name : 'string;
|
||||
surname : 'string;
|
||||
occ : int;
|
||||
image : 'string;
|
||||
public_name : 'string;
|
||||
qualifiers : 'string list;
|
||||
aliases : 'string list;
|
||||
first_names_aliases : 'string list;
|
||||
surnames_aliases : 'string list;
|
||||
titles : 'string gen_title list;
|
||||
(* relations with not native parents *)
|
||||
rparents : ('person, 'string) gen_relation list;
|
||||
(* related persons like (father of witnessed family,
|
||||
concerned person of witnessed event, adopted child, etc.) *)
|
||||
related : 'person list;
|
||||
occupation : 'string;
|
||||
sex : sex;
|
||||
access : access;
|
||||
birth : cdate;
|
||||
birth_place : 'string;
|
||||
birth_note : 'string;
|
||||
birth_src : 'string;
|
||||
baptism : cdate;
|
||||
baptism_place : 'string;
|
||||
baptism_note : 'string;
|
||||
baptism_src : 'string;
|
||||
death : death;
|
||||
death_place : 'string;
|
||||
death_note : 'string;
|
||||
death_src : 'string;
|
||||
burial : burial;
|
||||
burial_place : 'string;
|
||||
burial_note : 'string;
|
||||
burial_src : 'string;
|
||||
pevents : ('person, 'string) gen_pers_event list;
|
||||
notes : 'string;
|
||||
psources : 'string;
|
||||
key_index : 'iper;
|
||||
}
|
||||
(** Polymorphic type describing information about person. *)
|
||||
|
||||
type 'family gen_ascend = { parents : 'family option; consang : Adef.fix }
|
||||
(** Person's ascendants (family where he is a children) with its consangunity rate
|
||||
(equal to relationship betwen his parents). *)
|
||||
|
||||
(* Person's families to which he belongs as parent (union of families) *)
|
||||
type 'family gen_union = { family : 'family array }
|
||||
|
||||
type 'person gen_descend = { children : 'person array }
|
||||
(** Children of the family *)
|
||||
|
||||
type ('person, 'ifam, 'string) gen_family = {
|
||||
marriage : cdate;
|
||||
marriage_place : 'string;
|
||||
marriage_note : 'string;
|
||||
marriage_src : 'string;
|
||||
witnesses : 'person array;
|
||||
relation : relation_kind;
|
||||
divorce : divorce;
|
||||
fevents : ('person, 'string) gen_fam_event list;
|
||||
comment : 'string;
|
||||
origin_file : 'string; (* .gw filename where family is defined *)
|
||||
fsources : 'string;
|
||||
fam_index : 'ifam;
|
||||
}
|
||||
(** Polymorphic type describing information about family. *)
|
||||
|
||||
type 'person gen_couple = 'person Adef.gen_couple
|
||||
(** Alias to [Adef.gen_couple] *)
|
||||
|
||||
(** Database errors describing bad specification of the person *)
|
||||
type 'person error =
|
||||
| AlreadyDefined of 'person
|
||||
| OwnAncestor of 'person (** Person is his own ancestor *)
|
||||
| BadSexOfMarriedPerson of 'person
|
||||
|
||||
(** Database warnings attached to the specification of the person, family, relation, etc. *)
|
||||
type ('iper, 'person, 'family, 'descend, 'title, 'pevent, 'fevent) warning =
|
||||
| BigAgeBetweenSpouses of
|
||||
'person
|
||||
* 'person
|
||||
* dmy (* Age differece between couples is greater then 50 years *)
|
||||
| BirthAfterDeath of 'person (** Person is born after his death *)
|
||||
| IncoherentSex of 'person * int * int (** Incoherent sex of person *)
|
||||
| ChangedOrderOfChildren of 'family * 'descend * 'iper array * 'iper array
|
||||
(** Children order has been modified *)
|
||||
| ChangedOrderOfMarriages of 'person * 'family array * 'family array
|
||||
(** Person's marriages order has been modified *)
|
||||
| ChangedOrderOfFamilyEvents of 'family * 'fevent list * 'fevent list
|
||||
(** Family's events order has been modified *)
|
||||
| ChangedOrderOfPersonEvents of 'person * 'pevent list * 'pevent list
|
||||
(** Person's events order has been modified *)
|
||||
| ChildrenNotInOrder of 'family * 'descend * 'person * 'person
|
||||
(** Children aren't ordered *)
|
||||
| CloseChildren of 'family * 'person * 'person
|
||||
(** Age difference between two child is less then 7 month (except for twins) *)
|
||||
| DeadOld of 'person * dmy
|
||||
(** Dead old (at the age older then 109 after 1900 year and older then 100 before) *)
|
||||
| DeadTooEarlyToBeFather of 'person * 'person
|
||||
(** Children is born in more then 1 year after his father's death *)
|
||||
| DistantChildren of 'family * 'person * 'person
|
||||
(** Age gap between two of siblings greater then 50 years *)
|
||||
| FEventOrder of 'person * 'fevent * 'fevent
|
||||
(** Familial events haven't been ordered correctly *)
|
||||
| FWitnessEventAfterDeath of 'person * 'fevent * 'family
|
||||
(** Witness is dead before familial event date *)
|
||||
| FWitnessEventBeforeBirth of 'person * 'fevent * 'family
|
||||
(** Witness is born after familial event date *)
|
||||
| IncoherentAncestorDate of 'person * 'person
|
||||
(** Ancestor is born after person's birth *)
|
||||
| MarriageDateAfterDeath of 'person (** Person is married after his death *)
|
||||
| MarriageDateBeforeBirth of 'person
|
||||
(** Person is married before his birth *)
|
||||
| MotherDeadBeforeChildBirth of 'person * 'person
|
||||
(** Children is born after his mother's death *)
|
||||
| ParentBornAfterChild of 'person * 'person
|
||||
(** Parent is born after one of his children *)
|
||||
| ParentTooOld of 'person * dmy * 'person
|
||||
(** Person became a parent at age older then 55 years for mother and 70 for father *)
|
||||
| ParentTooYoung of 'person * dmy * 'person
|
||||
(** Person became a parent at age younger then 11 years old *)
|
||||
| PEventOrder of 'person * 'pevent * 'pevent
|
||||
(** Personal events haven't been ordered correctly *)
|
||||
| PossibleDuplicateFam of 'family * 'family
|
||||
(** There is a possibility that two families are a duplicate of each other *)
|
||||
| PossibleDuplicateFamHomonymous of 'family * 'family * 'person
|
||||
(** There is a possibility that two families are a duplicate of each other (Homonymous spouse) *)
|
||||
| PWitnessEventAfterDeath of 'person * 'pevent * 'person
|
||||
(** Witness is dead before personal event date *)
|
||||
| PWitnessEventBeforeBirth of 'person * 'pevent * 'person
|
||||
(** Witness is born after personal event date *)
|
||||
| TitleDatesError of 'person * 'title
|
||||
(** Title's start date is after end date or person is born after title dates *)
|
||||
| UndefinedSex of 'person (** Person has undefined sex (Neuter) *)
|
||||
| YoungForMarriage of 'person * dmy * 'family
|
||||
(** Person is married before he was 12 years old *)
|
||||
| OldForMarriage of 'person * dmy * 'family
|
||||
(** Person is married after he was 100 years old *)
|
||||
|
||||
(** Missing sources warning *)
|
||||
type ('person, 'descend, 'title) misc = MissingSources
|
||||
|
||||
(** Database note/page reading mode *)
|
||||
type rn_mode =
|
||||
| RnAll (** Read all content *)
|
||||
| Rn1Ln (** Read first line *)
|
||||
| RnDeg (** If file isn't empty returns a space *)
|
||||
|
||||
type base_notes = {
|
||||
(* read content of the page with giving mode.
|
||||
Page "" represent database note *)
|
||||
nread : string -> rn_mode -> string; (* origin .gw filename *)
|
||||
norigin_file : string; (* returns list of extended pages *)
|
||||
efiles : unit -> string list;
|
||||
}
|
||||
(** Database note/page explorer structure *)
|
||||
|
||||
(** Update modification used for history tracking *)
|
||||
type ('iper, 'person, 'family, 'string) base_changed =
|
||||
| U_Add_person of ('iper, 'person, 'string) gen_person
|
||||
| U_Modify_person of
|
||||
('iper, 'person, 'string) gen_person
|
||||
* ('iper, 'person, 'string) gen_person
|
||||
| U_Delete_person of ('iper, 'person, 'string) gen_person
|
||||
| U_Merge_person of
|
||||
('iper, 'person, 'string) gen_person
|
||||
* ('iper, 'person, 'string) gen_person
|
||||
* ('iper, 'person, 'string) gen_person
|
||||
| U_Send_image of ('iper, 'person, 'string) gen_person
|
||||
| U_Delete_image of ('iper, 'person, 'string) gen_person
|
||||
| U_Add_family of
|
||||
('iper, 'person, 'string) gen_person
|
||||
* ('person, 'family, 'string) gen_family
|
||||
| U_Modify_family of
|
||||
('iper, 'person, 'string) gen_person
|
||||
* ('person, 'family, 'string) gen_family
|
||||
* ('person, 'family, 'string) gen_family
|
||||
| U_Delete_family of
|
||||
('iper, 'person, 'string) gen_person
|
||||
* ('person, 'family, 'string) gen_family
|
||||
| U_Invert_family of ('iper, 'person, 'string) gen_person * 'family
|
||||
| U_Merge_family of
|
||||
('iper, 'person, 'string) gen_person
|
||||
* ('person, 'family, 'string) gen_family
|
||||
* ('person, 'family, 'string) gen_family
|
||||
* ('person, 'family, 'string) gen_family
|
||||
| U_Change_children_name of
|
||||
('iper, 'person, 'string) gen_person
|
||||
* ((string * string * int * 'person) * (string * string * int * 'person))
|
||||
list
|
||||
| U_Add_parent of
|
||||
('iper, 'person, 'string) gen_person
|
||||
* ('person, 'family, 'string) gen_family
|
||||
| U_Kill_ancestors of ('iper, 'person, 'string) gen_person
|
||||
(* Modification U_Multi used when multiple persons are modified successively. Separation with U_Modify_person is necessary to inform foreign notify_change script
|
||||
about database change without creating process for every person. *)
|
||||
| U_Multi of
|
||||
('iper, 'person, 'string) gen_person
|
||||
* ('iper, 'person, 'string) gen_person
|
||||
* bool
|
||||
| U_Notes of int option * string
|
||||
|
||||
(** TODOOCP : doc *)
|
||||
module NLDB = struct
|
||||
type ('a, 'b) page =
|
||||
| PgInd of 'a
|
||||
| PgFam of 'b
|
||||
| PgNotes
|
||||
| PgMisc of string
|
||||
| PgWizard of string
|
||||
|
||||
type key = string * string * int
|
||||
type ind = { lnTxt : string option; lnPos : int }
|
||||
type ('a, 'b) t = (('a, 'b) page * (string list * (key * ind) list)) list
|
||||
end
|
||||
|
||||
let ( ^^^ ) = Adef.( ^^^ )
|
||||
let ( ^>^ ) = Adef.( ^>^ )
|
||||
let ( ^<^ ) = Adef.( ^<^ )
|
||||
4
lib/def/dune
Normal file
4
lib/def/dune
Normal file
@@ -0,0 +1,4 @@
|
||||
(library
|
||||
(name geneweb_def)
|
||||
(public_name geneweb.def)
|
||||
(wrapped false))
|
||||
1882
lib/descendDisplay.ml
Normal file
1882
lib/descendDisplay.ml
Normal file
File diff suppressed because it is too large
Load Diff
66
lib/descendDisplay.mli
Normal file
66
lib/descendDisplay.mli
Normal file
@@ -0,0 +1,66 @@
|
||||
(* Public functions for API (plugin v7_descend) *)
|
||||
|
||||
val display_descendants_level :
|
||||
Config.config -> Gwdb.base -> int -> Gwdb.person -> unit
|
||||
(** Displays only descendants for specified level in unordered lists *)
|
||||
|
||||
val display_descendants_with_numbers :
|
||||
Config.config -> Gwdb.base -> int -> Gwdb.person -> unit
|
||||
(** Displays descendants with numerated by letter list. Title links to descendats index *)
|
||||
|
||||
val display_descendant_index :
|
||||
Config.config -> Gwdb.base -> int -> Gwdb.person -> unit
|
||||
(** Displays index of descendants *)
|
||||
|
||||
val display_spouse_index :
|
||||
Config.config -> Gwdb.base -> int -> Gwdb.person -> unit
|
||||
(** Displays index of descendant's spouses *)
|
||||
|
||||
val display_descendant_with_table :
|
||||
Config.config -> Gwdb.base -> int -> Gwdb.person -> unit
|
||||
(** Displays descendants in the table where rows are ordered by D'Aboville number. *)
|
||||
|
||||
val print_tree : Config.config -> Gwdb.base -> int -> Gwdb.person -> unit
|
||||
(** Displays tree of descendants *)
|
||||
|
||||
val print_aboville : Config.config -> Gwdb.base -> int -> Gwdb.person -> unit
|
||||
(** Displays descendants as follows :
|
||||
|
||||
person
|
||||
| desc1
|
||||
| desc2
|
||||
| | desc21
|
||||
| desc3
|
||||
|
||||
*)
|
||||
|
||||
val desmenu_print : Config.config -> Gwdb.base -> Gwdb.person -> unit
|
||||
(** Prints form that allows to customise display of descendants *)
|
||||
|
||||
val print : Config.config -> Gwdb.base -> Gwdb.person -> unit
|
||||
(** Displays the descendants of the selected in [conv.env] person. Descendants could be displayed by different ways
|
||||
depending on variable {i t} in [conv.env] environement:
|
||||
|
||||
- "L" dispalying descendants in unordered list
|
||||
- "F" same as "L" but displays only female line
|
||||
- "M" same as "L" but displays only female line
|
||||
- "H" table dispalying
|
||||
- "I" table dispalying with spouses information
|
||||
- "A" numerated list (d'Aboville)
|
||||
- "V" displaying a tree of descendants
|
||||
|
||||
Previous dispalyings are done by template evaluation. Next ones are done by functions inside this module:
|
||||
|
||||
- "B" for [print_aboville]
|
||||
- "S" for [display_descendants_level]
|
||||
- "K" for [display_descendant_with_table]
|
||||
- "N" for [display_descendants_with_numbers]
|
||||
- "G" for [display_descendant_index]
|
||||
- "C" for [display_spouse_index]
|
||||
- "T" for [print_tree]
|
||||
|
||||
Variable {i v} is used to select maximal level to descend for descendant displaying (1 for children, 2 for
|
||||
grandchildren, etc). If {i t} variable isn't defined, then displays the form that allows
|
||||
customising of display.
|
||||
|
||||
*)
|
||||
174
lib/difference.ml
Normal file
174
lib/difference.ml
Normal file
@@ -0,0 +1,174 @@
|
||||
(* $Id: diff.ml,v 5.1 2006-11-03 10:31:18 ddr Exp $ *)
|
||||
|
||||
(* Parts of Code of GNU diff (analyze.c) translated from C to OCaml
|
||||
and adjusted. Basic algorithm described by Eugene W.Myers in:
|
||||
"An O(ND) Difference Algorithm and Its Variations" *)
|
||||
|
||||
exception DiagReturn of int
|
||||
|
||||
let diag fd bd sh xv yv xoff xlim yoff ylim =
|
||||
let dmin = xoff - ylim in
|
||||
let dmax = xlim - yoff in
|
||||
let fmid = xoff - yoff in
|
||||
let bmid = xlim - ylim in
|
||||
let odd = (fmid - bmid) land 1 <> 0 in
|
||||
fd.(sh + fmid) <- xoff;
|
||||
bd.(sh + bmid) <- xlim;
|
||||
try
|
||||
let rec loop fmin fmax bmin bmax =
|
||||
let fmin =
|
||||
if fmin > dmin then (
|
||||
fd.(sh + fmin - 2) <- -1;
|
||||
fmin - 1)
|
||||
else fmin + 1
|
||||
in
|
||||
let fmax =
|
||||
if fmax < dmax then (
|
||||
fd.(sh + fmax + 2) <- -1;
|
||||
fmax + 1)
|
||||
else fmax - 1
|
||||
in
|
||||
(let rec loop d =
|
||||
if d < fmin then ()
|
||||
else
|
||||
let tlo = fd.(sh + d - 1) in
|
||||
let thi = fd.(sh + d + 1) in
|
||||
let x = if tlo >= thi then tlo + 1 else thi in
|
||||
let x =
|
||||
let rec loop xv yv xlim ylim x y =
|
||||
if x < xlim && y < ylim && xv x == yv y then
|
||||
loop xv yv xlim ylim (x + 1) (y + 1)
|
||||
else x
|
||||
in
|
||||
loop xv yv xlim ylim x (x - d)
|
||||
in
|
||||
fd.(sh + d) <- x;
|
||||
if odd && bmin <= d && d <= bmax && bd.(sh + d) <= fd.(sh + d) then
|
||||
raise (DiagReturn d)
|
||||
else loop (d - 2)
|
||||
in
|
||||
loop fmax);
|
||||
let bmin =
|
||||
if bmin > dmin then (
|
||||
bd.(sh + bmin - 2) <- max_int;
|
||||
bmin - 1)
|
||||
else bmin + 1
|
||||
in
|
||||
let bmax =
|
||||
if bmax < dmax then (
|
||||
bd.(sh + bmax + 2) <- max_int;
|
||||
bmax + 1)
|
||||
else bmax - 1
|
||||
in
|
||||
(let rec loop d =
|
||||
if d < bmin then ()
|
||||
else
|
||||
let tlo = bd.(sh + d - 1) in
|
||||
let thi = bd.(sh + d + 1) in
|
||||
let x = if tlo < thi then tlo else thi - 1 in
|
||||
let x =
|
||||
let rec loop xv yv xoff yoff x y =
|
||||
if x > xoff && y > yoff && xv (x - 1) == yv (y - 1) then
|
||||
loop xv yv xoff yoff (x - 1) (y - 1)
|
||||
else x
|
||||
in
|
||||
loop xv yv xoff yoff x (x - d)
|
||||
in
|
||||
bd.(sh + d) <- x;
|
||||
if (not odd) && fmin <= d && d <= fmax && bd.(sh + d) <= fd.(sh + d)
|
||||
then raise (DiagReturn d)
|
||||
else loop (d - 2)
|
||||
in
|
||||
loop bmax);
|
||||
loop fmin fmax bmin bmax
|
||||
in
|
||||
loop fmid fmid bmid bmid
|
||||
with DiagReturn i -> i
|
||||
|
||||
let diff_loop a ai b bi n m =
|
||||
let fd = Array.make (n + m + 3) 0 in
|
||||
let bd = Array.make (n + m + 3) 0 in
|
||||
let sh = m + 1 in
|
||||
let xvec i = a.(ai.(i)) in
|
||||
let yvec j = b.(bi.(j)) in
|
||||
let chng1 = Array.make (Array.length a) true in
|
||||
let chng2 = Array.make (Array.length b) true in
|
||||
for i = 0 to n - 1 do
|
||||
chng1.(ai.(i)) <- false
|
||||
done;
|
||||
for j = 0 to m - 1 do
|
||||
chng2.(bi.(j)) <- false
|
||||
done;
|
||||
(let rec loop xoff xlim yoff ylim =
|
||||
let xoff, yoff =
|
||||
let rec loop xoff yoff =
|
||||
if xoff < xlim && yoff < ylim && xvec xoff == yvec yoff then
|
||||
loop (xoff + 1) (yoff + 1)
|
||||
else (xoff, yoff)
|
||||
in
|
||||
loop xoff yoff
|
||||
in
|
||||
let xlim, ylim =
|
||||
let rec loop xlim ylim =
|
||||
if xlim > xoff && ylim > yoff && xvec (xlim - 1) == yvec (ylim - 1)
|
||||
then loop (xlim - 1) (ylim - 1)
|
||||
else (xlim, ylim)
|
||||
in
|
||||
loop xlim ylim
|
||||
in
|
||||
if xoff = xlim then
|
||||
for y = yoff to ylim - 1 do
|
||||
chng2.(bi.(y)) <- true
|
||||
done
|
||||
else if yoff = ylim then
|
||||
for x = xoff to xlim - 1 do
|
||||
chng1.(ai.(x)) <- true
|
||||
done
|
||||
else
|
||||
let d = diag fd bd sh xvec yvec xoff xlim yoff ylim in
|
||||
let b = bd.(sh + d) in
|
||||
loop xoff b yoff (b - d);
|
||||
loop b xlim (b - d) ylim
|
||||
in
|
||||
loop 0 n 0 m);
|
||||
(chng1, chng2)
|
||||
|
||||
(* [make_indexer a b] returns an array of index of items of [a] which
|
||||
are also present in [b]; this way, the main algorithm can skip items
|
||||
which, anyway, are different. This improves the speed much.
|
||||
The same time, this function updates the items of so that all
|
||||
equal items point to the same unique item. All items comparisons in
|
||||
the main algorithm can therefore be done with [==] instead of [=],
|
||||
what can improve speed much. *)
|
||||
let make_indexer a b =
|
||||
let n = Array.length a in
|
||||
let htb = Hashtbl.create (10 * Array.length b) in
|
||||
Array.iteri
|
||||
(fun i e ->
|
||||
try b.(i) <- Hashtbl.find htb e with Not_found -> Hashtbl.add htb e e)
|
||||
b;
|
||||
let ai = Array.make n 0 in
|
||||
let k =
|
||||
let rec loop i k =
|
||||
if i = n then k
|
||||
else
|
||||
let k =
|
||||
try
|
||||
a.(i) <- Hashtbl.find htb a.(i);
|
||||
(* line found (since "Not_found" not raised) *)
|
||||
ai.(k) <- i;
|
||||
k + 1
|
||||
with Not_found -> k
|
||||
in
|
||||
loop (i + 1) k
|
||||
in
|
||||
loop 0 0
|
||||
in
|
||||
Array.sub ai 0 k
|
||||
|
||||
let f a b =
|
||||
let ai = make_indexer a b in
|
||||
let bi = make_indexer b a in
|
||||
let n = Array.length ai in
|
||||
let m = Array.length bi in
|
||||
diff_loop a ai b bi n m
|
||||
18
lib/difference.mli
Normal file
18
lib/difference.mli
Normal file
@@ -0,0 +1,18 @@
|
||||
(* $Id: diff.mli,v 5.1 2006-11-03 10:31:18 ddr Exp $ *)
|
||||
|
||||
(** Differences between two arrays. *)
|
||||
|
||||
val f : 'a array -> 'a array -> bool array * bool array
|
||||
(** [Difference.f a1 a2] returns a couple of two arrays of booleans [(d1, d2)].
|
||||
[d1] has the same size as [a1].
|
||||
[d2] has the same size as [a2].
|
||||
[d1.(i)] is [True] if [a1.(i)] has no corresponding value in [a2].
|
||||
[d2.(i)] is [True] if [a2.(i)] has no corresponding value in [a1].
|
||||
[d1] and [s2] have the same number of values equal to [False].
|
||||
|
||||
Can be used to write the [diff] program (comparison of two files),
|
||||
the input arrays being the array of lines of each file.
|
||||
|
||||
Can be used also to compare two strings (they must have been exploded
|
||||
into arrays of chars), or two DNA strings, and so on.
|
||||
*)
|
||||
23
lib/dune.in
Normal file
23
lib/dune.in
Normal file
@@ -0,0 +1,23 @@
|
||||
(dirs :standard \ %%%DUNE_DIRS_EXCLUDE%%%)
|
||||
|
||||
(ocamllex (modules templ_parser))
|
||||
|
||||
(library
|
||||
(name geneweb)
|
||||
(public_name geneweb)
|
||||
(synopsis "GeneWeb library")
|
||||
(preprocess (action (run %{bin:cppo} %%%CPPO_D%%% -V OCAML:%{ocaml_version} %{input-file})))
|
||||
(libraries unix
|
||||
str
|
||||
stdlib-shims
|
||||
camlp-streams
|
||||
geneweb_core
|
||||
geneweb_def
|
||||
geneweb_gwdb
|
||||
geneweb_sosa_mli
|
||||
geneweb_util
|
||||
markup
|
||||
uri
|
||||
wserver)
|
||||
(modules_without_implementation templAst)
|
||||
)
|
||||
107
lib/event.ml
Normal file
107
lib/event.ml
Normal file
@@ -0,0 +1,107 @@
|
||||
open Def
|
||||
open Gwdb
|
||||
|
||||
type 'a event_name =
|
||||
| Pevent of 'a gen_pers_event_name
|
||||
| Fevent of 'a gen_fam_event_name
|
||||
|
||||
(*
|
||||
On ignore les événements personnalisés.
|
||||
Dans l'ordre de priorité :
|
||||
birth, baptism, ..., death, funeral, burial/cremation.
|
||||
Pour les évènements familiaux, cet ordre est envisageable :
|
||||
engage, PACS, marriage bann, marriage contract, marriage, ...,
|
||||
separate, divorce
|
||||
*)
|
||||
let compare_event_name name1 name2 =
|
||||
match (name1, name2) with
|
||||
| Pevent Epers_Birth, _ -> -1
|
||||
| _, Pevent Epers_Birth -> 1
|
||||
| ( Pevent Epers_Baptism,
|
||||
Pevent (Epers_Death | Epers_Funeral | Epers_Burial | Epers_Cremation) ) ->
|
||||
-1
|
||||
| ( Pevent (Epers_Death | Epers_Funeral | Epers_Burial | Epers_Cremation),
|
||||
Pevent Epers_Baptism ) ->
|
||||
1
|
||||
| Pevent Epers_Cremation, Pevent Epers_Burial -> -1
|
||||
| Pevent (Epers_Burial | Epers_Cremation), _ -> 1
|
||||
| _, Pevent (Epers_Burial | Epers_Cremation) -> -1
|
||||
| Pevent Epers_Funeral, _ -> 1
|
||||
| _, Pevent Epers_Funeral -> -1
|
||||
| Pevent Epers_Death, _ -> 1
|
||||
| _, Pevent Epers_Death -> -1
|
||||
| _ -> 0
|
||||
(*TODO Fevent??*)
|
||||
|
||||
let compare get_name get_date e1 e2 =
|
||||
match Date.cdate_to_dmy_opt (get_date e1) with
|
||||
| None -> compare_event_name (get_name e1) (get_name e2)
|
||||
| Some d1 -> (
|
||||
match Date.cdate_to_dmy_opt (get_date e2) with
|
||||
| None -> compare_event_name (get_name e1) (get_name e2)
|
||||
| Some d2 -> (
|
||||
match Date.compare_dmy_opt ~strict:false d1 d2 with
|
||||
| Some 0 | None -> compare_event_name (get_name e1) (get_name e2)
|
||||
| Some x -> x))
|
||||
|
||||
let sort_events get_name get_date events =
|
||||
List.stable_sort (fun e1 e2 -> compare get_name get_date e1 e2) events
|
||||
|
||||
type 'a event_item =
|
||||
'a event_name
|
||||
* cdate
|
||||
* istr
|
||||
* istr
|
||||
* istr
|
||||
* (iper * witness_kind) array
|
||||
* iper option
|
||||
|
||||
let events conf base p =
|
||||
if not (Util.authorized_age conf base p) then []
|
||||
else
|
||||
let pevents =
|
||||
List.fold_right
|
||||
(fun evt events ->
|
||||
let name = Pevent evt.epers_name in
|
||||
let date = evt.epers_date in
|
||||
let place = evt.epers_place in
|
||||
let note = evt.epers_note in
|
||||
let src = evt.epers_src in
|
||||
let wl = evt.epers_witnesses in
|
||||
let x = (name, date, place, note, src, wl, None) in
|
||||
x :: events)
|
||||
(get_pevents p) []
|
||||
in
|
||||
let fevents =
|
||||
Array.fold_right
|
||||
(fun ifam fevents ->
|
||||
let fam = foi base ifam in
|
||||
let isp = Gutil.spouse (get_iper p) fam in
|
||||
let m_auth =
|
||||
Util.authorized_age conf base (Util.pget conf base isp)
|
||||
in
|
||||
let fam_fevents =
|
||||
if m_auth then
|
||||
List.fold_right
|
||||
(fun evt fam_fevents ->
|
||||
let name = Fevent evt.efam_name in
|
||||
let date = evt.efam_date in
|
||||
let place = evt.efam_place in
|
||||
let note = evt.efam_note in
|
||||
let src = evt.efam_src in
|
||||
let wl = evt.efam_witnesses in
|
||||
let x = (name, date, place, note, src, wl, Some isp) in
|
||||
x :: fam_fevents)
|
||||
(get_fevents fam) []
|
||||
else []
|
||||
in
|
||||
fam_fevents @ fevents)
|
||||
(get_family p) []
|
||||
in
|
||||
pevents @ fevents
|
||||
|
||||
let sorted_events conf base p =
|
||||
let unsorted_events = events conf base p in
|
||||
let get_name (n, _, _, _, _, _, _) = n in
|
||||
let get_date (_, date, _, _, _, _, _) = date in
|
||||
sort_events get_name get_date unsorted_events
|
||||
29
lib/event.mli
Normal file
29
lib/event.mli
Normal file
@@ -0,0 +1,29 @@
|
||||
open Gwdb
|
||||
|
||||
(* NOTE : checkItem defined this as 'string event_same, instead of just istr
|
||||
.. why make it complicated *)
|
||||
type 'a event_name =
|
||||
| Pevent of 'a Def.gen_pers_event_name
|
||||
| Fevent of 'a Def.gen_fam_event_name
|
||||
|
||||
type 'a event_item =
|
||||
'a event_name
|
||||
* Def.cdate
|
||||
* istr
|
||||
* istr
|
||||
* istr
|
||||
* (iper * Def.witness_kind) array
|
||||
* iper option
|
||||
(** a representation of events *)
|
||||
|
||||
val compare_event_name : 'a event_name -> 'a event_name -> int
|
||||
|
||||
val sort_events :
|
||||
('a -> 'b event_name) -> ('a -> Adef.cdate) -> 'a list -> 'a list
|
||||
(** Sort events (both personal and familial) by their date and their name *)
|
||||
|
||||
val events : Config.config -> base -> person -> istr event_item list
|
||||
(** [events conf base p] is the list of [p]'s events *)
|
||||
|
||||
val sorted_events : Config.config -> base -> person -> istr event_item list
|
||||
(** [sorted_events conf base p] is the list of [p]'s events, sorted by Checkitem.sorted_events *)
|
||||
488
lib/fixbase.ml
Normal file
488
lib/fixbase.ml
Normal file
@@ -0,0 +1,488 @@
|
||||
open Def
|
||||
open Gwdb
|
||||
|
||||
type patch =
|
||||
| Fix_NBDS of iper
|
||||
| Fix_AddedUnion of iper
|
||||
| Fix_AddedParents of iper
|
||||
| Fix_ParentDeleted of iper
|
||||
| Fix_AddedChild of ifam
|
||||
| Fix_RemovedUnion of iper * ifam
|
||||
| Fix_RemovedDuplicateUnion of iper * ifam
|
||||
| Fix_AddedRelatedFromPevent of iper * iper
|
||||
| Fix_AddedRelatedFromFevent of iper * iper
|
||||
| Fix_MarriageDivorce of ifam
|
||||
| Fix_MissingSpouse of ifam * iper
|
||||
| Fix_WrongUTF8Encoding of
|
||||
Gwdb.ifam option * Gwdb.iper option * (Gwdb.istr * Gwdb.istr) option
|
||||
| Fix_UpdatedOcc of iper * int * int
|
||||
|
||||
let mk_pevent name date place note src =
|
||||
{
|
||||
epers_name = name;
|
||||
epers_date = date;
|
||||
epers_place = place;
|
||||
epers_reason = empty_string;
|
||||
epers_note = note;
|
||||
epers_src = src;
|
||||
epers_witnesses = [||];
|
||||
}
|
||||
|
||||
let of_pevent e = (e.epers_date, e.epers_place, e.epers_note, e.epers_src)
|
||||
|
||||
let find_pevent names pevents =
|
||||
List.find_opt (fun x -> List.mem x.epers_name names) pevents
|
||||
|
||||
let fix_pevents ?report base pp =
|
||||
(* Should it use UpdateIndOk.reconstitute_from_pevents? *)
|
||||
(* TODO clean up *)
|
||||
let p = gen_person_of_person pp in
|
||||
let empty_bi =
|
||||
(Date.cdate_None, Gwdb.empty_string, Gwdb.empty_string, Gwdb.empty_string)
|
||||
in
|
||||
let empty_bp =
|
||||
(Date.cdate_None, Gwdb.empty_string, Gwdb.empty_string, Gwdb.empty_string)
|
||||
in
|
||||
let empty_de =
|
||||
(Date.cdate_None, Gwdb.empty_string, Gwdb.empty_string, Gwdb.empty_string)
|
||||
in
|
||||
let empty_bu =
|
||||
(Date.cdate_None, Gwdb.empty_string, Gwdb.empty_string, Gwdb.empty_string)
|
||||
in
|
||||
let pevents = p.pevents in
|
||||
let aux name date place note src empty pevents =
|
||||
match find_pevent [ name ] pevents with
|
||||
| None ->
|
||||
let pevents =
|
||||
if (date, place, note, src) <> empty then
|
||||
mk_pevent name date place note src :: pevents
|
||||
else pevents
|
||||
in
|
||||
((date, place, note, src), pevents)
|
||||
| Some e ->
|
||||
let e' =
|
||||
{
|
||||
epers_name = e.epers_name;
|
||||
epers_date =
|
||||
(if e.epers_date = Date.cdate_None then date else e.epers_date);
|
||||
epers_place =
|
||||
(if e.epers_place = Gwdb.empty_string then place
|
||||
else e.epers_place);
|
||||
epers_reason = e.epers_reason;
|
||||
epers_note =
|
||||
(if e.epers_note = Gwdb.empty_string then note else e.epers_note);
|
||||
epers_src =
|
||||
(if e.epers_src = Gwdb.empty_string then src else e.epers_src);
|
||||
epers_witnesses = e.epers_witnesses;
|
||||
}
|
||||
in
|
||||
(of_pevent e', Mutil.list_replace e e' pevents)
|
||||
in
|
||||
let (birth, birth_place, birth_note, birth_src), pevents =
|
||||
aux Epers_Birth p.birth p.birth_place p.birth_note p.birth_src empty_bi
|
||||
pevents
|
||||
in
|
||||
let (baptism, baptism_place, baptism_note, baptism_src), pevents =
|
||||
aux Epers_Baptism p.baptism p.baptism_place p.baptism_note p.baptism_src
|
||||
empty_bp pevents
|
||||
in
|
||||
let (death, death_place, death_note, death_src), pevents =
|
||||
let death =
|
||||
match p.death with
|
||||
| Death (_, d) -> d
|
||||
| NotDead | DeadYoung | DeadDontKnowWhen | DontKnowIfDead | OfCourseDead
|
||||
->
|
||||
Date.cdate_None
|
||||
in
|
||||
aux Epers_Death death p.death_place p.death_note p.death_src empty_de
|
||||
pevents
|
||||
in
|
||||
let death =
|
||||
match p.death with
|
||||
| Death _ -> p.death
|
||||
| NotDead when death <> Date.cdate_None -> Death (Unspecified, death)
|
||||
| x -> x
|
||||
in
|
||||
let (burial, burial_place, burial_note, burial_src), pevents =
|
||||
match p.burial with
|
||||
| UnknownBurial -> (
|
||||
match find_pevent [ Epers_Burial; Epers_Cremation ] pevents with
|
||||
| None ->
|
||||
( (UnknownBurial, p.burial_place, p.burial_note, p.burial_src),
|
||||
pevents )
|
||||
| Some e ->
|
||||
let bu, bu_place, bu_note, bu_src = of_pevent e in
|
||||
( ( (match e.epers_name with
|
||||
| Epers_Burial -> Buried bu
|
||||
| Epers_Cremation -> Cremated bu
|
||||
| _ -> assert false),
|
||||
bu_place,
|
||||
bu_note,
|
||||
bu_src ),
|
||||
pevents ))
|
||||
| Buried d ->
|
||||
let (d, bu_place, bu_note, bu_src), pevents =
|
||||
aux Epers_Burial d p.burial_place p.burial_note p.burial_src empty_bu
|
||||
pevents
|
||||
in
|
||||
((Buried d, bu_place, bu_note, bu_src), pevents)
|
||||
| Cremated d ->
|
||||
let (d, bu_place, bu_note, bu_src), pevents =
|
||||
aux Epers_Cremation d p.burial_place p.burial_note p.burial_src
|
||||
empty_bu pevents
|
||||
in
|
||||
((Cremated d, bu_place, bu_note, bu_src), pevents)
|
||||
in
|
||||
let p' =
|
||||
{
|
||||
p with
|
||||
birth;
|
||||
birth_place;
|
||||
birth_note;
|
||||
birth_src;
|
||||
baptism;
|
||||
baptism_place;
|
||||
baptism_note;
|
||||
baptism_src;
|
||||
death;
|
||||
death_place;
|
||||
death_note;
|
||||
death_src;
|
||||
burial;
|
||||
burial_place;
|
||||
burial_note;
|
||||
burial_src;
|
||||
pevents;
|
||||
}
|
||||
in
|
||||
if p <> p' then (
|
||||
patch_person base p.key_index p';
|
||||
match report with Some fn -> fn (Fix_NBDS p.key_index) | None -> ())
|
||||
|
||||
let check_NBDS ?report progress base =
|
||||
let nb_ind = nb_of_persons base in
|
||||
Gwdb.Collection.iteri
|
||||
(fun i p ->
|
||||
progress i nb_ind;
|
||||
fix_pevents ?report base p)
|
||||
(Gwdb.persons base)
|
||||
|
||||
let check_families_parents ?report progress base =
|
||||
let nb_fam = nb_of_families base in
|
||||
Gwdb.Collection.iteri
|
||||
(fun i fam ->
|
||||
progress i nb_fam;
|
||||
let ifam = get_ifam fam in
|
||||
Array.iter
|
||||
(fun ip ->
|
||||
let unions = get_family (poi base ip) in
|
||||
if not @@ Array.mem ifam unions then (
|
||||
patch_union base ip { family = Array.append unions [| ifam |] };
|
||||
match report with Some fn -> fn (Fix_AddedUnion ip) | None -> ()))
|
||||
(get_parent_array fam))
|
||||
(Gwdb.families base)
|
||||
|
||||
let check_families_children ?report progress base =
|
||||
let nb_fam = nb_of_families base in
|
||||
Gwdb.Collection.iteri
|
||||
(fun i fam ->
|
||||
let ifam = get_ifam fam in
|
||||
progress i nb_fam;
|
||||
let children = get_children fam in
|
||||
for j = 0 to Array.length children - 1 do
|
||||
let ip = children.(j) in
|
||||
let a = poi base ip in
|
||||
let parents = get_parents a in
|
||||
if parents = Some dummy_ifam || parents = None then (
|
||||
patch_ascend base ip { parents = Some ifam; consang = get_consang a };
|
||||
match report with Some fn -> fn (Fix_AddedParents ip) | None -> ())
|
||||
(* else if parents <> Some ifam && verbosity1 then begin
|
||||
* (\* FIXME: what to do here ? *\)
|
||||
* Printf.printf "\tbad parents : %s\n" (string_of_p base ip);
|
||||
* flush stdout
|
||||
* end *)
|
||||
done)
|
||||
(Gwdb.families base)
|
||||
|
||||
let check_persons_parents ?report progress base =
|
||||
let nb_ind = nb_of_persons base in
|
||||
Gwdb.Collection.iteri
|
||||
(fun i p ->
|
||||
progress i nb_ind;
|
||||
get_parents p
|
||||
|> Option.iter @@ fun ifam ->
|
||||
let ip = get_iper p in
|
||||
let fam = Gwdb.foi base ifam in
|
||||
if get_ifam fam = dummy_ifam then (
|
||||
patch_ascend base ip { parents = None; consang = Adef.no_consang };
|
||||
match report with Some fn -> fn (Fix_ParentDeleted ip) | None -> ())
|
||||
else
|
||||
let children = get_children fam in
|
||||
if not @@ Array.mem ip children then (
|
||||
let children = Array.append children [| ip |] in
|
||||
patch_descend base ifam { children };
|
||||
match report with
|
||||
| Some fn -> fn (Fix_AddedChild ifam)
|
||||
| None -> ()))
|
||||
(Gwdb.persons base)
|
||||
|
||||
let check_persons_families ?report progress base =
|
||||
let nb_ind = nb_of_persons base in
|
||||
Gwdb.Collection.iteri
|
||||
(fun i p ->
|
||||
progress i nb_ind;
|
||||
let ip = get_iper p in
|
||||
let ifams = get_family p in
|
||||
let ifams' =
|
||||
Array.of_list
|
||||
@@ Array.fold_right
|
||||
(fun ifam acc ->
|
||||
let cpl = foi base ifam in
|
||||
if List.mem ifam acc then
|
||||
match report with
|
||||
| Some fn ->
|
||||
fn (Fix_RemovedDuplicateUnion (ip, ifam));
|
||||
acc
|
||||
| None -> acc
|
||||
else if not @@ Array.mem ip (get_parent_array cpl) then
|
||||
match report with
|
||||
| Some fn ->
|
||||
fn (Fix_RemovedUnion (ip, ifam));
|
||||
acc
|
||||
| None -> acc
|
||||
else ifam :: acc)
|
||||
ifams []
|
||||
in
|
||||
if ifams' <> ifams then patch_union base ip { family = ifams' })
|
||||
(Gwdb.persons base)
|
||||
|
||||
let check_pevents_witnesses ?report progress base =
|
||||
let nb_ind = nb_of_persons base in
|
||||
Gwdb.Collection.iteri
|
||||
(fun i p ->
|
||||
progress i nb_ind;
|
||||
let ip = get_iper p in
|
||||
List.iter
|
||||
(fun evt ->
|
||||
let witn = Array.map fst evt.epers_witnesses in
|
||||
for j = 0 to Array.length witn - 1 do
|
||||
let ip2 = witn.(j) in
|
||||
let p2 = poi base ip2 in
|
||||
if not (List.memq ip (get_related p2)) then (
|
||||
patch_person base ip2
|
||||
{
|
||||
(gen_person_of_person p2) with
|
||||
related = ip :: get_related p2;
|
||||
};
|
||||
match report with
|
||||
| Some fn -> fn (Fix_AddedRelatedFromPevent (ip2, ip))
|
||||
| None -> ())
|
||||
done)
|
||||
(get_pevents p))
|
||||
(Gwdb.persons base)
|
||||
|
||||
let check_fevents_witnesses ?report progress base =
|
||||
let nb_fam = nb_of_families base in
|
||||
Gwdb.Collection.iteri
|
||||
(fun i fam ->
|
||||
progress i nb_fam;
|
||||
let ifath = get_father fam in
|
||||
List.iter
|
||||
(fun evt ->
|
||||
let witn = Array.map fst evt.efam_witnesses in
|
||||
for j = 0 to Array.length witn - 1 do
|
||||
let ip = witn.(j) in
|
||||
let p = poi base ip in
|
||||
if not (List.memq ifath (get_related p)) then (
|
||||
patch_person base ip
|
||||
{
|
||||
(gen_person_of_person p) with
|
||||
related = ifath :: get_related p;
|
||||
};
|
||||
match report with
|
||||
| Some fn -> fn (Fix_AddedRelatedFromFevent (ip, ifath))
|
||||
| None -> ())
|
||||
done)
|
||||
(get_fevents fam))
|
||||
(Gwdb.families base)
|
||||
|
||||
let fix_marriage_divorce ?report progress base =
|
||||
let nb_fam = nb_of_families base in
|
||||
Gwdb.Collection.iteri
|
||||
(fun i fam ->
|
||||
progress i nb_fam;
|
||||
let fevents = get_fevents fam in
|
||||
let relation0 = get_relation fam in
|
||||
let marriage0 = get_marriage fam in
|
||||
let marriage_place0 = get_marriage_place fam in
|
||||
let marriage_note0 = get_marriage_note fam in
|
||||
let marriage_src0 = get_marriage_src fam in
|
||||
let divorce0 = get_divorce fam in
|
||||
let marr_data0 =
|
||||
(relation0, marriage0, marriage_place0, marriage_note0, marriage_src0)
|
||||
in
|
||||
let ( ((relation, marriage, marriage_place, marriage_note, marriage_src)
|
||||
as marr_data),
|
||||
divorce,
|
||||
_ ) =
|
||||
UpdateFamOk.reconstitute_from_fevents false (insert_string base "")
|
||||
fevents
|
||||
in
|
||||
if marr_data0 <> marr_data || divorce0 <> divorce then (
|
||||
let fam' =
|
||||
{
|
||||
(gen_family_of_family fam) with
|
||||
relation;
|
||||
marriage;
|
||||
marriage_place;
|
||||
marriage_note;
|
||||
marriage_src;
|
||||
divorce;
|
||||
}
|
||||
in
|
||||
patch_family base (get_ifam fam) fam';
|
||||
match report with
|
||||
| Some fn -> fn (Fix_MarriageDivorce (get_ifam fam))
|
||||
| None -> ()))
|
||||
(Gwdb.families base)
|
||||
|
||||
let fix_missing_spouses ?report progress base =
|
||||
let nb_fam = nb_of_families base in
|
||||
Gwdb.Collection.iteri
|
||||
(fun i fam ->
|
||||
progress i nb_fam;
|
||||
let aux i =
|
||||
let p = poi base i in
|
||||
if get_iper p = Gwdb.dummy_iper then (
|
||||
Gwdb.patch_union base i { family = [| get_ifam fam |] };
|
||||
Gwdb.patch_person base i
|
||||
{ (gen_person_of_person p) with key_index = i };
|
||||
match report with
|
||||
| Some fn -> fn (Fix_MissingSpouse (get_ifam fam, i))
|
||||
| None -> ())
|
||||
in
|
||||
aux @@ get_father fam;
|
||||
aux @@ get_mother fam)
|
||||
(Gwdb.families base)
|
||||
|
||||
let fix_utf8_sequence ?report progress base =
|
||||
let normalize_utf_8_date ifam iper s =
|
||||
let s' = Mutil.normalize_utf_8 s in
|
||||
(if s <> s' then
|
||||
match report with
|
||||
| Some fn -> fn (Fix_WrongUTF8Encoding (ifam, iper, None))
|
||||
| None -> ());
|
||||
s'
|
||||
in
|
||||
let normalize_utf_8 ifam iper i =
|
||||
let s = Gwdb.sou base i in
|
||||
let s' = Mutil.normalize_utf_8 s in
|
||||
let i' = Gwdb.insert_string base s' in
|
||||
(if i <> i' then
|
||||
match report with
|
||||
| Some fn -> fn (Fix_WrongUTF8Encoding (ifam, iper, Some (i, i')))
|
||||
| None -> ());
|
||||
i'
|
||||
in
|
||||
let nbf = nb_of_families base in
|
||||
let nbp = nb_of_persons base in
|
||||
let nb = nbp + nbf in
|
||||
let fp i = i in
|
||||
let ff i = i in
|
||||
let fs ifam iper i = normalize_utf_8 ifam iper i in
|
||||
let fd ifam iper = function
|
||||
| Dtext d -> Dtext (normalize_utf_8_date ifam iper d)
|
||||
| d -> d
|
||||
in
|
||||
Gwdb.Collection.iteri
|
||||
(fun i fam ->
|
||||
progress i nb;
|
||||
let ifam = Gwdb.get_ifam fam in
|
||||
let f = Gwdb.gen_family_of_family fam in
|
||||
let f' =
|
||||
Futil.map_family_ps ~fd:(fd (Some ifam) None) fp ff
|
||||
(fs (Some ifam) None) f
|
||||
in
|
||||
if f' <> f then Gwdb.patch_family base ifam f')
|
||||
(Gwdb.families base);
|
||||
Gwdb.Collection.iteri
|
||||
(fun i per ->
|
||||
progress (nbf + i) nb;
|
||||
let iper = Gwdb.get_iper per in
|
||||
let p = Gwdb.gen_person_of_person per in
|
||||
let p' =
|
||||
Futil.map_person_ps ~fd:(fd None (Some iper)) fp (fs None (Some iper)) p
|
||||
in
|
||||
if p' <> p then Gwdb.patch_person base iper p')
|
||||
(Gwdb.persons base)
|
||||
|
||||
let fix_key ?report progress base =
|
||||
let nb_ind = nb_of_persons base in
|
||||
let ipers = Gwdb.ipers base in
|
||||
let skip = Gwdb.iper_marker ipers false in
|
||||
Gwdb.Collection.iteri
|
||||
(fun i ip ->
|
||||
progress i nb_ind;
|
||||
let p = poi base ip in
|
||||
let f = Gwdb.p_first_name base p in
|
||||
let s = Gwdb.p_surname base p in
|
||||
if f <> "?" && s <> "?" then
|
||||
let key = Name.concat f s in
|
||||
let ipers = Gwdb.persons_of_name base key in
|
||||
let f = Name.lower f in
|
||||
let s = Name.lower s in
|
||||
let list =
|
||||
let rec loop acc = function
|
||||
| ip :: tl ->
|
||||
let p = poi base ip in
|
||||
if
|
||||
Name.lower @@ p_first_name base p = f
|
||||
&& Name.lower @@ p_surname base p = s
|
||||
then loop ((get_iper p, get_occ p) :: acc) tl
|
||||
else loop acc tl
|
||||
| [] -> acc
|
||||
in
|
||||
loop [] ipers
|
||||
in
|
||||
let rev_list = List.sort (fun a b -> compare b a) list in
|
||||
let cnt = ref 0 in
|
||||
let mem_occ occ acc list =
|
||||
List.exists (fun (_, o) -> o = occ) acc
|
||||
|| List.exists (fun (_, o) -> o = occ) list
|
||||
in
|
||||
let rec new_occ acc list =
|
||||
if mem_occ !cnt acc list then (
|
||||
incr cnt;
|
||||
new_occ acc list)
|
||||
else !cnt
|
||||
in
|
||||
let rec loop acc list =
|
||||
match acc with
|
||||
| [] -> (
|
||||
match list with
|
||||
| [] -> failwith key
|
||||
| (ip, occ) :: tl ->
|
||||
Gwdb.Marker.set skip ip true;
|
||||
loop [ (ip, occ) ] tl)
|
||||
| acc -> (
|
||||
match list with
|
||||
| [] -> acc
|
||||
| (ip, occ) :: tl ->
|
||||
if not @@ Gwdb.Marker.get skip ip then (
|
||||
Gwdb.Marker.set skip ip true;
|
||||
if mem_occ occ acc tl then (
|
||||
let occ' = new_occ acc list in
|
||||
Gwdb.patch_person base ip
|
||||
{
|
||||
(Gwdb.gen_person_of_person (poi base ip)) with
|
||||
occ = occ';
|
||||
};
|
||||
(match report with
|
||||
| Some fn -> fn (Fix_UpdatedOcc (ip, occ, occ'))
|
||||
| None -> ());
|
||||
loop ((ip, occ') :: acc) tl)
|
||||
else loop ((ip, occ) :: acc) tl)
|
||||
else loop ((ip, occ) :: acc) tl)
|
||||
in
|
||||
ignore @@ loop [] rev_list)
|
||||
ipers
|
||||
83
lib/fixbase.mli
Normal file
83
lib/fixbase.mli
Normal file
@@ -0,0 +1,83 @@
|
||||
(** All the function of this module scan the base and fix what is considered as corrupted data.
|
||||
|
||||
They all share a same signature : [let check_XXX ?report progress base = ...]
|
||||
|
||||
The optionnal [report] function should be used to track changes.
|
||||
|
||||
[progress i max] keep tracks of the progress of a task. When called, task is
|
||||
about [i/max] done.
|
||||
|
||||
Note that it does not actually commit the changes, so if you do not want a dry run, apply
|
||||
[Gwdb.commit_patches]
|
||||
*)
|
||||
|
||||
(** All possible patches that could be automatically deducted from inconsistent
|
||||
or absent information in the database *)
|
||||
type patch =
|
||||
| Fix_NBDS of Gwdb.iper
|
||||
| Fix_AddedUnion of Gwdb.iper
|
||||
| Fix_AddedParents of Gwdb.iper
|
||||
| Fix_ParentDeleted of Gwdb.iper
|
||||
| Fix_AddedChild of Gwdb.ifam
|
||||
| Fix_RemovedUnion of Gwdb.iper * Gwdb.ifam
|
||||
| Fix_RemovedDuplicateUnion of Gwdb.iper * Gwdb.ifam
|
||||
| Fix_AddedRelatedFromPevent of Gwdb.iper * Gwdb.iper
|
||||
| Fix_AddedRelatedFromFevent of Gwdb.iper * Gwdb.iper
|
||||
| Fix_MarriageDivorce of Gwdb.ifam
|
||||
| Fix_MissingSpouse of Gwdb.ifam * Gwdb.iper
|
||||
| Fix_WrongUTF8Encoding of
|
||||
Gwdb.ifam option * Gwdb.iper option * (Gwdb.istr * Gwdb.istr) option
|
||||
| Fix_UpdatedOcc of Gwdb.iper * int * int
|
||||
|
||||
val check_NBDS :
|
||||
?report:(patch -> unit) -> (int -> int -> unit) -> Gwdb.base -> unit
|
||||
(** For every person in the base synchronise his birth, death, baptism and burial events with
|
||||
his fields and vice versa. *)
|
||||
|
||||
val check_families_parents :
|
||||
?report:(patch -> unit) -> (int -> int -> unit) -> Gwdb.base -> unit
|
||||
(** For every family's parent in the base add current family to the parent's union if absent. *)
|
||||
|
||||
val check_families_children :
|
||||
?report:(patch -> unit) -> (int -> int -> unit) -> Gwdb.base -> unit
|
||||
(** For every family's children in the base add current family to the children's ascendants if absent.
|
||||
Doesn't modify consanguinity rate. *)
|
||||
|
||||
val check_persons_parents :
|
||||
?report:(patch -> unit) -> (int -> int -> unit) -> Gwdb.base -> unit
|
||||
(** For every person checks their ascendants.
|
||||
If it references to the dummy family, then remove this reference.
|
||||
Otherwise add the person to the family's children if absent. *)
|
||||
|
||||
val check_persons_families :
|
||||
?report:(patch -> unit) -> (int -> int -> unit) -> Gwdb.base -> unit
|
||||
(** For every person in the base removes all duplicate families and families where person isn't a parent. *)
|
||||
|
||||
val check_pevents_witnesses :
|
||||
?report:(patch -> unit) -> (int -> int -> unit) -> Gwdb.base -> unit
|
||||
(** For every person's event's witness add current person to the list of related of the witness if absent. *)
|
||||
|
||||
val check_fevents_witnesses :
|
||||
?report:(patch -> unit) -> (int -> int -> unit) -> Gwdb.base -> unit
|
||||
(** For every family's event's witness add family's father to the list of related of the witness if absent. *)
|
||||
|
||||
val fix_marriage_divorce :
|
||||
?report:(patch -> unit) -> (int -> int -> unit) -> Gwdb.base -> unit
|
||||
(** For every family in the base synchronise its fields with marriage and divorce events. *)
|
||||
|
||||
val fix_missing_spouses :
|
||||
?report:(patch -> unit) -> (int -> int -> unit) -> Gwdb.base -> unit
|
||||
(** For every family, if a parent refers to a person dummy person (with dummy iper).
|
||||
Fix this person and add the family to their union list.
|
||||
If this situation happens, an explaination is that the person has been incorrectly deleted,
|
||||
instead of just erasing their personal details.
|
||||
*)
|
||||
|
||||
val fix_utf8_sequence :
|
||||
?report:(patch -> unit) -> (int -> int -> unit) -> Gwdb.base -> unit
|
||||
(** For every person's and family's field, remplace it with normalized UTF8 version. *)
|
||||
|
||||
val fix_key :
|
||||
?report:(patch -> unit) -> (int -> int -> unit) -> Gwdb.base -> unit
|
||||
(** For every person in the base, update their occurence number
|
||||
if someone with same key (normalized first name and last name, and occurence number) already exists. *)
|
||||
112
lib/gwdb-legacy/btree.ml
Normal file
112
lib/gwdb-legacy/btree.ml
Normal file
@@ -0,0 +1,112 @@
|
||||
(* This code is directly copied from OCaml stdlib (Map module)
|
||||
with t*)
|
||||
|
||||
(**************************************************************************)
|
||||
(* *)
|
||||
(* OCaml *)
|
||||
(* *)
|
||||
(* Xavier Leroy, projet Cristal, INRIA Rocquencourt *)
|
||||
(* *)
|
||||
(* Copyright 1996 Institut National de Recherche en Informatique et *)
|
||||
(* en Automatique. *)
|
||||
(* *)
|
||||
(* All rights reserved. This file is distributed under the terms of *)
|
||||
(* the GNU Lesser General Public License version 2.1, with the *)
|
||||
(* special exception on linking described in the file LICENSE. *)
|
||||
(* *)
|
||||
(**************************************************************************)
|
||||
|
||||
module type OrderedType = sig
|
||||
type t
|
||||
|
||||
val compare : t -> t -> int
|
||||
end
|
||||
|
||||
module type S = sig
|
||||
type key
|
||||
type +'a t
|
||||
|
||||
val mem : key -> 'a t -> bool
|
||||
val add : key -> 'a -> 'a t -> 'a t
|
||||
val find : key -> 'a t -> 'a
|
||||
|
||||
(* Added for GeneWeb *)
|
||||
val key_after : (key -> int) -> 'a t -> key
|
||||
val next : key -> 'a t -> key
|
||||
end
|
||||
|
||||
module Make (Ord : OrderedType) : S with type key = Ord.t = struct
|
||||
type key = Ord.t
|
||||
type 'a t = Empty | Node of { l : 'a t; v : key; d : 'a; r : 'a t; h : int }
|
||||
|
||||
let height = function Empty -> 0 | Node { h } -> h
|
||||
|
||||
let create l x d r =
|
||||
let hl = height l and hr = height r in
|
||||
Node { l; v = x; d; r; h = (if hl >= hr then hl + 1 else hr + 1) }
|
||||
|
||||
let bal l x d r =
|
||||
let hl = match l with Empty -> 0 | Node { h } -> h in
|
||||
let hr = match r with Empty -> 0 | Node { h } -> h in
|
||||
if hl > hr + 2 then
|
||||
match l with
|
||||
| Empty -> invalid_arg "Map.bal"
|
||||
| Node { l = ll; v = lv; d = ld; r = lr } -> (
|
||||
if height ll >= height lr then create ll lv ld (create lr x d r)
|
||||
else
|
||||
match lr with
|
||||
| Empty -> invalid_arg "Map.bal"
|
||||
| Node { l = lrl; v = lrv; d = lrd; r = lrr } ->
|
||||
create (create ll lv ld lrl) lrv lrd (create lrr x d r))
|
||||
else if hr > hl + 2 then
|
||||
match r with
|
||||
| Empty -> invalid_arg "Map.bal"
|
||||
| Node { l = rl; v = rv; d = rd; r = rr } -> (
|
||||
if height rr >= height rl then create (create l x d rl) rv rd rr
|
||||
else
|
||||
match rl with
|
||||
| Empty -> invalid_arg "Map.bal"
|
||||
| Node { l = rll; v = rlv; d = rld; r = rlr } ->
|
||||
create (create l x d rll) rlv rld (create rlr rv rd rr))
|
||||
else Node { l; v = x; d; r; h = (if hl >= hr then hl + 1 else hr + 1) }
|
||||
|
||||
let rec add x data = function
|
||||
| Empty -> Node { l = Empty; v = x; d = data; r = Empty; h = 1 }
|
||||
| Node { l; v; d; r; h } as m ->
|
||||
let c = Ord.compare x v in
|
||||
if c = 0 then if d == data then m else Node { l; v = x; d = data; r; h }
|
||||
else if c < 0 then
|
||||
let ll = add x data l in
|
||||
if l == ll then m else bal ll v d r
|
||||
else
|
||||
let rr = add x data r in
|
||||
if r == rr then m else bal l v d rr
|
||||
|
||||
let rec find x = function
|
||||
| Empty -> raise Not_found
|
||||
| Node { l; v; d; r } ->
|
||||
let c = Ord.compare x v in
|
||||
if c = 0 then d else find x (if c < 0 then l else r)
|
||||
|
||||
let rec mem x = function
|
||||
| Empty -> false
|
||||
| Node { l; v; r } ->
|
||||
let c = Ord.compare x v in
|
||||
c = 0 || mem x (if c < 0 then l else r)
|
||||
|
||||
(* Added for GeneWeb *)
|
||||
|
||||
let rec key_after f_compare = function
|
||||
| Empty -> raise Not_found
|
||||
| Node { l; v; r; _ } ->
|
||||
let c = f_compare v in
|
||||
if c < 0 then try key_after f_compare l with Not_found -> v
|
||||
else if c > 0 then key_after f_compare r
|
||||
else v
|
||||
|
||||
let rec next x = function
|
||||
| Empty -> raise Not_found
|
||||
| Node { l; v; r; _ } ->
|
||||
let c = Ord.compare x v in
|
||||
if c < 0 then try next x l with Not_found -> v else next x r
|
||||
end
|
||||
35
lib/gwdb-legacy/btree.mli
Normal file
35
lib/gwdb-legacy/btree.mli
Normal file
@@ -0,0 +1,35 @@
|
||||
(** Input signature of the functor [Btree.Make]. *)
|
||||
module type OrderedType = sig
|
||||
type t
|
||||
|
||||
val compare : t -> t -> int
|
||||
end
|
||||
|
||||
(** Output signature of the functor [Btree.Make]. *)
|
||||
module type S = sig
|
||||
type key
|
||||
(** Same as {!Stdlib.Map.S.key} *)
|
||||
|
||||
type +'a t
|
||||
(** Same as {!Stdlib.Map.S.t} *)
|
||||
|
||||
val mem : key -> 'a t -> bool
|
||||
(** Same as {!Stdlib.Map.S.mem} *)
|
||||
|
||||
val add : key -> 'a -> 'a t -> 'a t
|
||||
(** Same as {!Stdlib.Map.S.add} *)
|
||||
|
||||
val find : key -> 'a t -> 'a
|
||||
(** Same as {!Stdlib.Map.S.find} *)
|
||||
|
||||
val key_after : (key -> int) -> 'a t -> key
|
||||
(** [key_after f_compare m] browse map [m] to find the key [k] which
|
||||
gives [f_compare k = 0]. Raise [Not_found] if such key doesn't exists. *)
|
||||
|
||||
val next : key -> 'a t -> key
|
||||
(** [next k bt] returns the smallest key that is bigger then [k] inside [bt]. *)
|
||||
end
|
||||
|
||||
(** Functor building an implementation of the map structure given a
|
||||
totally ordered type. *)
|
||||
module Make : functor (Ord : OrderedType) -> S with type key = Ord.t
|
||||
1288
lib/gwdb-legacy/database.ml
Normal file
1288
lib/gwdb-legacy/database.ml
Normal file
File diff suppressed because it is too large
Load Diff
35
lib/gwdb-legacy/database.mli
Normal file
35
lib/gwdb-legacy/database.mli
Normal file
@@ -0,0 +1,35 @@
|
||||
(* Copyright (c) 1998-2007 INRIA *)
|
||||
|
||||
val opendb : string -> Dbdisk.dsk_base
|
||||
(** Initialise [dsk_base] from the database situated in the specified directory.
|
||||
Initialises both data and functionallity part. *)
|
||||
|
||||
val make :
|
||||
string ->
|
||||
string list ->
|
||||
((int, int, int) Def.gen_person array
|
||||
* int Def.gen_ascend array
|
||||
* int Def.gen_union array)
|
||||
* ((int, int, int) Def.gen_family array
|
||||
* int Def.gen_couple array
|
||||
* int Def.gen_descend array)
|
||||
* string array
|
||||
* Def.base_notes ->
|
||||
Dbdisk.dsk_base
|
||||
(** [make bname particles ((persons, ascendants, unions) (families, couples,
|
||||
descendants) strings base_notes)] returns initialised with giving data
|
||||
[dsk_base]. This function is called exclusively for database creating
|
||||
purpose. It means that, it contains only data without functionalities.
|
||||
Either call [opendb] on existing database or call [Gwdb.make], if you
|
||||
want to make requests. *)
|
||||
|
||||
(* Ajout pour l'API *)
|
||||
|
||||
type synchro_patch = {
|
||||
mutable synch_list : (string * int list * int list) list;
|
||||
}
|
||||
(** List of commited modifications inside the database. First element is a timestamp of a commit,
|
||||
second - changed/added by considered commit person ids, third - changed/added by considered commit families ids. *)
|
||||
|
||||
val input_synchro : string -> synchro_patch
|
||||
(** Get [synchro_patch] from the giving database directory. *)
|
||||
429
lib/gwdb-legacy/dbdisk.mli
Normal file
429
lib/gwdb-legacy/dbdisk.mli
Normal file
@@ -0,0 +1,429 @@
|
||||
(** {1 Aliases to [Def] and [Adef]} *)
|
||||
|
||||
type fix = Adef.fix (* FIXME: expose its type *)
|
||||
type cdate = Def.cdate (* FIXME: expose its type *)
|
||||
|
||||
type date = Def.date = Dgreg of dmy * calendar | Dtext of string
|
||||
and calendar = Def.calendar = Dgregorian | Djulian | Dfrench | Dhebrew
|
||||
|
||||
and dmy = Def.dmy = {
|
||||
day : int;
|
||||
month : int;
|
||||
year : int;
|
||||
prec : precision;
|
||||
delta : int;
|
||||
}
|
||||
|
||||
and dmy2 = Def.dmy2 = { day2 : int; month2 : int; year2 : int; delta2 : int }
|
||||
|
||||
and precision = Def.precision =
|
||||
| Sure
|
||||
| About
|
||||
| Maybe
|
||||
| Before
|
||||
| After
|
||||
| OrYear of dmy2
|
||||
| YearInt of dmy2
|
||||
|
||||
type relation_kind = Def.relation_kind =
|
||||
| Married
|
||||
| NotMarried
|
||||
| Engaged
|
||||
| NoSexesCheckNotMarried
|
||||
| NoMention
|
||||
| NoSexesCheckMarried
|
||||
| MarriageBann
|
||||
| MarriageContract
|
||||
| MarriageLicense
|
||||
| Pacs
|
||||
| Residence
|
||||
|
||||
type divorce = Def.divorce = NotDivorced | Divorced of cdate | Separated
|
||||
|
||||
type death_reason = Def.death_reason =
|
||||
| Killed
|
||||
| Murdered
|
||||
| Executed
|
||||
| Disappeared
|
||||
| Unspecified
|
||||
|
||||
type death = Def.death =
|
||||
| NotDead
|
||||
| Death of death_reason * cdate
|
||||
| DeadYoung
|
||||
| DeadDontKnowWhen
|
||||
| DontKnowIfDead
|
||||
| OfCourseDead
|
||||
|
||||
type burial = Def.burial = UnknownBurial | Buried of cdate | Cremated of cdate
|
||||
type access = Def.access = IfTitles | Public | Private
|
||||
|
||||
type 'string gen_title_name = 'string Def.gen_title_name =
|
||||
| Tmain
|
||||
| Tname of 'string
|
||||
| Tnone
|
||||
|
||||
type 'string gen_title = 'string Def.gen_title = {
|
||||
t_name : 'string gen_title_name;
|
||||
t_ident : 'string;
|
||||
t_place : 'string;
|
||||
t_date_start : cdate;
|
||||
t_date_end : cdate;
|
||||
t_nth : int;
|
||||
}
|
||||
|
||||
type witness_kind = Def.witness_kind =
|
||||
| Witness
|
||||
| Witness_GodParent
|
||||
| Witness_CivilOfficer
|
||||
| Witness_ReligiousOfficer
|
||||
| Witness_Informant
|
||||
| Witness_Attending
|
||||
| Witness_Mentioned
|
||||
| Witness_Other
|
||||
|
||||
type 'string gen_pers_event_name = 'string Def.gen_pers_event_name =
|
||||
| Epers_Birth
|
||||
| Epers_Baptism
|
||||
| Epers_Death
|
||||
| Epers_Burial
|
||||
| Epers_Cremation
|
||||
| Epers_Accomplishment
|
||||
| Epers_Acquisition
|
||||
| Epers_Adhesion
|
||||
| Epers_BaptismLDS
|
||||
| Epers_BarMitzvah
|
||||
| Epers_BatMitzvah
|
||||
| Epers_Benediction
|
||||
| Epers_ChangeName
|
||||
| Epers_Circumcision
|
||||
| Epers_Confirmation
|
||||
| Epers_ConfirmationLDS
|
||||
| Epers_Decoration
|
||||
| Epers_DemobilisationMilitaire
|
||||
| Epers_Diploma
|
||||
| Epers_Distinction
|
||||
| Epers_Dotation
|
||||
| Epers_DotationLDS
|
||||
| Epers_Education
|
||||
| Epers_Election
|
||||
| Epers_Emigration
|
||||
| Epers_Excommunication
|
||||
| Epers_FamilyLinkLDS
|
||||
| Epers_FirstCommunion
|
||||
| Epers_Funeral
|
||||
| Epers_Graduate
|
||||
| Epers_Hospitalisation
|
||||
| Epers_Illness
|
||||
| Epers_Immigration
|
||||
| Epers_ListePassenger
|
||||
| Epers_MilitaryDistinction
|
||||
| Epers_MilitaryPromotion
|
||||
| Epers_MilitaryService
|
||||
| Epers_MobilisationMilitaire
|
||||
| Epers_Naturalisation
|
||||
| Epers_Occupation
|
||||
| Epers_Ordination
|
||||
| Epers_Property
|
||||
| Epers_Recensement
|
||||
| Epers_Residence
|
||||
| Epers_Retired
|
||||
| Epers_ScellentChildLDS
|
||||
| Epers_ScellentParentLDS
|
||||
| Epers_ScellentSpouseLDS
|
||||
| Epers_VenteBien
|
||||
| Epers_Will
|
||||
| Epers_Name of 'string
|
||||
|
||||
type ('person, 'string) gen_pers_event =
|
||||
('person, 'string) Def.gen_pers_event = {
|
||||
epers_name : 'string gen_pers_event_name;
|
||||
epers_date : cdate;
|
||||
epers_place : 'string;
|
||||
epers_reason : 'string;
|
||||
epers_note : 'string;
|
||||
epers_src : 'string;
|
||||
epers_witnesses : ('person * witness_kind) array;
|
||||
}
|
||||
|
||||
type 'string gen_fam_event_name = 'string Def.gen_fam_event_name =
|
||||
| Efam_Marriage
|
||||
| Efam_NoMarriage
|
||||
| Efam_NoMention
|
||||
| Efam_Engage
|
||||
| Efam_Divorce
|
||||
| Efam_Separated
|
||||
| Efam_Annulation
|
||||
| Efam_MarriageBann
|
||||
| Efam_MarriageContract
|
||||
| Efam_MarriageLicense
|
||||
| Efam_PACS
|
||||
| Efam_Residence
|
||||
| Efam_Name of 'string
|
||||
|
||||
type ('person, 'string) gen_fam_event = ('person, 'string) Def.gen_fam_event = {
|
||||
efam_name : 'string gen_fam_event_name;
|
||||
efam_date : cdate;
|
||||
efam_place : 'string;
|
||||
efam_reason : 'string;
|
||||
efam_note : 'string;
|
||||
efam_src : 'string;
|
||||
efam_witnesses : ('person * witness_kind) array;
|
||||
}
|
||||
|
||||
type relation_type = Def.relation_type =
|
||||
| Adoption
|
||||
| Recognition
|
||||
| CandidateParent
|
||||
| GodParent
|
||||
| FosterParent
|
||||
|
||||
type ('person, 'string) gen_relation = ('person, 'string) Def.gen_relation = {
|
||||
r_type : relation_type;
|
||||
r_fath : 'person option;
|
||||
r_moth : 'person option;
|
||||
r_sources : 'string;
|
||||
}
|
||||
|
||||
type sex = Def.sex = Male | Female | Neuter
|
||||
|
||||
type place = Def.place = {
|
||||
other : string;
|
||||
town : string;
|
||||
township : string;
|
||||
canton : string;
|
||||
district : string;
|
||||
county : string;
|
||||
region : string;
|
||||
country : string;
|
||||
}
|
||||
|
||||
(* person *)
|
||||
|
||||
type ('iper, 'person, 'string) gen_person =
|
||||
('iper, 'person, 'string) Def.gen_person = {
|
||||
first_name : 'string;
|
||||
surname : 'string;
|
||||
occ : int;
|
||||
image : 'string;
|
||||
public_name : 'string;
|
||||
qualifiers : 'string list;
|
||||
aliases : 'string list;
|
||||
first_names_aliases : 'string list;
|
||||
surnames_aliases : 'string list;
|
||||
titles : 'string gen_title list;
|
||||
rparents : ('person, 'string) gen_relation list;
|
||||
related : 'person list;
|
||||
occupation : 'string;
|
||||
sex : sex;
|
||||
access : access;
|
||||
birth : cdate;
|
||||
birth_place : 'string;
|
||||
birth_note : 'string;
|
||||
birth_src : 'string;
|
||||
baptism : cdate;
|
||||
baptism_place : 'string;
|
||||
baptism_note : 'string;
|
||||
baptism_src : 'string;
|
||||
death : death;
|
||||
death_place : 'string;
|
||||
death_note : 'string;
|
||||
death_src : 'string;
|
||||
burial : burial;
|
||||
burial_place : 'string;
|
||||
burial_note : 'string;
|
||||
burial_src : 'string;
|
||||
pevents : ('person, 'string) gen_pers_event list;
|
||||
notes : 'string;
|
||||
psources : 'string;
|
||||
key_index : 'iper;
|
||||
}
|
||||
|
||||
type 'family gen_ascend = 'family Def.gen_ascend = {
|
||||
parents : 'family option;
|
||||
consang : fix;
|
||||
}
|
||||
|
||||
type 'family gen_union = 'family Def.gen_union = { family : 'family array }
|
||||
|
||||
(* family *)
|
||||
|
||||
type ('person, 'ifam, 'string) gen_family =
|
||||
('person, 'ifam, 'string) Def.gen_family = {
|
||||
marriage : cdate;
|
||||
marriage_place : 'string;
|
||||
marriage_note : 'string;
|
||||
marriage_src : 'string;
|
||||
witnesses : 'person array;
|
||||
relation : relation_kind;
|
||||
divorce : divorce;
|
||||
fevents : ('person, 'string) gen_fam_event list;
|
||||
comment : 'string;
|
||||
origin_file : 'string;
|
||||
fsources : 'string;
|
||||
fam_index : 'ifam;
|
||||
}
|
||||
|
||||
type 'person gen_couple = 'person Def.gen_couple (* FIXME: expose its type *)
|
||||
|
||||
type 'person gen_descend = 'person Def.gen_descend = {
|
||||
children : 'person array;
|
||||
}
|
||||
|
||||
type dsk_person = (int, int, int) gen_person
|
||||
(** Extended person's entry in the base *)
|
||||
|
||||
type dsk_ascend = int gen_ascend
|
||||
(** Person's ascendants entry in the base *)
|
||||
|
||||
type dsk_union = int gen_union
|
||||
(** Person's union entry in the base *)
|
||||
|
||||
type dsk_family = (int, int, int) gen_family
|
||||
(** Family's entry in the base *)
|
||||
|
||||
type dsk_couple = int gen_couple
|
||||
(** Family's couple entry in the base *)
|
||||
|
||||
type dsk_descend = int gen_descend
|
||||
(** Family's descendants entry in the base *)
|
||||
|
||||
type dsk_title = int gen_title
|
||||
(** Nobility title in the base *)
|
||||
|
||||
type 'a record_access = {
|
||||
(* Load array in the memory and cache it so it could be accessed
|
||||
instantly by other functions unless [clear_array] is called. *)
|
||||
load_array : unit -> unit;
|
||||
(* Get the nth element of array. In details, it searches for an element in
|
||||
the following order:
|
||||
- Search inside the pending patches
|
||||
- Search inside the commited patches
|
||||
- Search insede the loaded in memory array
|
||||
- Search inside the "base" file *)
|
||||
get : int -> 'a;
|
||||
(* Same as [get] but doesn't consider pending patches *)
|
||||
get_nopending : int -> 'a;
|
||||
(* Set the nth element of array *)
|
||||
set : int -> 'a -> unit;
|
||||
(* Return length of an array that by default takes into account
|
||||
commited patches *)
|
||||
mutable len : int;
|
||||
(* Output array with applied commited patches to the giving chanel *)
|
||||
output_array : out_channel -> unit;
|
||||
(* Remove array from the memory *)
|
||||
clear_array : unit -> unit;
|
||||
}
|
||||
(** Type that define the functions to use to access and manipulate with
|
||||
database arrays. *)
|
||||
|
||||
type string_person_index = {
|
||||
(* Find all person's ids that has giving surname/first name. *)
|
||||
find : int -> int list;
|
||||
(* Return surname's/first name's id. If it doen't present return id of the next
|
||||
name by alphabetical order *)
|
||||
cursor : string -> int;
|
||||
(* Return surname's/first name's id. If it doen't present return id of the next
|
||||
name by alphabetical order *)
|
||||
next : int -> int;
|
||||
}
|
||||
(** Data structure for optimised search throughout index by name
|
||||
(surname or first name). Considers also patched persons. *)
|
||||
|
||||
type visible_record_access = {
|
||||
v_write : unit -> unit;
|
||||
v_get : (dsk_person -> bool) -> int -> bool;
|
||||
}
|
||||
|
||||
type perm = RDONLY | RDRW
|
||||
|
||||
type base_data = {
|
||||
(* Array of persons *)
|
||||
persons : dsk_person record_access;
|
||||
(* Array of persons' ascendants *)
|
||||
ascends : dsk_ascend record_access;
|
||||
(* Array of persons' unions *)
|
||||
unions : dsk_union record_access;
|
||||
(* unused by default *)
|
||||
visible : visible_record_access;
|
||||
(* Array of families *)
|
||||
families : dsk_family record_access;
|
||||
(* Array of families' couples *)
|
||||
couples : dsk_couple record_access;
|
||||
(* Array of families' descendants *)
|
||||
descends : dsk_descend record_access;
|
||||
(* Array of strings *)
|
||||
strings : string record_access;
|
||||
(* Array of autorised to use surname's particles *)
|
||||
particles_txt : string list;
|
||||
(* Regular expression that matches particles in [particles_txt] *)
|
||||
particles : Re.re Lazy.t;
|
||||
(* Data base notes and extended page structure *)
|
||||
bnotes : Def.base_notes;
|
||||
(* Directory where database's files are stored *)
|
||||
bdir : string;
|
||||
perm : perm;
|
||||
}
|
||||
(** Data part of database *)
|
||||
|
||||
type base_func = {
|
||||
(* Return person's id from the giving key (first name, surname and occurene number).
|
||||
If person doesn't exists return None. Doesn't consider pending patches *)
|
||||
person_of_key : string -> string -> int -> int option;
|
||||
(* Return list of person ids that have giving name
|
||||
(could be one of the mix). Doesn't consider pending patches *)
|
||||
persons_of_name : string -> int list;
|
||||
(* Return list of surnames (string ids) that contain giving person's surname or surname substring.
|
||||
Consider also surnames of pathed persons. Doesn't consider pending patches *)
|
||||
strings_of_sname : string -> int list;
|
||||
(* Return list of first names (string ids) that contain giving person's first name or first name's
|
||||
substring. Consider also first names of pathed persons. Doesn't consider pending patches *)
|
||||
strings_of_fname : string -> int list;
|
||||
(* Search functionalities throughout index by surname *)
|
||||
persons_of_surname : string_person_index;
|
||||
(* Search functionalities throughout index by first name *)
|
||||
persons_of_first_name : string_person_index;
|
||||
(* Insert or modify person with a giving id (add to pending patches). *)
|
||||
patch_person : int -> dsk_person -> unit;
|
||||
(* Insert or modify ascendants of a person with a giving id (add to pending patches). *)
|
||||
patch_ascend : int -> dsk_ascend -> unit;
|
||||
(* Insert or modify union of a person with a giving id (add to pending patches). *)
|
||||
patch_union : int -> dsk_union -> unit;
|
||||
(* Insert or modify family with a giving id (add to pending patches). *)
|
||||
patch_family : int -> dsk_family -> unit;
|
||||
(* Insert or modify couple of a family with a giving id (add to pending patches). *)
|
||||
patch_couple : int -> dsk_couple -> unit;
|
||||
(* Insert or modify descendants of a family with a giving id (add to pending patches). *)
|
||||
patch_descend : int -> dsk_descend -> unit;
|
||||
(* Associate person to [name] inside the index.
|
||||
Added directly inside commited patches. *)
|
||||
patch_name : string -> int -> unit;
|
||||
(* Insert new string inside the pending patches and returns its id.
|
||||
If string already exists return its id. *)
|
||||
insert_string : string -> int;
|
||||
(* Commit pending patches and write a patches' new state inside "patches"
|
||||
file. "nb_persons" is also updated. *)
|
||||
commit_patches : unit -> unit;
|
||||
(* Update content (second arg) of the notes' file (first arg) if exists. *)
|
||||
commit_notes : string -> string -> unit;
|
||||
(* Close every opened channel. *)
|
||||
cleanup : unit -> unit;
|
||||
(* Returns real number of persons inside the base (without empty persons).
|
||||
Pending patches aren't considered. *)
|
||||
nb_of_real_persons : unit -> int;
|
||||
(* Tells if person with giving id exists in the base.
|
||||
Pending patches are also considered. *)
|
||||
iper_exists : int -> bool;
|
||||
(* Tells if family with giving id exists in the base.
|
||||
Pending patches are also considered. *)
|
||||
ifam_exists : int -> bool;
|
||||
}
|
||||
(** Functionality part of database. Every modification of the base is stored in {i patches} file.
|
||||
Note that, every modification firstly is pendent and should be commited
|
||||
to apply them and to update {i patches} file with [commit_patches]. *)
|
||||
|
||||
(** Geneweb database version *)
|
||||
type base_version = GnWb0020 | GnWb0021 | GnWb0022 | GnWb0023 | GnWb0024
|
||||
|
||||
type dsk_base = { data : base_data; func : base_func; version : base_version }
|
||||
(** Database representation: data and basic requests over this data. *)
|
||||
7
lib/gwdb-legacy/dune
Normal file
7
lib/gwdb-legacy/dune
Normal file
@@ -0,0 +1,7 @@
|
||||
(library
|
||||
(name gwdb_legacy)
|
||||
(public_name geneweb.gwdb-legacy)
|
||||
(implements geneweb.gwdb_driver)
|
||||
(libraries geneweb.def geneweb.util re unix)
|
||||
(modules_without_implementation dbdisk)
|
||||
(modules btree database dbdisk dutil gwdb_driver gwdb_gc iovalue outbase))
|
||||
76
lib/gwdb-legacy/dutil.ml
Normal file
76
lib/gwdb-legacy/dutil.ml
Normal file
@@ -0,0 +1,76 @@
|
||||
(* Copyright (c) 2006-2007 INRIA *)
|
||||
|
||||
open Dbdisk
|
||||
open Def
|
||||
|
||||
type name_index_data = int array array
|
||||
type strings_of_fsname = int array array
|
||||
|
||||
let magic_GnWb0020 = "GnWb0020"
|
||||
let magic_GnWb0021 = "GnWb0021"
|
||||
let magic_GnWb0022 = "GnWb0022"
|
||||
let magic_GnWb0023 = "GnWb0023"
|
||||
let magic_GnWb0024 = "GnWb0024"
|
||||
let table_size = 0x3fff
|
||||
let poi base i = base.data.persons.get i
|
||||
let aoi base i = base.data.ascends.get i
|
||||
let uoi base i = base.data.unions.get i
|
||||
let coi base i = base.data.couples.get i
|
||||
let sou base i = base.data.strings.get i
|
||||
let p_first_name base p = Mutil.nominative (sou base p.first_name)
|
||||
let p_surname base p = Mutil.nominative (sou base p.surname)
|
||||
|
||||
let husbands base p =
|
||||
Array.map
|
||||
(fun ifam ->
|
||||
let cpl = coi base ifam in
|
||||
let husband = poi base (Adef.father cpl) in
|
||||
let husband_surname = husband.surname in
|
||||
let husband_surnames_aliases = husband.surnames_aliases in
|
||||
(husband_surname, husband_surnames_aliases))
|
||||
(uoi base p.key_index).family
|
||||
|
||||
let father_titles_places base p nobtit =
|
||||
match (aoi base p.key_index).parents with
|
||||
| Some ifam ->
|
||||
let cpl = coi base ifam in
|
||||
let fath = poi base (Adef.father cpl) in
|
||||
nobtit fath
|
||||
| None -> []
|
||||
|
||||
let dsk_person_misc_names base p nobtit =
|
||||
Futil.gen_person_misc_names (sou base) 0 1 p.first_name p.surname
|
||||
p.public_name p.qualifiers p.aliases p.first_names_aliases
|
||||
p.surnames_aliases (nobtit p)
|
||||
(if p.sex = Female then husbands base p else [||])
|
||||
(father_titles_places base p nobtit)
|
||||
|
||||
let compare_snames base_data s1 s2 =
|
||||
Mutil.compare_after_particle (Lazy.force base_data.particles) s1 s2
|
||||
|
||||
let compare_snames_i base_data is1 is2 =
|
||||
if is1 = is2 then 0
|
||||
else
|
||||
compare_snames base_data
|
||||
(base_data.strings.get is1)
|
||||
(base_data.strings.get is2)
|
||||
|
||||
let compare_fnames = String.compare
|
||||
|
||||
let compare_fnames_i base_data is1 is2 =
|
||||
if is1 = is2 then 0
|
||||
else compare_fnames (base_data.strings.get is1) (base_data.strings.get is2)
|
||||
|
||||
let int_size = 4
|
||||
|
||||
let output_value_no_sharing oc v =
|
||||
Marshal.to_channel oc v [ Marshal.No_sharing ]
|
||||
|
||||
module IntHT = Hashtbl.Make (struct
|
||||
type t = int
|
||||
|
||||
let equal = ( = )
|
||||
let hash x = x
|
||||
end)
|
||||
|
||||
let name_index s = Hashtbl.hash (Name.crush_lower s) mod table_size
|
||||
78
lib/gwdb-legacy/dutil.mli
Normal file
78
lib/gwdb-legacy/dutil.mli
Normal file
@@ -0,0 +1,78 @@
|
||||
(* Copyright (c) 2006-2007 INRIA *)
|
||||
|
||||
open Dbdisk
|
||||
|
||||
type name_index_data = int array array
|
||||
(** Index for all kind of mix between person's names (first index inside {i names.inx}) *)
|
||||
|
||||
type strings_of_fsname = int array array
|
||||
(** Index for sub-strings of person's surame and first name (second and third index respectively inside {i names.inx}) *)
|
||||
|
||||
val magic_GnWb0020 : string
|
||||
(** Header for the {i base} file (version 0020) *)
|
||||
|
||||
val magic_GnWb0021 : string
|
||||
(** Header for the {i base} file (version 0021) *)
|
||||
|
||||
val magic_GnWb0022 : string
|
||||
(** Header for the {i base} file (version 0022) *)
|
||||
|
||||
val magic_GnWb0023 : string
|
||||
(** Header for the {i base} file (version 0023) *)
|
||||
|
||||
val magic_GnWb0024 : string
|
||||
(** Header for the latest version of {i base} file *)
|
||||
|
||||
val table_size : int
|
||||
(** Maximal size of hash table for name indexation (inside {i names.inx}) *)
|
||||
|
||||
val compare_fnames_i : Dbdisk.base_data -> int -> int -> int
|
||||
(** [compare_fnames_i base i1 i2] compare two first names that have indexes [i1] and [i2] inside the [base]. *)
|
||||
|
||||
val compare_fnames : string -> string -> int
|
||||
(** [compare_fnames] compare two first names. *)
|
||||
|
||||
val compare_snames_i : Dbdisk.base_data -> int -> int -> int
|
||||
(** [compare_snames_i base i1 i2] compare two surnames that have indexes [i1] and [i2] inside the [base]. *)
|
||||
|
||||
val compare_snames : Dbdisk.base_data -> string -> string -> int
|
||||
(** [compare_snames_i base s1 s2] compare two surnames according to the principe specified by [Mutil.compare_after_particle]. *)
|
||||
|
||||
val dsk_person_misc_names :
|
||||
dsk_base -> dsk_person -> (dsk_person -> dsk_title list) -> string list
|
||||
(** [dsk_person_misc_names base p nobtit] computes various mix between all kind of names of a person's entry [p]
|
||||
from the database [base]. [nobtit] is used to return a title entries for passed in argument person. *)
|
||||
|
||||
val poi : dsk_base -> int -> dsk_person
|
||||
(** [poi base i] returns person's entry with index [i] from [base]. *)
|
||||
|
||||
val sou : dsk_base -> int -> string
|
||||
(** [poi base i] returns string with index [i] from [base]. *)
|
||||
|
||||
val p_first_name : dsk_base -> dsk_person -> string
|
||||
(** Returns person's first name from the given person's entry. *)
|
||||
|
||||
val p_surname : dsk_base -> dsk_person -> string
|
||||
(** Returns person's surname from the given person's entry. *)
|
||||
|
||||
val output_value_no_sharing : out_channel -> _ -> unit
|
||||
(** Output given value to the channel. Uses [Marshall.to_channel] with [No_sharing] flag. *)
|
||||
|
||||
val int_size : int
|
||||
(** Size of integer value inside the Geneweb's binary files *)
|
||||
|
||||
(** Hastable that has unhashed int as a key. *)
|
||||
module IntHT : sig
|
||||
include module type of Hashtbl.Make (struct
|
||||
type t = int
|
||||
|
||||
let equal = ( = )
|
||||
let hash x = x
|
||||
end)
|
||||
end
|
||||
|
||||
val name_index : string -> int
|
||||
(** [name_index s]
|
||||
Compute the index of crush_lowered version of s
|
||||
in an array of size [table_size].
|
||||
*)
|
||||
571
lib/gwdb-legacy/gwdb_driver.ml
Normal file
571
lib/gwdb-legacy/gwdb_driver.ml
Normal file
@@ -0,0 +1,571 @@
|
||||
(* Copyright (c) 1998-2007 INRIA *)
|
||||
|
||||
open Dbdisk
|
||||
|
||||
type istr = int
|
||||
type ifam = int
|
||||
type iper = int
|
||||
|
||||
let string_of_iper = string_of_int
|
||||
let string_of_ifam = string_of_int
|
||||
let string_of_istr = string_of_int
|
||||
let iper_of_string = int_of_string
|
||||
let ifam_of_string = int_of_string
|
||||
let istr_of_string = int_of_string
|
||||
let dummy_iper = -1
|
||||
let dummy_ifam = -1
|
||||
let empty_string = 0
|
||||
let quest_string = 1
|
||||
let eq_istr i1 i2 = i1 = i2
|
||||
let eq_ifam i1 i2 = i1 = i2
|
||||
let eq_iper i1 i2 = i1 = i2
|
||||
let is_empty_string istr = istr = 0
|
||||
let is_quest_string istr = istr = 1
|
||||
|
||||
type string_person_index = Dbdisk.string_person_index
|
||||
|
||||
let spi_find spi = spi.find
|
||||
let spi_first spi = spi.cursor
|
||||
let spi_next (spi : string_person_index) istr = spi.next istr
|
||||
|
||||
type base = dsk_base
|
||||
|
||||
let open_base bname : base = Database.opendb bname
|
||||
let sou base i = base.data.strings.get i
|
||||
let bname base = Filename.(remove_extension @@ basename base.data.bdir)
|
||||
let nb_of_persons base = base.data.persons.len
|
||||
let nb_of_real_persons base = base.func.nb_of_real_persons ()
|
||||
let nb_of_families base = base.data.families.len
|
||||
|
||||
let insert_string base s =
|
||||
base.func.Dbdisk.insert_string @@ Mutil.normalize_utf_8 s
|
||||
|
||||
let commit_patches base = base.func.Dbdisk.commit_patches ()
|
||||
let commit_notes base s = base.func.Dbdisk.commit_notes s
|
||||
let person_of_key base = base.func.Dbdisk.person_of_key
|
||||
let persons_of_name base = base.func.Dbdisk.persons_of_name
|
||||
let persons_of_first_name base = base.func.Dbdisk.persons_of_first_name
|
||||
let persons_of_surname base = base.func.Dbdisk.persons_of_surname
|
||||
let base_particles base = Lazy.force base.data.particles
|
||||
let base_strings_of_first_name base s = base.func.strings_of_fname s
|
||||
let base_strings_of_surname base s = base.func.strings_of_sname s
|
||||
let load_ascends_array base = base.data.ascends.load_array ()
|
||||
let load_unions_array base = base.data.unions.load_array ()
|
||||
let load_couples_array base = base.data.couples.load_array ()
|
||||
let load_descends_array base = base.data.descends.load_array ()
|
||||
let load_strings_array base = base.data.strings.load_array ()
|
||||
let load_persons_array base = base.data.persons.load_array ()
|
||||
let load_families_array base = base.data.families.load_array ()
|
||||
let clear_ascends_array base = base.data.ascends.clear_array ()
|
||||
let clear_unions_array base = base.data.unions.clear_array ()
|
||||
let clear_couples_array base = base.data.couples.clear_array ()
|
||||
let clear_descends_array base = base.data.descends.clear_array ()
|
||||
let clear_strings_array base = base.data.strings.clear_array ()
|
||||
let clear_persons_array base = base.data.persons.clear_array ()
|
||||
let clear_families_array base = base.data.families.clear_array ()
|
||||
|
||||
let close_base base =
|
||||
base.func.cleanup ();
|
||||
clear_ascends_array base;
|
||||
clear_unions_array base;
|
||||
clear_couples_array base;
|
||||
clear_descends_array base;
|
||||
clear_strings_array base;
|
||||
clear_persons_array base;
|
||||
clear_families_array base;
|
||||
()
|
||||
|
||||
let date_of_last_change base =
|
||||
let s =
|
||||
let bdir = base.data.bdir in
|
||||
try Unix.stat (Filename.concat bdir "patches")
|
||||
with Unix.Unix_error (_, _, _) -> Unix.stat (Filename.concat bdir "base")
|
||||
in
|
||||
s.Unix.st_mtime
|
||||
|
||||
let gen_gen_person_misc_names = Dutil.dsk_person_misc_names
|
||||
|
||||
let patch_misc_names base ip (p : (iper, iper, istr) Def.gen_person) =
|
||||
let p = { p with Def.key_index = ip } in
|
||||
List.iter
|
||||
(fun s -> base.func.Dbdisk.patch_name s ip)
|
||||
(gen_gen_person_misc_names base p (fun p -> p.Def.titles))
|
||||
|
||||
let patch_person base ip (p : (iper, iper, istr) Def.gen_person) =
|
||||
base.func.Dbdisk.patch_person ip p;
|
||||
let s = sou base p.first_name ^ " " ^ sou base p.surname in
|
||||
base.func.Dbdisk.patch_name s ip;
|
||||
patch_misc_names base ip p;
|
||||
Array.iter
|
||||
(fun i ->
|
||||
let cpl = base.data.couples.get i in
|
||||
let m = Adef.mother cpl in
|
||||
let f = Adef.father cpl in
|
||||
patch_misc_names base m (base.data.persons.get m);
|
||||
patch_misc_names base f (base.data.persons.get f);
|
||||
Array.iter
|
||||
(fun i -> patch_misc_names base i (base.data.persons.get i))
|
||||
(base.data.descends.get i).children)
|
||||
(base.data.unions.get ip).family
|
||||
|
||||
let patch_ascend base ip a = base.func.Dbdisk.patch_ascend ip a
|
||||
let patch_union base ip u = base.func.Dbdisk.patch_union ip u
|
||||
let patch_family base ifam f = base.func.Dbdisk.patch_family ifam f
|
||||
let patch_couple base ifam c = base.func.Dbdisk.patch_couple ifam c
|
||||
let patch_descend base ifam d = base.func.Dbdisk.patch_descend ifam d
|
||||
let insert_person = patch_person
|
||||
let insert_ascend = patch_ascend
|
||||
let insert_union = patch_union
|
||||
let insert_family = patch_family
|
||||
let insert_couple = patch_couple
|
||||
let insert_descend = patch_descend
|
||||
|
||||
let delete_person base ip =
|
||||
patch_person base ip
|
||||
{
|
||||
first_name = quest_string;
|
||||
surname = quest_string;
|
||||
occ = 0;
|
||||
image = empty_string;
|
||||
first_names_aliases = [];
|
||||
surnames_aliases = [];
|
||||
public_name = empty_string;
|
||||
qualifiers = [];
|
||||
titles = [];
|
||||
rparents = [];
|
||||
related = [];
|
||||
aliases = [];
|
||||
occupation = empty_string;
|
||||
sex = Neuter;
|
||||
access = Private;
|
||||
birth = Date.cdate_None;
|
||||
birth_place = empty_string;
|
||||
birth_note = empty_string;
|
||||
birth_src = empty_string;
|
||||
baptism = Date.cdate_None;
|
||||
baptism_place = empty_string;
|
||||
baptism_note = empty_string;
|
||||
baptism_src = empty_string;
|
||||
death = DontKnowIfDead;
|
||||
death_place = empty_string;
|
||||
death_note = empty_string;
|
||||
death_src = empty_string;
|
||||
burial = UnknownBurial;
|
||||
burial_place = empty_string;
|
||||
burial_note = empty_string;
|
||||
burial_src = empty_string;
|
||||
pevents = [];
|
||||
notes = empty_string;
|
||||
psources = empty_string;
|
||||
key_index = ip;
|
||||
}
|
||||
|
||||
let delete_ascend base ip =
|
||||
patch_ascend base ip { parents = None; consang = Adef.no_consang }
|
||||
|
||||
let delete_union base ip = patch_union base ip { family = [||] }
|
||||
|
||||
let delete_family base ifam =
|
||||
patch_family base ifam
|
||||
{
|
||||
marriage = Date.cdate_None;
|
||||
marriage_place = empty_string;
|
||||
marriage_note = empty_string;
|
||||
marriage_src = empty_string;
|
||||
relation = Married;
|
||||
divorce = NotDivorced;
|
||||
fevents = [];
|
||||
witnesses = [||];
|
||||
comment = empty_string;
|
||||
origin_file = empty_string;
|
||||
fsources = empty_string;
|
||||
fam_index = dummy_ifam;
|
||||
}
|
||||
|
||||
let delete_couple base ifam =
|
||||
patch_couple base ifam (Adef.couple dummy_iper dummy_iper)
|
||||
|
||||
let delete_descend base ifam = patch_descend base ifam { children = [||] }
|
||||
let new_iper base = base.data.persons.len
|
||||
let new_ifam base = base.data.families.len
|
||||
|
||||
(* FIXME: lock *)
|
||||
let sync ?(scratch = false) base =
|
||||
if base.data.perm = RDONLY && not scratch then
|
||||
raise Def.(HttpExn (Forbidden, __LOC__))
|
||||
else Outbase.output base
|
||||
|
||||
let make bname particles arrays : Dbdisk.dsk_base =
|
||||
sync ~scratch:true (Database.make bname particles arrays);
|
||||
open_base bname
|
||||
|
||||
let bfname base fname = Filename.concat base.data.bdir fname
|
||||
|
||||
module NLDB = struct
|
||||
let magic = "GWNL0010"
|
||||
|
||||
let read base =
|
||||
let fname = bfname base "notes_links" in
|
||||
match try Some (open_in_bin fname) with Sys_error _ -> None with
|
||||
| Some ic ->
|
||||
let r =
|
||||
if Mutil.check_magic magic ic then
|
||||
(input_value ic : (iper, iper) Def.NLDB.t)
|
||||
else failwith "unsupported nldb format"
|
||||
in
|
||||
close_in ic;
|
||||
r
|
||||
| None -> []
|
||||
|
||||
let write base db =
|
||||
if base.data.perm = RDONLY then raise Def.(HttpExn (Forbidden, __LOC__))
|
||||
else
|
||||
let fname_tmp = bfname base "1notes_links" in
|
||||
let fname_def = bfname base "notes_links" in
|
||||
let fname_back = bfname base "notes_links~" in
|
||||
let oc = open_out_bin fname_tmp in
|
||||
output_string oc magic;
|
||||
output_value oc (db : (iper, ifam) Def.NLDB.t);
|
||||
close_out oc;
|
||||
Mutil.rm fname_back;
|
||||
Mutil.mv fname_def fname_back;
|
||||
Sys.rename fname_tmp fname_def
|
||||
end
|
||||
|
||||
let read_nldb = NLDB.read
|
||||
let write_nldb = NLDB.write
|
||||
let base_notes_origin_file base = base.data.bnotes.Def.norigin_file
|
||||
let base_notes_dir _base = "notes_d"
|
||||
let base_wiznotes_dir _base = "wiznotes"
|
||||
|
||||
let base_notes_read_aux base fnotes mode =
|
||||
let fname =
|
||||
if fnotes = "" then "notes" else Filename.concat "notes_d" (fnotes ^ ".txt")
|
||||
in
|
||||
try
|
||||
let ic = Secure.open_in @@ Filename.concat base.data.bdir fname in
|
||||
let str =
|
||||
match mode with
|
||||
| Def.RnDeg -> if in_channel_length ic = 0 then "" else " "
|
||||
| Def.Rn1Ln -> ( try input_line ic with End_of_file -> "")
|
||||
| Def.RnAll -> Mutil.input_file_ic ic
|
||||
in
|
||||
close_in ic;
|
||||
str
|
||||
with Sys_error _ -> ""
|
||||
|
||||
let base_notes_read base fnotes = base_notes_read_aux base fnotes Def.RnAll
|
||||
|
||||
let base_notes_read_first_line base fnotes =
|
||||
base_notes_read_aux base fnotes Def.Rn1Ln
|
||||
|
||||
let base_notes_are_empty base fnotes =
|
||||
base_notes_read_aux base fnotes Def.RnDeg = ""
|
||||
|
||||
type relation = (iper, istr) Def.gen_relation
|
||||
type title = istr Def.gen_title
|
||||
type pers_event = (iper, istr) Def.gen_pers_event
|
||||
type fam_event = (iper, istr) Def.gen_fam_event
|
||||
|
||||
let cache f a get set x =
|
||||
match get x with
|
||||
| Some v -> v
|
||||
| None ->
|
||||
let v = f a in
|
||||
set x (Some v);
|
||||
v
|
||||
|
||||
(** Persons *)
|
||||
|
||||
type person = {
|
||||
base : base;
|
||||
iper : iper;
|
||||
mutable p : (iper, iper, istr) gen_person option;
|
||||
mutable a : ifam gen_ascend option;
|
||||
mutable u : ifam gen_union option;
|
||||
}
|
||||
|
||||
let cache_per f ({ base; iper; _ } as p) =
|
||||
f (cache base.data.persons.get iper (fun p -> p.p) (fun p v -> p.p <- v) p)
|
||||
|
||||
let cache_asc f ({ base; iper; _ } as p) =
|
||||
f (cache base.data.ascends.get iper (fun p -> p.a) (fun p v -> p.a <- v) p)
|
||||
|
||||
let cache_uni f ({ base; iper; _ } as p) =
|
||||
f (cache base.data.unions.get iper (fun p -> p.u) (fun p v -> p.u <- v) p)
|
||||
|
||||
let gen_person_of_person = cache_per (fun p -> p)
|
||||
let gen_ascend_of_person = cache_asc (fun p -> p)
|
||||
let gen_union_of_person = cache_uni (fun p -> p)
|
||||
let get_access = cache_per (fun p -> p.Def.access)
|
||||
let get_aliases = cache_per (fun p -> p.Def.aliases)
|
||||
let get_baptism = cache_per (fun p -> p.Def.baptism)
|
||||
let get_baptism_note = cache_per (fun p -> p.Def.baptism_note)
|
||||
let get_baptism_place = cache_per (fun p -> p.Def.baptism_place)
|
||||
let get_baptism_src = cache_per (fun p -> p.Def.baptism_src)
|
||||
let get_birth = cache_per (fun p -> p.Def.birth)
|
||||
let get_birth_note = cache_per (fun p -> p.Def.birth_note)
|
||||
let get_birth_place = cache_per (fun p -> p.Def.birth_place)
|
||||
let get_birth_src = cache_per (fun p -> p.Def.birth_src)
|
||||
let get_burial = cache_per (fun p -> p.Def.burial)
|
||||
let get_burial_note = cache_per (fun p -> p.Def.burial_note)
|
||||
let get_burial_place = cache_per (fun p -> p.Def.burial_place)
|
||||
let get_burial_src = cache_per (fun p -> p.Def.burial_src)
|
||||
let get_consang = cache_asc (fun a -> a.Def.consang)
|
||||
let get_death = cache_per (fun p -> p.Def.death)
|
||||
let get_death_note = cache_per (fun p -> p.Def.death_note)
|
||||
let get_death_place = cache_per (fun p -> p.Def.death_place)
|
||||
let get_death_src = cache_per (fun p -> p.Def.death_src)
|
||||
let get_family = cache_uni (fun u -> u.Def.family)
|
||||
let get_first_name = cache_per (fun p -> p.Def.first_name)
|
||||
let get_first_names_aliases = cache_per (fun p -> p.Def.first_names_aliases)
|
||||
let get_image = cache_per (fun p -> p.Def.image)
|
||||
let get_iper = cache_per (fun p -> p.Def.key_index)
|
||||
let get_notes = cache_per (fun p -> p.Def.notes)
|
||||
let get_occ = cache_per (fun p -> p.Def.occ)
|
||||
let get_occupation = cache_per (fun p -> p.Def.occupation)
|
||||
let get_parents = cache_asc (fun a -> a.Def.parents)
|
||||
let get_pevents = cache_per (fun p -> p.Def.pevents)
|
||||
let get_psources = cache_per (fun p -> p.Def.psources)
|
||||
let get_public_name = cache_per (fun p -> p.Def.public_name)
|
||||
let get_qualifiers = cache_per (fun p -> p.Def.qualifiers)
|
||||
let get_related = cache_per (fun p -> p.Def.related)
|
||||
let get_rparents = cache_per (fun p -> p.Def.rparents)
|
||||
let get_sex = cache_per (fun p -> p.Def.sex)
|
||||
let get_surname = cache_per (fun p -> p.Def.surname)
|
||||
let get_surnames_aliases = cache_per (fun p -> p.Def.surnames_aliases)
|
||||
let get_titles = cache_per (fun p -> p.Def.titles)
|
||||
|
||||
(** Families *)
|
||||
|
||||
type family = {
|
||||
base : base;
|
||||
ifam : ifam;
|
||||
mutable f : (iper, ifam, istr) gen_family option;
|
||||
mutable c : iper gen_couple option;
|
||||
mutable d : iper gen_descend option;
|
||||
}
|
||||
|
||||
let cache_fam f ({ base; ifam; _ } as fam) =
|
||||
f (cache base.data.families.get ifam (fun f -> f.f) (fun f v -> f.f <- v) fam)
|
||||
|
||||
let cache_cpl f ({ base; ifam; _ } as fam) =
|
||||
f (cache base.data.couples.get ifam (fun f -> f.c) (fun f v -> f.c <- v) fam)
|
||||
|
||||
let cache_des f ({ base; ifam; _ } as fam) =
|
||||
f (cache base.data.descends.get ifam (fun f -> f.d) (fun f v -> f.d <- v) fam)
|
||||
|
||||
let gen_couple_of_family = cache_cpl (fun c -> c)
|
||||
let gen_descend_of_family = cache_des (fun d -> d)
|
||||
let gen_family_of_family = cache_fam (fun f -> f)
|
||||
let get_children = cache_des (fun d -> d.Def.children)
|
||||
let get_comment = cache_fam (fun f -> f.Def.comment)
|
||||
let get_ifam = cache_fam (fun f -> f.Def.fam_index)
|
||||
let get_divorce = cache_fam (fun f -> f.Def.divorce)
|
||||
let get_father = cache_cpl (fun c -> Adef.father c)
|
||||
let get_fevents = cache_fam (fun f -> f.Def.fevents)
|
||||
let get_fsources = cache_fam (fun f -> f.Def.fsources)
|
||||
let get_marriage = cache_fam (fun f -> f.Def.marriage)
|
||||
let get_marriage_note = cache_fam (fun f -> f.Def.marriage_note)
|
||||
let get_marriage_place = cache_fam (fun f -> f.Def.marriage_place)
|
||||
let get_marriage_src = cache_fam (fun f -> f.Def.marriage_src)
|
||||
let get_mother = cache_cpl (fun c -> Adef.mother c)
|
||||
let get_origin_file = cache_fam (fun f -> f.Def.origin_file)
|
||||
let get_parent_array = cache_cpl (fun c -> Adef.parent_array c)
|
||||
let get_relation = cache_fam (fun f -> f.Def.relation)
|
||||
let get_witnesses = cache_fam (fun f -> f.Def.witnesses)
|
||||
|
||||
let no_person ip =
|
||||
{ (Mutil.empty_person empty_string empty_string) with key_index = ip }
|
||||
|
||||
let no_ascend = { parents = None; consang = Adef.no_consang }
|
||||
let no_union = { family = [||] }
|
||||
|
||||
let empty_person base iper =
|
||||
{
|
||||
base;
|
||||
iper;
|
||||
p = Some (no_person iper);
|
||||
a = Some no_ascend;
|
||||
u = Some no_union;
|
||||
}
|
||||
[@ocaml.warning "-42"]
|
||||
|
||||
let person_of_gen_person base (p, a, u) =
|
||||
{ base; iper = p.key_index; p = Some p; a = Some a; u = Some u }
|
||||
[@ocaml.warning "-42"]
|
||||
|
||||
let family_of_gen_family base (f, c, d) =
|
||||
{ base; ifam = f.fam_index; f = Some f; c = Some c; d = Some d }
|
||||
[@ocaml.warning "-42"]
|
||||
|
||||
let iper_exists base = base.func.iper_exists
|
||||
let ifam_exists base = base.func.ifam_exists
|
||||
|
||||
let poi base iper =
|
||||
if iper = dummy_iper then empty_person base iper
|
||||
else { base; iper; p = None; a = None; u = None } [@ocaml.warning "-42"]
|
||||
|
||||
let no_family ifam = { (Mutil.empty_family empty_string) with fam_index = ifam }
|
||||
let no_couple = Adef.couple dummy_iper dummy_iper
|
||||
let no_descend = { Def.children = [||] }
|
||||
|
||||
let empty_family base ifam =
|
||||
{
|
||||
base;
|
||||
ifam;
|
||||
f = Some (no_family ifam);
|
||||
c = Some no_couple;
|
||||
d = Some no_descend;
|
||||
}
|
||||
|
||||
let foi base ifam =
|
||||
if ifam = dummy_ifam then empty_family base ifam
|
||||
else { base; ifam; f = None; c = None; d = None }
|
||||
|
||||
module Collection = struct
|
||||
type 'a t = { length : int; get : int -> 'a option }
|
||||
|
||||
let map (fn : 'a -> 'b) c =
|
||||
{
|
||||
length = c.length;
|
||||
get = (fun i -> match c.get i with Some x -> Some (fn x) | None -> None);
|
||||
}
|
||||
|
||||
let length { length; _ } = length
|
||||
|
||||
let iter fn { get; length } =
|
||||
for i = 0 to length - 1 do
|
||||
match get i with Some x -> fn x | None -> ()
|
||||
done
|
||||
|
||||
let iteri fn { get; length } =
|
||||
for i = 0 to length - 1 do
|
||||
match get i with Some x -> fn i x | None -> ()
|
||||
done
|
||||
|
||||
let fold ?from ?until fn acc { get; length } =
|
||||
let from = match from with Some x -> x | None -> 0 in
|
||||
let until = match until with Some x -> x + 1 | None -> length in
|
||||
let rec loop acc i =
|
||||
if i = until then acc
|
||||
else loop (match get i with Some x -> fn acc x | None -> acc) (i + 1)
|
||||
in
|
||||
loop acc from
|
||||
|
||||
let fold_until continue fn acc { get; length } =
|
||||
let rec loop acc i =
|
||||
if (not (continue acc)) || i = length then acc
|
||||
else loop (match get i with Some x -> fn acc x | None -> acc) (i + 1)
|
||||
in
|
||||
loop acc 0
|
||||
|
||||
let iterator { get; length } =
|
||||
let cursor = ref 0 in
|
||||
let rec next () =
|
||||
if !cursor < length then (
|
||||
match get !cursor with
|
||||
| None ->
|
||||
incr cursor;
|
||||
next ()
|
||||
| v ->
|
||||
incr cursor;
|
||||
v)
|
||||
else None
|
||||
in
|
||||
next
|
||||
end
|
||||
|
||||
module Marker = struct
|
||||
type ('k, 'v) t = { get : 'k -> 'v; set : 'k -> 'v -> unit }
|
||||
|
||||
let make (k : 'a -> int) (c : 'a Collection.t) (i : 'v) : ('a, 'v) t =
|
||||
let a = Array.make c.Collection.length i in
|
||||
{
|
||||
get = (fun x -> Array.get a (k x));
|
||||
set = (fun x v -> Array.set a (k x) v);
|
||||
}
|
||||
|
||||
let get ({ get; _ } : _ t) k = get k
|
||||
let set ({ set; _ } : _ t) k = set k
|
||||
end
|
||||
|
||||
let persons base =
|
||||
{ Collection.length = nb_of_persons base; get = (fun i -> Some (poi base i)) }
|
||||
|
||||
let ipers base =
|
||||
{ Collection.length = nb_of_persons base; get = (fun i -> Some i) }
|
||||
|
||||
let iper_marker c i = Marker.make (fun i -> i) c i
|
||||
|
||||
let ifams ?(select = fun _ -> true) base =
|
||||
{
|
||||
Collection.length = nb_of_families base;
|
||||
get =
|
||||
(fun i ->
|
||||
if select i then
|
||||
if get_ifam (foi base i) = dummy_ifam then None else Some i
|
||||
else None);
|
||||
}
|
||||
|
||||
let families ?(select = fun _ -> true) base =
|
||||
{
|
||||
Collection.length = nb_of_families base;
|
||||
get =
|
||||
(fun i ->
|
||||
let f = foi base i in
|
||||
if get_ifam f <> dummy_ifam && select f then Some f else None);
|
||||
}
|
||||
|
||||
let dummy_collection _ = { Collection.length = -1; get = (fun _ -> None) }
|
||||
let ifam_marker c i = Marker.make (fun i -> i) c i
|
||||
|
||||
let dummy_marker (_ : 'a) (v : 'b) : ('a, 'b) Marker.t =
|
||||
{ Marker.get = (fun _ -> v); set = (fun _ _ -> ()) }
|
||||
|
||||
(* Restrict file *)
|
||||
|
||||
(* FIXME: these values should not be global *)
|
||||
let visible_ref : (iper, bool) Hashtbl.t option ref = ref None
|
||||
|
||||
let read_or_create_visible base =
|
||||
let fname = Filename.concat base.data.bdir "restrict" in
|
||||
let visible =
|
||||
if Sys.file_exists fname then (
|
||||
let ic = Secure.open_in fname in
|
||||
let visible =
|
||||
if Mutil.check_magic Mutil.executable_magic ic then input_value ic
|
||||
else Hashtbl.create (nb_of_persons base)
|
||||
in
|
||||
close_in ic;
|
||||
visible)
|
||||
else Hashtbl.create (nb_of_persons base)
|
||||
in
|
||||
visible_ref := Some visible;
|
||||
visible
|
||||
|
||||
let base_visible_write base =
|
||||
if base.data.perm = RDONLY then raise Def.(HttpExn (Forbidden, __LOC__))
|
||||
else
|
||||
let fname = Filename.concat base.data.bdir "restrict" in
|
||||
match !visible_ref with
|
||||
| Some visible ->
|
||||
let oc = Secure.open_out fname in
|
||||
output_string oc Mutil.executable_magic;
|
||||
output_value oc visible;
|
||||
close_out oc
|
||||
| None -> ()
|
||||
|
||||
let base_visible_get base fct i =
|
||||
let visible =
|
||||
match !visible_ref with
|
||||
| Some visible -> visible
|
||||
| None -> read_or_create_visible base
|
||||
in
|
||||
match Hashtbl.find_opt visible i with
|
||||
| None ->
|
||||
let status = fct (poi base i) in
|
||||
Hashtbl.add visible i status;
|
||||
visible_ref := Some visible;
|
||||
status
|
||||
| Some b -> b
|
||||
187
lib/gwdb-legacy/gwdb_gc.ml
Normal file
187
lib/gwdb-legacy/gwdb_gc.ml
Normal file
@@ -0,0 +1,187 @@
|
||||
open Dbdisk
|
||||
|
||||
(* copied from Gwdb_driver *)
|
||||
let dummy_ifam = -1
|
||||
let empty_string = 0
|
||||
let quest_string = 1
|
||||
|
||||
let empty_person p =
|
||||
(p.first_name = empty_string || p.first_name = quest_string)
|
||||
&& (p.surname = empty_string || p.surname = quest_string)
|
||||
(* && p.occ = 0 *)
|
||||
&& p.image = empty_string
|
||||
&& p.first_names_aliases = [] && p.surnames_aliases = []
|
||||
&& p.public_name = empty_string
|
||||
&& p.qualifiers = [] && p.titles = [] && p.rparents = [] && p.related = []
|
||||
&& p.aliases = []
|
||||
&& p.occupation = empty_string
|
||||
&& p.sex = Neuter
|
||||
(* && p.access = Private *)
|
||||
&& p.birth = Date.cdate_None
|
||||
&& p.birth_place = empty_string
|
||||
&& p.birth_note = empty_string
|
||||
&& p.birth_src = empty_string
|
||||
&& p.baptism = Date.cdate_None
|
||||
&& p.baptism_place = empty_string
|
||||
&& p.baptism_note = empty_string
|
||||
&& p.baptism_src = empty_string
|
||||
&& p.death = DontKnowIfDead
|
||||
&& p.death_place = empty_string
|
||||
&& p.death_note = empty_string
|
||||
&& p.death_src = empty_string && p.burial = UnknownBurial
|
||||
&& p.burial_place = empty_string
|
||||
&& p.burial_note = empty_string
|
||||
&& p.burial_src = empty_string
|
||||
&& p.pevents = [] && p.notes = empty_string && p.psources = empty_string
|
||||
|
||||
let gc ?(dry_run = true) base =
|
||||
base.data.persons.load_array ();
|
||||
base.data.ascends.load_array ();
|
||||
base.data.unions.load_array ();
|
||||
base.data.families.load_array ();
|
||||
base.data.couples.load_array ();
|
||||
base.data.descends.load_array ();
|
||||
base.data.strings.load_array ();
|
||||
let mp = Array.make base.data.persons.len false in
|
||||
let mf = Array.make base.data.families.len false in
|
||||
let ms = Array.make base.data.strings.len false in
|
||||
let markp i = Array.set mp i true in
|
||||
let markf i = Array.set mf i true in
|
||||
let marks i = Array.set ms i true in
|
||||
marks 0;
|
||||
marks 1;
|
||||
for i = 0 to base.data.persons.len - 1 do
|
||||
let p = base.data.persons.get i in
|
||||
if not (empty_person p) then (
|
||||
markp i;
|
||||
let _ = Futil.map_person_ps markp marks p in
|
||||
let _ = Futil.map_union_f markf @@ base.data.unions.get i in
|
||||
let _ = Futil.map_ascend_f markf @@ base.data.ascends.get i in
|
||||
())
|
||||
done;
|
||||
for i = 0 to base.data.families.len - 1 do
|
||||
if Array.get mf i then
|
||||
let f = base.data.families.get i in
|
||||
(* if family wasn't deleted *)
|
||||
if f.fam_index <> dummy_ifam then
|
||||
let _ = Futil.map_family_ps markp markf marks f in
|
||||
let _ = Futil.map_couple_p false markp @@ base.data.couples.get i in
|
||||
let _ = Futil.map_descend_p markp @@ base.data.descends.get i in
|
||||
()
|
||||
done;
|
||||
(* [p1;p2:p3;p4] [true;false;true;false] -> [0;0;1;1] *)
|
||||
let dst_i src m =
|
||||
let off = ref 0 in
|
||||
Array.init src.len (fun i ->
|
||||
if Array.get m i then i - !off
|
||||
else (
|
||||
incr off;
|
||||
i - !off))
|
||||
in
|
||||
(* 2 [true;false;true;false] -> [0;2] *)
|
||||
let src_i len m =
|
||||
let off = ref 0 in
|
||||
let a = Array.make len (-1) in
|
||||
let rec loop i =
|
||||
if i = len then ()
|
||||
else if Array.get m (i + !off) then (
|
||||
Array.set a i (i + !off);
|
||||
loop (i + 1))
|
||||
else (
|
||||
incr off;
|
||||
loop i)
|
||||
in
|
||||
loop 0;
|
||||
a
|
||||
in
|
||||
let aux arr =
|
||||
let rec loop i (sum, acc) =
|
||||
if i < 0 then (sum, acc)
|
||||
else if Array.get arr i then loop (pred i) (succ sum, acc)
|
||||
else loop (pred i) (sum, i :: acc)
|
||||
in
|
||||
loop (Array.length arr - 1) (0, [])
|
||||
in
|
||||
let lenp, deletedp = aux mp in
|
||||
let lenf, deletedf = aux mf in
|
||||
let lens, deleteds = aux ms in
|
||||
if dry_run then (deletedp, deletedf, deleteds)
|
||||
else
|
||||
let dst_ipers = dst_i base.data.persons mp in
|
||||
let dst_ifams = dst_i base.data.families mf in
|
||||
let dst_istrs = dst_i base.data.strings ms in
|
||||
let dst_iper = Array.get dst_ipers in
|
||||
let dst_ifam = Array.get dst_ifams in
|
||||
let dst_istr = Array.get dst_istrs in
|
||||
let src_ipers = src_i lenp mp in
|
||||
let src_ifams = src_i lenf mf in
|
||||
let src_istrs = src_i lens ms in
|
||||
let src_iper = Array.get src_ipers in
|
||||
let src_ifam = Array.get src_ifams in
|
||||
let src_istr = Array.get src_istrs in
|
||||
let persons =
|
||||
Array.init lenp @@ fun i ->
|
||||
{
|
||||
(Futil.map_person_ps dst_iper dst_istr
|
||||
@@ base.data.persons.get @@ src_iper i)
|
||||
with
|
||||
key_index = i;
|
||||
}
|
||||
in
|
||||
let ascends =
|
||||
Array.init lenp @@ fun i ->
|
||||
Futil.map_ascend_f dst_ifam @@ base.data.ascends.get @@ src_iper i
|
||||
in
|
||||
let unions =
|
||||
Array.init lenp @@ fun i ->
|
||||
Futil.map_union_f dst_ifam @@ base.data.unions.get @@ src_iper i
|
||||
in
|
||||
let families =
|
||||
Array.init lenf @@ fun i ->
|
||||
Futil.map_family_ps dst_iper (fun _ -> i) dst_istr
|
||||
@@ base.data.families.get @@ src_ifam i
|
||||
in
|
||||
let couples =
|
||||
Array.init lenf @@ fun i ->
|
||||
Futil.map_couple_p false dst_iper @@ base.data.couples.get @@ src_ifam i
|
||||
in
|
||||
let descends =
|
||||
Array.init lenf @@ fun i ->
|
||||
Futil.map_descend_p dst_iper @@ base.data.descends.get @@ src_ifam i
|
||||
in
|
||||
let strings =
|
||||
Array.init lens (fun i -> base.data.strings.get @@ src_istr i)
|
||||
in
|
||||
let bnotes = base.data.bnotes in
|
||||
let particles = base.data.particles_txt in
|
||||
let bname = base.data.bdir in
|
||||
base.data.persons.clear_array ();
|
||||
base.data.ascends.clear_array ();
|
||||
base.data.unions.clear_array ();
|
||||
base.data.families.clear_array ();
|
||||
base.data.couples.clear_array ();
|
||||
base.data.descends.clear_array ();
|
||||
base.data.strings.clear_array ();
|
||||
let base' =
|
||||
Database.make bname particles
|
||||
( (persons, ascends, unions),
|
||||
(families, couples, descends),
|
||||
strings,
|
||||
bnotes )
|
||||
in
|
||||
base'.data.persons.load_array ();
|
||||
base'.data.ascends.load_array ();
|
||||
base'.data.unions.load_array ();
|
||||
base'.data.families.load_array ();
|
||||
base'.data.couples.load_array ();
|
||||
base'.data.descends.load_array ();
|
||||
base'.data.strings.load_array ();
|
||||
Outbase.output base';
|
||||
base'.data.persons.clear_array ();
|
||||
base'.data.ascends.clear_array ();
|
||||
base'.data.unions.clear_array ();
|
||||
base'.data.families.clear_array ();
|
||||
base'.data.couples.clear_array ();
|
||||
base'.data.descends.clear_array ();
|
||||
base'.data.strings.clear_array ();
|
||||
(deletedp, deletedf, deleteds)
|
||||
7
lib/gwdb-legacy/gwdb_gc.mli
Normal file
7
lib/gwdb-legacy/gwdb_gc.mli
Normal file
@@ -0,0 +1,7 @@
|
||||
val gc : ?dry_run:bool -> Dbdisk.dsk_base -> int list * int list * int list
|
||||
(** [gc ~dry_run base] launch garbage collector over that analyse database [base] that detects
|
||||
all deleted or empty elements that aren't referenced by anyone (unmarked element). If [dry_run]
|
||||
is unset then performs memory compacting by eliminating all unmarked elements from all database
|
||||
arrays and update corresponding database on the disk. Otherwise, it just perform computing stage
|
||||
without database update. Returns [(deletedp,deletedf,deleteds)] where [deletedp] is ids of all unmarked
|
||||
persons, [deletedf] is ids of all unmarked families and [deleteds] is ids of all unmarked strings *)
|
||||
211
lib/gwdb-legacy/iovalue.ml
Normal file
211
lib/gwdb-legacy/iovalue.ml
Normal file
@@ -0,0 +1,211 @@
|
||||
(* $Id: iovalue.ml,v 5.15 2012-01-27 16:27:46 ddr Exp $ *)
|
||||
(* Copyright (c) 1998-2007 INRIA *)
|
||||
|
||||
(* Input:
|
||||
read inside a value output by output_value (no headers) must
|
||||
match OCaml's input_value system (intern.c) *)
|
||||
|
||||
let sizeof_long = 4
|
||||
let sign_extend_shift = (((Sys.word_size / 8) - 1) * 8) - 1
|
||||
let sign_extend x = (x lsl sign_extend_shift) asr sign_extend_shift
|
||||
let prefix_SMALL_BLOCK = 0x80
|
||||
let prefix_SMALL_INT = 0x40
|
||||
let prefix_SMALL_STRING = 0x20
|
||||
let code_INT8 = 0x0
|
||||
let code_INT16 = 0x1
|
||||
let code_INT32 = 0x2
|
||||
let code_INT64 = 0x3
|
||||
let code_BLOCK32 = 0x8
|
||||
let code_BLOCK64 = 0x13
|
||||
let code_STRING8 = 0x9
|
||||
let code_STRING32 = 0xA
|
||||
|
||||
type 'a in_funs = {
|
||||
input_byte : 'a -> int;
|
||||
input_binary_int : 'a -> int;
|
||||
input : 'a -> bytes -> int -> int -> unit;
|
||||
}
|
||||
|
||||
let input_binary_int64 ifuns ic =
|
||||
let rec loop cnt n =
|
||||
if cnt = 0 then n else loop (cnt - 1) ((n lsl 8) + ifuns.input_byte ic)
|
||||
in
|
||||
loop 8 0
|
||||
|
||||
let rec input_loop ifuns ic =
|
||||
let code = ifuns.input_byte ic in
|
||||
if code >= prefix_SMALL_INT then
|
||||
if code >= prefix_SMALL_BLOCK then
|
||||
input_block ifuns ic (code land 0xf) ((code lsr 4) land 0x7)
|
||||
else Obj.magic (code land 0x3f)
|
||||
else if code >= prefix_SMALL_STRING then (
|
||||
let len = code land 0x1F in
|
||||
let s = Bytes.create len in
|
||||
ifuns.input ic s 0 len;
|
||||
Obj.magic s)
|
||||
else if code = code_INT8 then Obj.magic (sign_extend (ifuns.input_byte ic))
|
||||
else if code = code_INT16 then
|
||||
let h = ifuns.input_byte ic in
|
||||
Obj.magic ((sign_extend h lsl 8) + ifuns.input_byte ic)
|
||||
else if code = code_INT32 then
|
||||
let x1 = ifuns.input_byte ic in
|
||||
let x2 = ifuns.input_byte ic in
|
||||
let x3 = ifuns.input_byte ic in
|
||||
let x4 = ifuns.input_byte ic in
|
||||
Obj.magic ((sign_extend x1 lsl 24) + (x2 lsl 16) + (x3 lsl 8) + x4)
|
||||
else if code = code_INT64 then
|
||||
let () = assert (Sys.word_size = 64) in
|
||||
Obj.magic (input_binary_int64 ifuns ic)
|
||||
else if code = code_BLOCK32 then
|
||||
let header = ifuns.input_binary_int ic in
|
||||
Obj.magic (input_block ifuns ic (header land 0xff) (header lsr 10))
|
||||
else if code = code_BLOCK64 then
|
||||
if Sys.word_size = 64 then
|
||||
let header = input_binary_int64 ifuns ic in
|
||||
Obj.magic (input_block ifuns ic (header land 0xff) (header lsr 10))
|
||||
else failwith "input bad code block 64"
|
||||
else if code = code_STRING8 then (
|
||||
let len = ifuns.input_byte ic in
|
||||
let s = Bytes.create len in
|
||||
ifuns.input ic s 0 len;
|
||||
Obj.magic s)
|
||||
else if code = code_STRING32 then (
|
||||
let len = ifuns.input_binary_int ic in
|
||||
let s = Bytes.create len in
|
||||
ifuns.input ic s 0 len;
|
||||
Obj.magic s)
|
||||
else failwith (Printf.sprintf "input bad code 0x%x" code)
|
||||
|
||||
and input_block ifuns ic tag size =
|
||||
let v =
|
||||
if tag = 0 then Obj.magic (Array.make size (Obj.magic 0))
|
||||
else Obj.new_block tag size
|
||||
in
|
||||
for i = 0 to size - 1 do
|
||||
let x = input_loop ifuns ic in
|
||||
Obj.set_field v i (Obj.magic x)
|
||||
done;
|
||||
v
|
||||
|
||||
let in_channel_funs = { input_byte; input_binary_int; input = really_input }
|
||||
let input ic = Obj.magic (input_loop in_channel_funs ic)
|
||||
|
||||
(* Output *)
|
||||
|
||||
type 'a out_funs = {
|
||||
output_byte : 'a -> int -> unit;
|
||||
output_binary_int : 'a -> int -> unit;
|
||||
output : 'a -> string -> int -> int -> unit;
|
||||
}
|
||||
|
||||
let size_32 = ref 0
|
||||
let size_64 = ref 0
|
||||
|
||||
let output_binary_int64 ofuns oc x =
|
||||
for i = 1 to 8 do
|
||||
ofuns.output_byte oc ((x lsr (64 - (8 * i))) land 0xFF)
|
||||
done
|
||||
|
||||
let gen_output_block_header ofuns oc tag size =
|
||||
let hd = (size lsl 10) + tag in
|
||||
if tag < 16 && size < 8 then
|
||||
ofuns.output_byte oc (prefix_SMALL_BLOCK + tag + (size lsl 4))
|
||||
else if Sys.word_size = 64 && hd >= 1 lsl 32 then (
|
||||
ofuns.output_byte oc code_BLOCK64;
|
||||
output_binary_int64 ofuns oc hd)
|
||||
else (
|
||||
ofuns.output_byte oc code_BLOCK32;
|
||||
(* hd = size << 10 + tag *)
|
||||
ofuns.output_byte oc ((size lsr 14) land 0xFF);
|
||||
ofuns.output_byte oc ((size lsr 6) land 0xFF);
|
||||
ofuns.output_byte oc ((size lsl 2) land 0xFF);
|
||||
ofuns.output_byte oc (((size lsl 10) land 0xFF) + tag));
|
||||
if size = 0 then ()
|
||||
else (
|
||||
size_32 := !size_32 + 1 + size;
|
||||
size_64 := !size_64 + 1 + size)
|
||||
|
||||
let rec output_loop ofuns oc x =
|
||||
if Obj.is_int x then
|
||||
if Obj.magic x >= 0 && Obj.magic x < 0x40 then
|
||||
ofuns.output_byte oc (prefix_SMALL_INT + Obj.magic x)
|
||||
else if Obj.magic x >= -128 && Obj.magic x < 128 then (
|
||||
ofuns.output_byte oc code_INT8;
|
||||
ofuns.output_byte oc (Obj.magic x))
|
||||
else if Obj.magic x >= -32768 && Obj.magic x <= 32767 then (
|
||||
ofuns.output_byte oc code_INT16;
|
||||
ofuns.output_byte oc (Obj.magic x lsr 8);
|
||||
ofuns.output_byte oc (Obj.magic x))
|
||||
else if Obj.magic x >= -1073741824 && Obj.magic x <= 1073741823 then (
|
||||
ofuns.output_byte oc code_INT32;
|
||||
ofuns.output_binary_int oc (Obj.magic x))
|
||||
else (
|
||||
ofuns.output_byte oc code_INT64;
|
||||
output_binary_int64 ofuns oc (Obj.magic x))
|
||||
else if Obj.tag x = Obj.string_tag then (
|
||||
let len = String.length (Obj.magic x) in
|
||||
if len < 0x20 then ofuns.output_byte oc (prefix_SMALL_STRING + len)
|
||||
else if len < 0x100 then (
|
||||
ofuns.output_byte oc code_STRING8;
|
||||
ofuns.output_byte oc len)
|
||||
else (
|
||||
ofuns.output_byte oc code_STRING32;
|
||||
ofuns.output_binary_int oc len);
|
||||
ofuns.output oc (Obj.magic x) 0 len;
|
||||
size_32 := !size_32 + 1 + ((len + 4) / 4);
|
||||
size_64 := !size_64 + 1 + ((len + 8) / 8))
|
||||
else if Obj.tag x = Obj.double_tag || Obj.tag x = Obj.double_array_tag then
|
||||
failwith "Iovalue.output: floats not implemented"
|
||||
else if Obj.tag x = Obj.closure_tag then failwith "Iovalue.output <fun>"
|
||||
else if Obj.tag x = Obj.abstract_tag then failwith "Iovalue.output <abstract>"
|
||||
else if Obj.tag x = Obj.infix_tag then failwith "Iovalue.output: <infix>"
|
||||
else if Obj.tag x = Obj.custom_tag then failwith "Iovalue.output: <custom>"
|
||||
else if Obj.tag x = Obj.out_of_heap_tag then
|
||||
failwith "Iovalue.output: abstract value (outside heap)"
|
||||
else (
|
||||
gen_output_block_header ofuns oc (Obj.tag x) (Obj.size x);
|
||||
(* last case of "for" separated, to make more tail recursive cases
|
||||
when last field is itself, to prevent some stacks overflows *)
|
||||
if Obj.size x > 0 then (
|
||||
for i = 0 to Obj.size x - 2 do
|
||||
output_loop ofuns oc (Obj.field x i)
|
||||
done;
|
||||
output_loop ofuns oc (Obj.field x (Obj.size x - 1))))
|
||||
|
||||
let out_channel_funs =
|
||||
{ output_byte; output_binary_int; output = output_substring }
|
||||
|
||||
let output oc x = output_loop out_channel_funs oc (Obj.repr x)
|
||||
let gen_output ofuns i x = output_loop ofuns i (Obj.repr x)
|
||||
|
||||
(* Size *)
|
||||
|
||||
let size_funs =
|
||||
{
|
||||
output_byte = (fun r _ -> incr r);
|
||||
output_binary_int = (fun r _ -> r := !r + 4);
|
||||
output = (fun r _ beg len -> r := !r + len - beg);
|
||||
}
|
||||
|
||||
let size = ref 0
|
||||
|
||||
let size v =
|
||||
size := 0;
|
||||
gen_output size_funs size v;
|
||||
!size
|
||||
|
||||
let output_value_header_size = 20
|
||||
|
||||
let array_header_size arr_len =
|
||||
if arr_len < 8 then 1
|
||||
else if Sys.word_size = 64 && arr_len lsl 10 >= 1 lsl 32 then 9
|
||||
else 5
|
||||
|
||||
let output_array_access oc arr_get arr_len pos =
|
||||
let rec loop pos i =
|
||||
if i = arr_len then pos
|
||||
else (
|
||||
output_binary_int oc pos;
|
||||
loop (pos + size (arr_get i)) (i + 1))
|
||||
in
|
||||
loop (pos + output_value_header_size + array_header_size arr_len) 0
|
||||
18
lib/gwdb-legacy/iovalue.mli
Normal file
18
lib/gwdb-legacy/iovalue.mli
Normal file
@@ -0,0 +1,18 @@
|
||||
(* $Id: iovalue.mli,v 5.5 2012-01-27 08:53:53 ddr Exp $ *)
|
||||
(* Copyright (c) 1998-2007 INRIA *)
|
||||
|
||||
val sizeof_long : int
|
||||
(** Size of long integer value inside the Geneweb's binary files *)
|
||||
|
||||
val input : in_channel -> 'a
|
||||
(** Input a value from the giving channel. Identical to [Marshal.from_channel]. *)
|
||||
|
||||
val output : out_channel -> 'a -> unit
|
||||
(** Output a value to the giving channel. Identical to [Marshal.to_channel] with [No_sharing] flag. *)
|
||||
|
||||
val output_array_access : out_channel -> (int -> 'a) -> int -> int -> int
|
||||
(** [output_array_acces oc getf arr_get arr_len pos] prints to the channel
|
||||
[oc] position for each element (that could be obtained with [arr_get])
|
||||
in the binary file where marshalled array is stored. Array should be
|
||||
of length [arr_len] and should start at the position [pos] inside the
|
||||
binary file. Returns a position just after the end of array. *)
|
||||
390
lib/gwdb-legacy/outbase.ml
Normal file
390
lib/gwdb-legacy/outbase.ml
Normal file
@@ -0,0 +1,390 @@
|
||||
(* Copyright (c) 2006-2007 INRIA *)
|
||||
|
||||
open Dbdisk
|
||||
|
||||
let load_ascends_array base = base.data.ascends.load_array ()
|
||||
let load_unions_array base = base.data.unions.load_array ()
|
||||
let load_couples_array base = base.data.couples.load_array ()
|
||||
let load_descends_array base = base.data.descends.load_array ()
|
||||
let load_strings_array base = base.data.strings.load_array ()
|
||||
let close_base base = base.func.cleanup ()
|
||||
let save_mem = ref false
|
||||
let verbose = Mutil.verbose
|
||||
|
||||
let trace s =
|
||||
if !verbose then (
|
||||
Printf.eprintf "*** %s\n" s;
|
||||
flush stderr)
|
||||
|
||||
let count_error computed found =
|
||||
Printf.eprintf "Count error. Computed %d. Found %d.\n" computed found;
|
||||
flush stderr;
|
||||
exit 2
|
||||
|
||||
let output_index_aux oc_inx oc_inx_acc ni =
|
||||
let bpos = pos_out oc_inx in
|
||||
(* output name index (circular hash table) in the "names.inx" and position for hashed value in the "names.acc" *)
|
||||
Dutil.output_value_no_sharing oc_inx ni;
|
||||
let epos =
|
||||
Iovalue.output_array_access oc_inx_acc (Array.get ni) (Array.length ni) bpos
|
||||
in
|
||||
if epos <> pos_out oc_inx then count_error epos (pos_out oc_inx)
|
||||
|
||||
let make_name_index base =
|
||||
let t = Array.make Dutil.table_size [] in
|
||||
for i = 0 to base.data.persons.len - 1 do
|
||||
let p = base.data.persons.get i in
|
||||
(* not ? ? *)
|
||||
if p.first_name <> 1 && p.first_name <> 1 then
|
||||
List.iter (fun i -> Array.set t i @@ (p.key_index :: Array.get t i))
|
||||
@@ Mutil.list_map_sort_uniq Dutil.name_index
|
||||
@@ Dutil.dsk_person_misc_names base p (fun p -> p.titles)
|
||||
done;
|
||||
Array.map Array.of_list t
|
||||
|
||||
let create_name_index oc_inx oc_inx_acc base =
|
||||
output_index_aux oc_inx oc_inx_acc (make_name_index base)
|
||||
|
||||
module IntSet = Set.Make (struct
|
||||
type t = int
|
||||
|
||||
let compare = compare
|
||||
end)
|
||||
|
||||
let make_strings_of_fsname_aux split get base =
|
||||
let t = Array.make Dutil.table_size IntSet.empty in
|
||||
let add_name (key : string) (value : int) =
|
||||
let key = Dutil.name_index key in
|
||||
let set = Array.get t key in
|
||||
let set' = IntSet.add value set in
|
||||
if set == set' then () else Array.set t key set'
|
||||
in
|
||||
for i = 0 to base.data.persons.len - 1 do
|
||||
let p = Dutil.poi base i in
|
||||
let aux istr =
|
||||
if istr <> 1 then (
|
||||
let s = base.data.strings.get istr in
|
||||
add_name s istr;
|
||||
split (fun i j -> add_name (String.sub s i j) istr) s)
|
||||
in
|
||||
aux (get p)
|
||||
done;
|
||||
Array.map
|
||||
(fun set ->
|
||||
let a = Array.make (IntSet.cardinal set) 0 in
|
||||
let i = ref 0 in
|
||||
IntSet.iter
|
||||
(fun e ->
|
||||
Array.set a !i e;
|
||||
incr i)
|
||||
set;
|
||||
a)
|
||||
t
|
||||
|
||||
let make_strings_of_fname =
|
||||
make_strings_of_fsname_aux Name.split_fname_callback (fun p -> p.first_name)
|
||||
|
||||
let make_strings_of_sname =
|
||||
make_strings_of_fsname_aux Name.split_sname_callback (fun p -> p.surname)
|
||||
|
||||
let create_strings_of_sname oc_inx oc_inx_acc base =
|
||||
output_index_aux oc_inx oc_inx_acc (make_strings_of_sname base)
|
||||
|
||||
let create_strings_of_fname oc_inx oc_inx_acc base =
|
||||
output_index_aux oc_inx oc_inx_acc (make_strings_of_fname base)
|
||||
|
||||
let is_prime a =
|
||||
let rec loop b =
|
||||
if a / b < b then true else if a mod b = 0 then false else loop (b + 1)
|
||||
in
|
||||
loop 2
|
||||
|
||||
let rec prime_after n = if is_prime n then n else prime_after (n + 1)
|
||||
|
||||
let output_strings_hash tmp_strings_inx base =
|
||||
let oc = Secure.open_out_bin tmp_strings_inx in
|
||||
let () = base.data.strings.load_array () in
|
||||
let strings_array = base.data.strings in
|
||||
let taba =
|
||||
Array.make
|
||||
(min Sys.max_array_length (prime_after (max 2 (10 * strings_array.len))))
|
||||
(-1)
|
||||
in
|
||||
let tabl = Array.make strings_array.len (-1) in
|
||||
for i = 0 to strings_array.len - 1 do
|
||||
let ia = Hashtbl.hash (base.data.strings.get i) mod Array.length taba in
|
||||
(* store last associated value associated to the same hash *)
|
||||
tabl.(i) <- taba.(ia);
|
||||
taba.(ia) <- i
|
||||
done;
|
||||
output_binary_int oc (Array.length taba);
|
||||
for i = 0 to Array.length taba - 1 do
|
||||
output_binary_int oc taba.(i)
|
||||
done;
|
||||
for i = 0 to Array.length tabl - 1 do
|
||||
output_binary_int oc tabl.(i)
|
||||
done;
|
||||
close_out oc
|
||||
|
||||
(* Associate istr to persons.
|
||||
A person is associated with its first name/surname and aliases
|
||||
*)
|
||||
let output_name_index_aux cmp get base names_inx names_dat =
|
||||
let ht = Dutil.IntHT.create 0 in
|
||||
for i = 0 to base.data.persons.len - 1 do
|
||||
let p = base.data.persons.get i in
|
||||
let k = get p in
|
||||
match Dutil.IntHT.find_opt ht k with
|
||||
| Some list -> Dutil.IntHT.replace ht k (p.key_index :: list)
|
||||
| None -> Dutil.IntHT.add ht k [ p.key_index ]
|
||||
done;
|
||||
let a = Array.make (Dutil.IntHT.length ht) (0, []) in
|
||||
ignore
|
||||
@@ Dutil.IntHT.fold
|
||||
(fun k v i ->
|
||||
Array.set a i (k, v);
|
||||
succ i)
|
||||
ht 0;
|
||||
(* sort by name behind the int order *)
|
||||
Array.sort (fun (k, _) (k', _) -> cmp k k') a;
|
||||
let oc_n_dat = Secure.open_out_bin names_dat in
|
||||
let bt2 =
|
||||
Array.map
|
||||
(fun (k, ipl) ->
|
||||
let off = pos_out oc_n_dat in
|
||||
output_binary_int oc_n_dat (List.length ipl);
|
||||
List.iter (output_binary_int oc_n_dat) ipl;
|
||||
(k, off))
|
||||
a
|
||||
in
|
||||
close_out oc_n_dat;
|
||||
let oc_n_inx = Secure.open_out_bin names_inx in
|
||||
Dutil.output_value_no_sharing oc_n_inx (bt2 : (int * int) array);
|
||||
close_out oc_n_inx
|
||||
|
||||
let output_surname_index base tmp_snames_inx tmp_snames_dat =
|
||||
output_name_index_aux
|
||||
(Dutil.compare_snames_i base.data)
|
||||
(fun p -> p.surname)
|
||||
base tmp_snames_inx tmp_snames_dat
|
||||
|
||||
let output_first_name_index base tmp_fnames_inx tmp_fnames_dat =
|
||||
output_name_index_aux
|
||||
(Dutil.compare_fnames_i base.data)
|
||||
(fun p -> p.first_name)
|
||||
base tmp_fnames_inx tmp_fnames_dat
|
||||
|
||||
let output_particles_file particles fname =
|
||||
let oc = open_out fname in
|
||||
List.iter (fun s -> Printf.fprintf oc "%s\n" (Mutil.tr ' ' '_' s)) particles;
|
||||
close_out oc
|
||||
|
||||
let output base =
|
||||
(* create database directory *)
|
||||
let bname = base.data.bdir in
|
||||
if not (Sys.file_exists bname) then Unix.mkdir bname 0o755;
|
||||
(* temporary files *)
|
||||
let tmp_particles = Filename.concat bname "1particles.txt" in
|
||||
let tmp_base = Filename.concat bname "1base" in
|
||||
let tmp_base_acc = Filename.concat bname "1base.acc" in
|
||||
let tmp_names_inx = Filename.concat bname "1names.inx" in
|
||||
let tmp_names_acc = Filename.concat bname "1names.acc" in
|
||||
let tmp_snames_inx = Filename.concat bname "1snames.inx" in
|
||||
let tmp_snames_dat = Filename.concat bname "1snames.dat" in
|
||||
let tmp_fnames_inx = Filename.concat bname "1fnames.inx" in
|
||||
let tmp_fnames_dat = Filename.concat bname "1fnames.dat" in
|
||||
let tmp_strings_inx = Filename.concat bname "1strings.inx" in
|
||||
let tmp_notes = Filename.concat bname "1notes" in
|
||||
let tmp_notes_d = Filename.concat bname "1notes_d" in
|
||||
load_ascends_array base;
|
||||
load_unions_array base;
|
||||
load_couples_array base;
|
||||
load_descends_array base;
|
||||
load_strings_array base;
|
||||
let oc = Secure.open_out_bin tmp_base in
|
||||
let oc_acc = Secure.open_out_bin tmp_base_acc in
|
||||
let output_array arrname arr =
|
||||
let bpos = pos_out oc in
|
||||
if !verbose then Printf.eprintf "*** saving %s array\n" arrname;
|
||||
flush stderr;
|
||||
arr.output_array oc;
|
||||
let epos = Iovalue.output_array_access oc_acc arr.get arr.len bpos in
|
||||
if epos <> pos_out oc then count_error epos (pos_out oc)
|
||||
in
|
||||
(try
|
||||
(* output header of "base" *)
|
||||
output_string oc Dutil.magic_GnWb0024;
|
||||
output_binary_int oc base.data.persons.len;
|
||||
output_binary_int oc base.data.families.len;
|
||||
output_binary_int oc base.data.strings.len;
|
||||
let array_start_indexes = pos_out oc in
|
||||
output_binary_int oc 0;
|
||||
output_binary_int oc 0;
|
||||
output_binary_int oc 0;
|
||||
output_binary_int oc 0;
|
||||
output_binary_int oc 0;
|
||||
output_binary_int oc 0;
|
||||
output_binary_int oc 0;
|
||||
Dutil.output_value_no_sharing oc
|
||||
(base.data.bnotes.Def.norigin_file : string);
|
||||
(* output arrays in the "base" and position for each element in the "base.acc" *)
|
||||
let persons_array_pos = pos_out oc in
|
||||
output_array "persons" base.data.persons;
|
||||
let ascends_array_pos = pos_out oc in
|
||||
output_array "ascends" base.data.ascends;
|
||||
let unions_array_pos = pos_out oc in
|
||||
output_array "unions" base.data.unions;
|
||||
let families_array_pos = pos_out oc in
|
||||
output_array "families" base.data.families;
|
||||
let couples_array_pos = pos_out oc in
|
||||
output_array "couples" base.data.couples;
|
||||
let descends_array_pos = pos_out oc in
|
||||
output_array "descends" base.data.descends;
|
||||
let strings_array_pos = pos_out oc in
|
||||
output_array "strings" base.data.strings;
|
||||
(* output arrays position in the header *)
|
||||
seek_out oc array_start_indexes;
|
||||
output_binary_int oc persons_array_pos;
|
||||
output_binary_int oc ascends_array_pos;
|
||||
output_binary_int oc unions_array_pos;
|
||||
output_binary_int oc families_array_pos;
|
||||
output_binary_int oc couples_array_pos;
|
||||
output_binary_int oc descends_array_pos;
|
||||
output_binary_int oc strings_array_pos;
|
||||
base.data.families.clear_array ();
|
||||
base.data.descends.clear_array ();
|
||||
close_out oc;
|
||||
close_out oc_acc;
|
||||
(let oc_inx = Secure.open_out_bin tmp_names_inx in
|
||||
let oc_inx_acc = Secure.open_out_bin tmp_names_acc in
|
||||
try
|
||||
trace "create name index";
|
||||
output_binary_int oc_inx 0;
|
||||
(* room for sname index *)
|
||||
output_binary_int oc_inx 0;
|
||||
(* room for fname index *)
|
||||
create_name_index oc_inx oc_inx_acc base;
|
||||
base.data.ascends.clear_array ();
|
||||
base.data.unions.clear_array ();
|
||||
base.data.couples.clear_array ();
|
||||
if !save_mem then (
|
||||
trace "compacting";
|
||||
Gc.compact ());
|
||||
let surname_pos = pos_out oc_inx in
|
||||
trace "create strings of sname";
|
||||
create_strings_of_sname oc_inx oc_inx_acc base;
|
||||
let first_name_pos = pos_out oc_inx in
|
||||
trace "create strings of fname";
|
||||
create_strings_of_fname oc_inx oc_inx_acc base;
|
||||
seek_out oc_inx 0;
|
||||
(* sname index *)
|
||||
output_binary_int oc_inx surname_pos;
|
||||
seek_out oc_inx 1;
|
||||
(* fname index *)
|
||||
output_binary_int oc_inx first_name_pos;
|
||||
close_out oc_inx;
|
||||
close_out oc_inx_acc;
|
||||
if !save_mem then (
|
||||
trace "compacting";
|
||||
Gc.compact ());
|
||||
Gc.compact ();
|
||||
trace "create string index";
|
||||
output_strings_hash tmp_strings_inx base;
|
||||
if !save_mem then (
|
||||
trace "compacting";
|
||||
Gc.compact ());
|
||||
trace "create surname index";
|
||||
output_surname_index base tmp_snames_inx tmp_snames_dat;
|
||||
if !save_mem then (
|
||||
trace "compacting";
|
||||
Gc.compact ());
|
||||
trace "create first name index";
|
||||
output_first_name_index base tmp_fnames_inx tmp_fnames_dat;
|
||||
let s = base.data.bnotes.Def.nread "" Def.RnAll in
|
||||
(if s = "" then ()
|
||||
else
|
||||
let oc_not = Secure.open_out tmp_notes in
|
||||
output_string oc_not s;
|
||||
close_out oc_not);
|
||||
List.iter
|
||||
(fun f ->
|
||||
let s = base.data.bnotes.Def.nread f Def.RnAll in
|
||||
let fname = Filename.concat tmp_notes_d (f ^ ".txt") in
|
||||
Mutil.mkdir_p (Filename.dirname fname);
|
||||
let oc = open_out fname in
|
||||
output_string oc s;
|
||||
close_out oc)
|
||||
(List.rev (base.data.bnotes.Def.efiles ()));
|
||||
output_particles_file base.data.particles_txt tmp_particles
|
||||
with e ->
|
||||
(try close_out oc_inx with _ -> ());
|
||||
(try close_out oc_inx_acc with _ -> ());
|
||||
raise e);
|
||||
trace "ok";
|
||||
let nbp =
|
||||
let rec loop i acc =
|
||||
if i = base.data.persons.len then acc
|
||||
else
|
||||
let p = base.data.persons.get i in
|
||||
let acc =
|
||||
if
|
||||
p.key_index = -1
|
||||
|| (0 = p.surname || 1 = p.surname)
|
||||
&& (0 = p.first_name || 1 = p.first_name)
|
||||
then acc
|
||||
else acc + 1
|
||||
in
|
||||
loop (i + 1) acc
|
||||
in
|
||||
loop 0 0
|
||||
in
|
||||
let oc = Secure.open_out_bin @@ Filename.concat bname "nb_persons" in
|
||||
output_value oc nbp;
|
||||
close_out oc
|
||||
with e ->
|
||||
(try close_out oc with _ -> ());
|
||||
(try close_out oc_acc with _ -> ());
|
||||
Mutil.rm tmp_base;
|
||||
Mutil.rm tmp_base_acc;
|
||||
Mutil.rm tmp_names_inx;
|
||||
Mutil.rm tmp_names_acc;
|
||||
Mutil.rm tmp_strings_inx;
|
||||
Mutil.remove_dir tmp_notes_d;
|
||||
raise e);
|
||||
close_base base;
|
||||
Mutil.rm (Filename.concat bname "base");
|
||||
Sys.rename tmp_base (Filename.concat bname "base");
|
||||
Mutil.rm (Filename.concat bname "base.acc");
|
||||
Sys.rename tmp_base_acc (Filename.concat bname "base.acc");
|
||||
Mutil.rm (Filename.concat bname "names.inx");
|
||||
Sys.rename tmp_names_inx (Filename.concat bname "names.inx");
|
||||
Mutil.rm (Filename.concat bname "names.acc");
|
||||
Sys.rename tmp_names_acc (Filename.concat bname "names.acc");
|
||||
Mutil.rm (Filename.concat bname "snames.dat");
|
||||
Sys.rename tmp_snames_dat (Filename.concat bname "snames.dat");
|
||||
Mutil.rm (Filename.concat bname "snames.inx");
|
||||
Sys.rename tmp_snames_inx (Filename.concat bname "snames.inx");
|
||||
Mutil.rm (Filename.concat bname "fnames.dat");
|
||||
Sys.rename tmp_fnames_dat (Filename.concat bname "fnames.dat");
|
||||
Mutil.rm (Filename.concat bname "fnames.inx");
|
||||
Sys.rename tmp_fnames_inx (Filename.concat bname "fnames.inx");
|
||||
Mutil.rm (Filename.concat bname "strings.inx");
|
||||
Sys.rename tmp_strings_inx (Filename.concat bname "strings.inx");
|
||||
Sys.rename tmp_particles (Filename.concat bname "particles.txt");
|
||||
Mutil.rm (Filename.concat bname "notes");
|
||||
if Sys.file_exists tmp_notes then
|
||||
Sys.rename tmp_notes (Filename.concat bname "notes");
|
||||
if Sys.file_exists tmp_notes_d then (
|
||||
let notes_d = Filename.concat bname "notes_d" in
|
||||
Mutil.remove_dir notes_d;
|
||||
Sys.rename tmp_notes_d notes_d);
|
||||
Mutil.rm (Filename.concat bname "patches");
|
||||
Mutil.rm (Filename.concat bname "patches~");
|
||||
Mutil.rm (Filename.concat bname "synchro_patches");
|
||||
Mutil.rm (Filename.concat bname "notes_link");
|
||||
Mutil.rm (Filename.concat bname "restrict");
|
||||
Mutil.rm (Filename.concat bname "tstab_visitor");
|
||||
Mutil.rm (Filename.concat bname "nb_persons");
|
||||
(* FIXME: should not be present in this part of the code? *)
|
||||
Mutil.rm (Filename.concat bname "tstab");
|
||||
Mutil.rm (Filename.concat bname "tstab_visitor")
|
||||
28
lib/gwdb-legacy/outbase.mli
Normal file
28
lib/gwdb-legacy/outbase.mli
Normal file
@@ -0,0 +1,28 @@
|
||||
val save_mem : bool ref
|
||||
(** Flag that enables memory saving by calling gc sometimes *)
|
||||
|
||||
val output : Dbdisk.dsk_base -> unit
|
||||
(** [output base] uses data section of the [base] to store database on the disk in the files:
|
||||
|
||||
- {i base} main file that stores all the arrays of the database
|
||||
- {i base.acc} direct accesses to arrays inside {i base} (list of offsets for every array)
|
||||
- {i names.inx} 3 different name indexes :
|
||||
|
||||
- For all kind of mix between person's names. Associate hash value
|
||||
of the name to the array of persons (index of its entry) containing the given name
|
||||
- sub-strings of surname. Associate hash value of the sub-string to
|
||||
the array of string indexes of the names that contains mentionned sub string.
|
||||
- sub-strings of first name. Same storage principe as for surname sub-strings.
|
||||
- {i names.acc} direct accesses to arrays inside {i names.inx}.
|
||||
- {i strings.inx} strings index. Associate hash of the string to the index
|
||||
of its entry in the base's string array. Save also previus value in the case of
|
||||
collision of hash.
|
||||
- {i snames.inx} ordered index for surnames. Associate index of surname to its offset in
|
||||
{i snames.dat}.
|
||||
- {i snames.dat} For a giving surname give associated list of perosons (index of its entry)
|
||||
- {i fnames.inx} and {i fnames.dat} same as for {i snames.inx} and {i snames.dat} but deals
|
||||
with first names
|
||||
- {i notes} text file containing data base notes.
|
||||
- {i notes_d} directory containing .txt for each extended page.
|
||||
- {i particles.txt} text file with autorised name's particles.
|
||||
*)
|
||||
947
lib/gwdb-versioned/gwdb_driver.ml
Normal file
947
lib/gwdb-versioned/gwdb_driver.ml
Normal file
@@ -0,0 +1,947 @@
|
||||
module type Data = sig
|
||||
type t
|
||||
type index = int
|
||||
type base
|
||||
|
||||
val patch_file : base -> string
|
||||
val data_file : base -> string
|
||||
val directory : base -> string
|
||||
val tmp_file : string -> string
|
||||
end
|
||||
|
||||
module Store (D : Data) : sig
|
||||
val get : D.base -> D.index -> D.t option
|
||||
val set : D.base -> D.index -> D.t option -> unit
|
||||
val unsafe_set : D.index -> D.t option -> unit
|
||||
val write : D.base -> unit
|
||||
val sync : (D.base -> D.t option array) -> D.base -> unit
|
||||
val empty : unit -> unit
|
||||
val close_data_file : unit -> unit
|
||||
val move_data_file : D.base -> unit
|
||||
val move_patch_file : D.base -> unit
|
||||
val remove_patch_file : D.base -> unit
|
||||
end = struct
|
||||
type t = (D.index, D.t option) Hashtbl.t
|
||||
|
||||
let patch_ht : (D.index, D.t option) Hashtbl.t option ref = ref None
|
||||
let data_file_in_channel : in_channel option ref = ref None
|
||||
let cache_ht : (D.index, D.t option) Hashtbl.t option ref = ref None
|
||||
|
||||
let open_data_file base =
|
||||
match !data_file_in_channel with
|
||||
| Some ic ->
|
||||
seek_in ic 0;
|
||||
ic
|
||||
| None ->
|
||||
let file = D.data_file base in
|
||||
let ic = Secure.open_in file in
|
||||
data_file_in_channel := Some ic;
|
||||
ic
|
||||
|
||||
let close_data_file () =
|
||||
match !data_file_in_channel with
|
||||
| Some ic ->
|
||||
close_in ic;
|
||||
data_file_in_channel := None
|
||||
| None -> ()
|
||||
|
||||
let patch_file_exists base = Sys.file_exists (D.patch_file base)
|
||||
let data_file_exists base = Sys.file_exists (D.data_file base)
|
||||
let directory_exists base = Sys.file_exists (D.directory base)
|
||||
let create_files base = Files.mkdir_p (D.directory base)
|
||||
|
||||
let move_data_file base =
|
||||
let dataf = D.data_file base in
|
||||
let tmp = D.tmp_file dataf in
|
||||
Files.mv tmp dataf
|
||||
|
||||
let move_patch_file base =
|
||||
let patchf = D.patch_file base in
|
||||
let tmp = D.tmp_file patchf in
|
||||
Files.mv tmp patchf
|
||||
|
||||
let remove_patch_file base =
|
||||
let patchf = D.patch_file base in
|
||||
Files.rm patchf
|
||||
|
||||
let load_patch base =
|
||||
if patch_file_exists base then (
|
||||
let file = D.patch_file base in
|
||||
let ic = Secure.open_in file in
|
||||
let tbl = (Marshal.from_channel ic : t) in
|
||||
close_in ic;
|
||||
patch_ht := Some tbl;
|
||||
tbl)
|
||||
else
|
||||
let tbl = Hashtbl.create 1 in
|
||||
patch_ht := Some tbl;
|
||||
tbl
|
||||
|
||||
let patch base =
|
||||
match !patch_ht with Some ht -> ht | None -> load_patch base
|
||||
|
||||
let cache () =
|
||||
match !cache_ht with
|
||||
| Some ht -> ht
|
||||
| None ->
|
||||
let tbl = Hashtbl.create 10 in
|
||||
cache_ht := Some tbl;
|
||||
tbl
|
||||
|
||||
let get_from_data_file base index =
|
||||
if data_file_exists base then
|
||||
let ic = open_data_file base in
|
||||
let len = input_binary_int ic in
|
||||
if not (index < len) then None
|
||||
else (
|
||||
assert (index < len);
|
||||
seek_in ic (4 + (index * 4));
|
||||
let pos_data = input_binary_int ic in
|
||||
if pos_data <> -1 then (
|
||||
seek_in ic pos_data;
|
||||
let data = (Marshal.from_channel ic : D.t) in
|
||||
let c = cache () in
|
||||
Hashtbl.replace c index (Some data);
|
||||
Some data)
|
||||
else None)
|
||||
else None
|
||||
|
||||
let get_from_cache _base index =
|
||||
match !cache_ht with Some ht -> Hashtbl.find_opt ht index | None -> None
|
||||
|
||||
let get base index =
|
||||
match Hashtbl.find_opt (patch base) index with
|
||||
| Some v -> v
|
||||
| None -> (
|
||||
match get_from_cache base index with
|
||||
| Some v -> v
|
||||
| None -> get_from_data_file base index)
|
||||
|
||||
let set base index value =
|
||||
let tbl = patch base in
|
||||
Hashtbl.replace tbl index value
|
||||
|
||||
let unsafe_set index value =
|
||||
let tbl = Option.get !patch_ht in
|
||||
Hashtbl.replace tbl index value
|
||||
|
||||
let write base =
|
||||
let tbl = patch base in
|
||||
if not (directory_exists base) then create_files base;
|
||||
let patchfile = D.patch_file base in
|
||||
let patchfile_tmp = D.tmp_file patchfile in
|
||||
if Sys.file_exists patchfile_tmp then
|
||||
failwith "Error while writing patch file : temporary file remained";
|
||||
let oc = Secure.open_out patchfile_tmp in
|
||||
Marshal.to_channel oc tbl [ Marshal.No_sharing ];
|
||||
close_out oc
|
||||
|
||||
let empty () =
|
||||
patch_ht := Some (Hashtbl.create 1);
|
||||
data_file_in_channel := None;
|
||||
cache_ht := None
|
||||
|
||||
let load_data build_from_scratch base : D.t option array =
|
||||
if not (data_file_exists base) then build_from_scratch base
|
||||
else
|
||||
let ic = open_data_file base in
|
||||
let len = input_binary_int ic in
|
||||
|
||||
let get_pos i =
|
||||
seek_in ic (4 + (4 * i));
|
||||
let pos = input_binary_int ic in
|
||||
pos
|
||||
in
|
||||
|
||||
seek_in ic (4 + (4 * len));
|
||||
|
||||
let rec loop i l =
|
||||
if i = 0 then l
|
||||
else
|
||||
let pos = get_pos (i - 1) in
|
||||
if pos = -1 then loop (i - 1) (None :: l)
|
||||
else (
|
||||
seek_in ic pos;
|
||||
let l = Some (Marshal.from_channel ic : D.t) :: l in
|
||||
loop (i - 1) l)
|
||||
in
|
||||
|
||||
let data = Array.of_list @@ loop len [] in
|
||||
data
|
||||
|
||||
let sync build_from_scratch base =
|
||||
if not (directory_exists base) then create_files base;
|
||||
|
||||
let tbl = patch base in
|
||||
let data = load_data build_from_scratch base in
|
||||
|
||||
let dfile = D.data_file base in
|
||||
let dfile_tmp = D.tmp_file dfile in
|
||||
if Sys.file_exists dfile_tmp then
|
||||
failwith "Error while writing data file : temporary file remained";
|
||||
|
||||
let oc = Secure.open_out dfile_tmp in
|
||||
|
||||
let syncdata = Hashtbl.create (Array.length data) in
|
||||
Array.iteri (Hashtbl.add syncdata) data;
|
||||
Hashtbl.iter (Hashtbl.replace syncdata) tbl;
|
||||
|
||||
let max_index, l =
|
||||
Hashtbl.fold
|
||||
(fun k v (max_index, l) ->
|
||||
let max_index = max max_index k in
|
||||
(max_index, (k, v) :: l))
|
||||
syncdata (-1, [])
|
||||
in
|
||||
|
||||
let a = Array.of_list l in
|
||||
Array.sort (fun (k, _) (k', _) -> k - k') a;
|
||||
|
||||
let len = max_index + 1 in
|
||||
let accesses = Array.make len (-1) in
|
||||
|
||||
output_binary_int oc len;
|
||||
seek_out oc (4 + (len * 4));
|
||||
Array.iter
|
||||
(fun (index, data) ->
|
||||
match data with
|
||||
| Some data ->
|
||||
let pos = pos_out oc in
|
||||
Marshal.to_channel oc data [ Marshal.No_sharing ];
|
||||
accesses.(index) <- pos
|
||||
| None -> accesses.(index) <- -1)
|
||||
a;
|
||||
seek_out oc 4;
|
||||
Array.iter (output_binary_int oc) accesses;
|
||||
close_out oc;
|
||||
close_data_file ()
|
||||
end
|
||||
|
||||
module Legacy_driver = struct
|
||||
include Gwdb_legacy.Gwdb_driver
|
||||
|
||||
let compatibility_directory = "gnwb25-2"
|
||||
let compatibility_file = "witness_notes"
|
||||
let fcompatibility_file = "fwitness_notes"
|
||||
let data_file = "witness_notes.dat"
|
||||
let fdata_file = "fwitness_notes.dat"
|
||||
let directory base = Filename.concat (bdir base) compatibility_directory
|
||||
let patch_file base = Filename.concat (directory base) compatibility_file
|
||||
let data_file base = Filename.concat (directory base) data_file
|
||||
let fpatch_file base = Filename.concat (directory base) fcompatibility_file
|
||||
let fdata_file base = Filename.concat (directory base) fdata_file
|
||||
let tmp_file fname = fname ^ "~"
|
||||
|
||||
module PersonData = struct
|
||||
type t = istr array array
|
||||
type index = iper
|
||||
type base = Gwdb_legacy.Gwdb_driver.base
|
||||
|
||||
let directory = directory
|
||||
let patch_file = patch_file
|
||||
let data_file = data_file
|
||||
let tmp_file = tmp_file
|
||||
end
|
||||
|
||||
module PatchPer = Store (PersonData)
|
||||
|
||||
module FamilyData = struct
|
||||
type t = istr array array
|
||||
type index = ifam
|
||||
type base = Gwdb_legacy.Gwdb_driver.base
|
||||
|
||||
let directory = directory
|
||||
let patch_file = fpatch_file
|
||||
let data_file = fdata_file
|
||||
let tmp_file = tmp_file
|
||||
end
|
||||
|
||||
module PatchFam = Store (FamilyData)
|
||||
|
||||
let versions = Version.[ gnwb20; gnwb21; gnwb22; gnwb23; gnwb24 ]
|
||||
|
||||
type person = {
|
||||
person : Gwdb_legacy.Gwdb_driver.person;
|
||||
base : Gwdb_legacy.Gwdb_driver.base;
|
||||
mutable witness_notes : istr array array option;
|
||||
}
|
||||
|
||||
type family = {
|
||||
family : Gwdb_legacy.Gwdb_driver.family;
|
||||
base : Gwdb_legacy.Gwdb_driver.base;
|
||||
mutable witness_notes : istr array array option;
|
||||
}
|
||||
|
||||
type pers_event = {
|
||||
pevent : Gwdb_legacy.Gwdb_driver.pers_event;
|
||||
event_index : int;
|
||||
event_person : person;
|
||||
mutable pwitness_notes : istr array option;
|
||||
mutable witnesses : (iper * Def.witness_kind * istr) array option;
|
||||
}
|
||||
|
||||
type fam_event = {
|
||||
fevent : Gwdb_legacy.Gwdb_driver.fam_event;
|
||||
event_index : int;
|
||||
event_family : family;
|
||||
mutable fwitness_notes : istr array option;
|
||||
mutable witnesses : (iper * Def.witness_kind * istr) array option;
|
||||
}
|
||||
|
||||
let poi_ht : (iper, person) Hashtbl.t option ref = ref None
|
||||
let foi_ht : (ifam, family) Hashtbl.t option ref = ref None
|
||||
|
||||
let reset_poi_ht () =
|
||||
match !poi_ht with
|
||||
| Some ht ->
|
||||
Hashtbl.clear ht;
|
||||
poi_ht := None
|
||||
| None -> ()
|
||||
|
||||
let reset_foi_ht () =
|
||||
match !foi_ht with
|
||||
| Some ht ->
|
||||
Hashtbl.clear ht;
|
||||
foi_ht := None
|
||||
| None -> ()
|
||||
|
||||
let cache_foi_poi = ref true
|
||||
|
||||
let set_fpoi_cache _base b =
|
||||
reset_poi_ht ();
|
||||
reset_foi_ht ();
|
||||
cache_foi_poi := b
|
||||
|
||||
let find_poi iper =
|
||||
if not !cache_foi_poi then None
|
||||
else
|
||||
match !poi_ht with
|
||||
| Some ht -> Hashtbl.find_opt ht iper
|
||||
| None ->
|
||||
poi_ht := Some (Hashtbl.create 1);
|
||||
None
|
||||
|
||||
let find_foi ifam =
|
||||
if not !cache_foi_poi then None
|
||||
else
|
||||
match !foi_ht with
|
||||
| Some ht -> Hashtbl.find_opt ht ifam
|
||||
| None ->
|
||||
foi_ht := Some (Hashtbl.create 1);
|
||||
None
|
||||
|
||||
let set_poi iper data =
|
||||
if !cache_foi_poi then
|
||||
match !poi_ht with Some ht -> Hashtbl.add ht iper data | _ -> ()
|
||||
|
||||
let set_foi ifam data =
|
||||
if !cache_foi_poi then
|
||||
match !foi_ht with Some ht -> Hashtbl.add ht ifam data | _ -> ()
|
||||
|
||||
let clear_poi iper =
|
||||
match !poi_ht with Some ht -> Hashtbl.remove ht iper | _ -> ()
|
||||
|
||||
let clear_foi ifam =
|
||||
match !foi_ht with Some ht -> Hashtbl.remove ht ifam | _ -> ()
|
||||
|
||||
let get_pers_full_wit_notes (p : person) =
|
||||
match p.witness_notes with
|
||||
| Some a when Array.length a > 0 ->
|
||||
fun ie ->
|
||||
if Array.length a.(ie) > 0 then fun iw -> a.(ie).(iw)
|
||||
else fun _iw -> empty_string
|
||||
| Some _a -> fun _ie _iw -> empty_string
|
||||
| None -> (
|
||||
let iper = Gwdb_legacy.Gwdb_driver.get_iper p.person in
|
||||
if iper = dummy_iper then (
|
||||
p.witness_notes <- Some [||];
|
||||
fun _ie _iw -> empty_string)
|
||||
else
|
||||
let notes = PatchPer.get p.base iper in
|
||||
match notes with
|
||||
| Some wnotes ->
|
||||
p.witness_notes <- notes;
|
||||
fun ie ->
|
||||
if Array.length wnotes.(ie) = 0 then fun _iw -> empty_string
|
||||
else fun iw -> wnotes.(ie).(iw)
|
||||
| None ->
|
||||
p.witness_notes <- Some [||];
|
||||
fun _ie _iw -> empty_string)
|
||||
|
||||
let get_pers_wit_notes (p : person) ie iw =
|
||||
match p.witness_notes with
|
||||
| Some a when Array.length a > 0 && Array.length a.(ie) > 0 -> a.(ie).(iw)
|
||||
| Some _a -> empty_string
|
||||
| None -> (
|
||||
let iper = Gwdb_legacy.Gwdb_driver.get_iper p.person in
|
||||
if iper = dummy_iper then (
|
||||
p.witness_notes <- Some [||];
|
||||
empty_string)
|
||||
else
|
||||
let notes = PatchPer.get p.base iper in
|
||||
match notes with
|
||||
| Some wnotes ->
|
||||
p.witness_notes <- notes;
|
||||
if Array.length wnotes.(ie) = 0 then empty_string
|
||||
else wnotes.(ie).(iw)
|
||||
| None ->
|
||||
p.witness_notes <- Some [||];
|
||||
empty_string)
|
||||
|
||||
let get_fam_full_wit_notes (f : family) =
|
||||
match f.witness_notes with
|
||||
| Some a when Array.length a > 0 ->
|
||||
fun ie ->
|
||||
if Array.length a.(ie) > 0 then fun iw -> a.(ie).(iw)
|
||||
else fun _iw -> empty_string
|
||||
| Some _a -> fun _ie _iw -> empty_string
|
||||
| None -> (
|
||||
let ifam = Gwdb_legacy.Gwdb_driver.get_ifam f.family in
|
||||
if ifam = dummy_ifam then (
|
||||
f.witness_notes <- Some [||];
|
||||
fun _ie _iw -> empty_string)
|
||||
else
|
||||
let notes = PatchFam.get f.base ifam in
|
||||
match notes with
|
||||
| Some wnotes ->
|
||||
f.witness_notes <- notes;
|
||||
fun ie ->
|
||||
if Array.length wnotes.(ie) = 0 then fun _iw -> empty_string
|
||||
else fun iw -> wnotes.(ie).(iw)
|
||||
| None ->
|
||||
f.witness_notes <- Some [||];
|
||||
fun _ie _iw -> empty_string)
|
||||
|
||||
let get_fam_wit_notes (f : family) ie iw =
|
||||
match f.witness_notes with
|
||||
| Some a when Array.length a > 0 && Array.length a.(ie) > 0 -> a.(ie).(iw)
|
||||
| Some _a -> empty_string
|
||||
| None -> (
|
||||
let ifam = Gwdb_legacy.Gwdb_driver.get_ifam f.family in
|
||||
if ifam = dummy_ifam then (
|
||||
f.witness_notes <- Some [||];
|
||||
empty_string)
|
||||
else
|
||||
let notes = PatchFam.get f.base ifam in
|
||||
match notes with
|
||||
| Some wnotes ->
|
||||
f.witness_notes <- notes;
|
||||
if Array.length wnotes.(ie) = 0 then empty_string
|
||||
else wnotes.(ie).(iw)
|
||||
| None ->
|
||||
f.witness_notes <- Some [||];
|
||||
empty_string)
|
||||
|
||||
let gen_person_of_person (p : person) =
|
||||
let gen_pers = gen_person_of_person p.person in
|
||||
let pevents =
|
||||
List.mapi
|
||||
(fun ie pe ->
|
||||
let pe = Translate.legacy_to_def_pevent empty_string pe in
|
||||
let epers_witnesses =
|
||||
Array.mapi
|
||||
(fun iw (ip, wk, _) -> (ip, wk, get_pers_wit_notes p ie iw))
|
||||
pe.epers_witnesses
|
||||
in
|
||||
{ pe with epers_witnesses })
|
||||
gen_pers.pevents
|
||||
in
|
||||
let gen_pers = Translate.legacy_to_def_person empty_string gen_pers in
|
||||
{ gen_pers with pevents }
|
||||
|
||||
let person_of_gen_person base (genpers, gen_ascend, gen_union) =
|
||||
let pevents = genpers.Def.pevents in
|
||||
let witness_notes =
|
||||
Some
|
||||
(List.map
|
||||
(fun pe ->
|
||||
Array.map (fun (_, _, wnote) -> wnote) pe.Def.epers_witnesses)
|
||||
pevents
|
||||
|> Array.of_list)
|
||||
in
|
||||
let genpers = Translate.as_legacy_person genpers in
|
||||
let person = person_of_gen_person base (genpers, gen_ascend, gen_union) in
|
||||
{ person; base; witness_notes }
|
||||
|
||||
let no_person iper =
|
||||
let nop = no_person iper in
|
||||
Translate.legacy_to_def_person empty_string nop
|
||||
|
||||
let witness_notes_of_events pevents : istr array array option =
|
||||
let l =
|
||||
List.map
|
||||
(fun pe ->
|
||||
let a =
|
||||
Array.map (fun (_, _, wnote) -> wnote) pe.Def.epers_witnesses
|
||||
in
|
||||
if Array.exists (fun wnote -> not (is_empty_string wnote)) a then a
|
||||
else [||])
|
||||
pevents
|
||||
in
|
||||
let has_data = List.exists (fun a -> Array.length a <> 0) l in
|
||||
if has_data then Some (Array.of_list l) else None
|
||||
|
||||
let fwitness_notes_of_events fevents : istr array array option =
|
||||
let l =
|
||||
List.map
|
||||
(fun fe ->
|
||||
let a =
|
||||
Array.map (fun (_, _, wnote) -> wnote) fe.Def.efam_witnesses
|
||||
in
|
||||
if Array.exists (fun wnote -> not (is_empty_string wnote)) a then a
|
||||
else [||])
|
||||
fevents
|
||||
in
|
||||
let has_data = List.exists (fun a -> Array.length a <> 0) l in
|
||||
if has_data then Some (Array.of_list l) else None
|
||||
|
||||
let patch_person base iper genpers =
|
||||
let pevents = genpers.Def.pevents in
|
||||
let genpers = Translate.as_legacy_person genpers in
|
||||
patch_person base iper genpers;
|
||||
let witnotes = witness_notes_of_events pevents in
|
||||
PatchPer.set base iper witnotes;
|
||||
clear_poi iper
|
||||
|
||||
let insert_person base iper genpers =
|
||||
let pevents = genpers.Def.pevents in
|
||||
let genpers = Translate.as_legacy_person genpers in
|
||||
insert_person base iper genpers;
|
||||
let witnotes = witness_notes_of_events pevents in
|
||||
PatchPer.set base iper witnotes;
|
||||
clear_poi iper
|
||||
|
||||
let delete_person base iper =
|
||||
Gwdb_legacy.Gwdb_driver.delete_person base iper;
|
||||
clear_poi iper
|
||||
|
||||
let commit_patches base =
|
||||
PatchPer.write base;
|
||||
PatchFam.write base;
|
||||
commit_patches base;
|
||||
PatchPer.move_patch_file base;
|
||||
PatchFam.move_patch_file base
|
||||
|
||||
let pwitness_notes_of_pevent (pe : pers_event) =
|
||||
match pe.pwitness_notes with
|
||||
| Some a -> a
|
||||
| None ->
|
||||
let wlen = Array.length pe.pevent.epers_witnesses in
|
||||
let a =
|
||||
Array.init wlen
|
||||
(get_pers_full_wit_notes pe.event_person pe.event_index)
|
||||
in
|
||||
pe.pwitness_notes <- Some a;
|
||||
a
|
||||
|
||||
let fwitness_notes_of_fevent (fe : fam_event) =
|
||||
match fe.fwitness_notes with
|
||||
| Some a -> a
|
||||
| None ->
|
||||
let wlen = Array.length fe.fevent.efam_witnesses in
|
||||
let a =
|
||||
Array.init wlen
|
||||
(get_fam_full_wit_notes fe.event_family fe.event_index)
|
||||
in
|
||||
fe.fwitness_notes <- Some a;
|
||||
a
|
||||
|
||||
let get_pevents (p : person) =
|
||||
let pevents = Gwdb_legacy.Gwdb_driver.get_pevents p.person in
|
||||
List.mapi
|
||||
(fun ie pevent ->
|
||||
{
|
||||
pevent;
|
||||
event_person = p;
|
||||
event_index = ie;
|
||||
pwitness_notes = None;
|
||||
witnesses = None;
|
||||
})
|
||||
pevents
|
||||
|
||||
let get_pevent_name pe = pe.pevent.epers_name
|
||||
let get_pevent_date pe = pe.pevent.epers_date
|
||||
let get_pevent_place pe = pe.pevent.epers_place
|
||||
let get_pevent_reason pe = pe.pevent.epers_reason
|
||||
let get_pevent_note pe = pe.pevent.epers_note
|
||||
let get_pevent_src pe = pe.pevent.epers_src
|
||||
let get_pevent_witnesses pe = pe.pevent.epers_witnesses
|
||||
let get_pevent_witness_notes pe = pwitness_notes_of_pevent pe
|
||||
|
||||
let get_pevent_witnesses_and_notes (pe : pers_event) =
|
||||
match pe.witnesses with
|
||||
| Some a -> a
|
||||
| None ->
|
||||
let len = Array.length pe.pevent.epers_witnesses in
|
||||
let wnotes = pwitness_notes_of_pevent pe in
|
||||
let a =
|
||||
Array.init len (fun iw ->
|
||||
let ip, wk = pe.pevent.epers_witnesses.(iw) in
|
||||
let wnote = wnotes.(iw) in
|
||||
(ip, wk, wnote))
|
||||
in
|
||||
pe.witnesses <- Some a;
|
||||
a
|
||||
|
||||
let gen_pevent_of_pers_event pe =
|
||||
let genpers = Translate.legacy_to_def_pevent empty_string pe.pevent in
|
||||
let len = Array.length pe.pevent.epers_witnesses in
|
||||
let wnotes = pwitness_notes_of_pevent pe in
|
||||
let epers_witnesses =
|
||||
Array.init len (fun i ->
|
||||
let ip, wk = pe.pevent.epers_witnesses.(i) in
|
||||
let wnote = wnotes.(i) in
|
||||
(ip, wk, wnote))
|
||||
in
|
||||
{ genpers with epers_witnesses }
|
||||
|
||||
let pers_event_of_gen_pevent _base _genpers = assert false
|
||||
let eq_pevent p1 p2 = p1.pevent = p2.pevent
|
||||
let eq_fevent f1 f2 = f1.fevent = f2.fevent
|
||||
|
||||
let gen_fevent_of_fam_event fe =
|
||||
let genfam = Translate.legacy_to_def_fevent empty_string fe.fevent in
|
||||
let len = Array.length fe.fevent.efam_witnesses in
|
||||
let wnotes = fwitness_notes_of_fevent fe in
|
||||
let efam_witnesses =
|
||||
Array.init len (fun i ->
|
||||
let ip, wk = fe.fevent.efam_witnesses.(i) in
|
||||
let wnote = wnotes.(i) in
|
||||
(ip, wk, wnote))
|
||||
in
|
||||
{ genfam with efam_witnesses }
|
||||
|
||||
let fam_event_of_gen_fevent _base _genfam = assert false
|
||||
|
||||
let get_fevents (f : family) =
|
||||
let fevents = Gwdb_legacy.Gwdb_driver.get_fevents f.family in
|
||||
List.mapi
|
||||
(fun ie fevent ->
|
||||
{
|
||||
fevent;
|
||||
fwitness_notes = None;
|
||||
event_index = ie;
|
||||
witnesses = None;
|
||||
event_family = f;
|
||||
})
|
||||
fevents
|
||||
|
||||
let get_fevent_name fe = fe.fevent.efam_name
|
||||
let get_fevent_date fe = fe.fevent.efam_date
|
||||
let get_fevent_place fe = fe.fevent.efam_place
|
||||
let get_fevent_reason fe = fe.fevent.efam_reason
|
||||
let get_fevent_note fe = fe.fevent.efam_note
|
||||
let get_fevent_src fe = fe.fevent.efam_src
|
||||
let get_fevent_witnesses fe = fe.fevent.efam_witnesses
|
||||
let get_fevent_witness_notes fe = fwitness_notes_of_fevent fe
|
||||
|
||||
let get_fevent_witnesses_and_notes fe =
|
||||
let len = Array.length fe.fevent.efam_witnesses in
|
||||
let wnotes = fwitness_notes_of_fevent fe in
|
||||
Array.init len (fun iw ->
|
||||
let ip, wk = fe.fevent.efam_witnesses.(iw) in
|
||||
let wnote = wnotes.(iw) in
|
||||
(ip, wk, wnote))
|
||||
|
||||
let build_from_scratch_pevents base =
|
||||
let persons = Gwdb_legacy.Gwdb_driver.persons base in
|
||||
let max_index, data =
|
||||
Gwdb_legacy.Gwdb_driver.Collection.fold
|
||||
(fun (max_index, l) p ->
|
||||
let iper = get_iper p in
|
||||
(max max_index iper, (iper, None) :: l))
|
||||
(0, []) persons
|
||||
in
|
||||
let d = Array.make (max_index + 1) None in
|
||||
List.iter (fun (i, v) -> if i = -1 then () else Array.unsafe_set d i v) data;
|
||||
d
|
||||
|
||||
let build_from_scratch_fevents base =
|
||||
let families = Gwdb_legacy.Gwdb_driver.families base in
|
||||
let max_index, data =
|
||||
Gwdb_legacy.Gwdb_driver.Collection.fold
|
||||
(fun (max_index, l) f ->
|
||||
let ifam = get_ifam f in
|
||||
(max max_index ifam, (ifam, None) :: l))
|
||||
(0, []) families
|
||||
in
|
||||
let d = Array.make (max_index + 1) None in
|
||||
List.iter (fun (i, v) -> if i = -1 then () else Array.unsafe_set d i v) data;
|
||||
d
|
||||
|
||||
let sync ?(scratch = false) ~save_mem base =
|
||||
let dir = Filename.concat (bdir base) compatibility_directory in
|
||||
if scratch && Sys.file_exists dir then Files.remove_dir dir;
|
||||
PatchPer.sync build_from_scratch_pevents base;
|
||||
PatchFam.sync build_from_scratch_fevents base;
|
||||
|
||||
sync ~scratch ~save_mem base;
|
||||
|
||||
PatchPer.move_data_file base;
|
||||
PatchPer.remove_patch_file base;
|
||||
PatchFam.move_data_file base;
|
||||
PatchFam.remove_patch_file base
|
||||
|
||||
let make bname particles
|
||||
( (persons, ascends, unions),
|
||||
(families, couples, descends),
|
||||
string_arrays,
|
||||
base_notes ) =
|
||||
PatchPer.empty ();
|
||||
PatchFam.empty ();
|
||||
let persons =
|
||||
Array.map
|
||||
(fun p ->
|
||||
let leg_person = Translate.as_legacy_person p in
|
||||
PatchPer.unsafe_set p.key_index (witness_notes_of_events p.pevents);
|
||||
leg_person)
|
||||
persons
|
||||
in
|
||||
let families =
|
||||
Array.map
|
||||
(fun f ->
|
||||
let leg_family = Translate.as_legacy_family f in
|
||||
PatchFam.unsafe_set f.fam_index (fwitness_notes_of_events f.fevents);
|
||||
leg_family)
|
||||
families
|
||||
in
|
||||
let base =
|
||||
make bname particles
|
||||
( (persons, ascends, unions),
|
||||
(families, couples, descends),
|
||||
string_arrays,
|
||||
base_notes )
|
||||
in
|
||||
|
||||
let dir = Filename.concat (bdir base) compatibility_directory in
|
||||
if Sys.file_exists dir then Files.remove_dir dir;
|
||||
PatchPer.sync build_from_scratch_pevents base;
|
||||
|
||||
PatchFam.sync build_from_scratch_fevents base;
|
||||
PatchPer.move_data_file base;
|
||||
PatchFam.move_data_file base;
|
||||
base
|
||||
|
||||
let open_base = open_base
|
||||
|
||||
let empty_person base iper =
|
||||
let p = empty_person base iper in
|
||||
{ person = p; base; witness_notes = Some [||] }
|
||||
|
||||
let get_access p = get_access p.person
|
||||
let get_aliases p = get_aliases p.person
|
||||
let get_baptism p = get_baptism p.person
|
||||
let get_baptism_note p = get_baptism_note p.person
|
||||
let get_baptism_place p = get_baptism_place p.person
|
||||
let get_baptism_src p = get_baptism_src p.person
|
||||
let get_birth p = get_birth p.person
|
||||
let get_birth_note p = get_birth_note p.person
|
||||
let get_birth_place p = get_birth_place p.person
|
||||
let get_birth_src p = get_birth_src p.person
|
||||
let get_death p = get_death p.person
|
||||
let get_death_note p = get_death_note p.person
|
||||
let get_death_place p = get_death_place p.person
|
||||
let get_death_src p = get_death_src p.person
|
||||
let get_burial p = get_burial p.person
|
||||
let get_burial_note p = get_burial_note p.person
|
||||
let get_burial_place p = get_burial_place p.person
|
||||
let get_burial_src p = get_burial_src p.person
|
||||
let get_consang p = get_consang p.person
|
||||
let get_family p = get_family p.person
|
||||
let get_first_name p = get_first_name p.person
|
||||
let get_first_names_aliases p = get_first_names_aliases p.person
|
||||
let get_image p = get_image p.person
|
||||
let get_iper p = get_iper p.person
|
||||
let get_notes p = get_notes p.person
|
||||
let get_occ p = get_occ p.person
|
||||
let get_occupation p = get_occupation p.person
|
||||
let get_parents p = get_parents p.person
|
||||
let get_psources p = get_psources p.person
|
||||
let get_public_name p = get_public_name p.person
|
||||
let get_qualifiers p = get_qualifiers p.person
|
||||
let get_related p = get_related p.person
|
||||
let get_rparents p = get_rparents p.person
|
||||
let get_sex p = get_sex p.person
|
||||
let get_surname p = get_surname p.person
|
||||
let get_surnames_aliases p = get_surnames_aliases p.person
|
||||
let get_titles p = get_titles p.person
|
||||
let gen_ascend_of_person p = gen_ascend_of_person p.person
|
||||
let gen_union_of_person p = gen_union_of_person p.person
|
||||
|
||||
let poi base iper =
|
||||
match find_poi iper with
|
||||
| Some p -> p
|
||||
| None ->
|
||||
let p = { person = poi base iper; base; witness_notes = None } in
|
||||
set_poi iper p;
|
||||
p
|
||||
|
||||
let base_visible_get base (f : person -> bool) iper =
|
||||
let f person = f { person; base; witness_notes = None } in
|
||||
base_visible_get base f iper
|
||||
|
||||
let persons base =
|
||||
let coll = persons base in
|
||||
Collection.map (fun person -> { person; base; witness_notes = None }) coll
|
||||
|
||||
let empty_family base ifam =
|
||||
let f = empty_family base ifam in
|
||||
{ family = f; base; witness_notes = Some [||] }
|
||||
|
||||
let gen_family_of_family f =
|
||||
let gen_fam = gen_family_of_family f.family in
|
||||
let fevents =
|
||||
List.mapi
|
||||
(fun ie fe ->
|
||||
let fe = Translate.legacy_to_def_fevent empty_string fe in
|
||||
let efam_witnesses =
|
||||
Array.mapi
|
||||
(fun iw (ip, wk, _) -> (ip, wk, get_fam_wit_notes f ie iw))
|
||||
fe.efam_witnesses
|
||||
in
|
||||
{ fe with efam_witnesses })
|
||||
gen_fam.fevents
|
||||
in
|
||||
let gen_fam = Translate.legacy_to_def_family empty_string gen_fam in
|
||||
{ gen_fam with fevents }
|
||||
|
||||
let family_of_gen_family base (genfam, gen_couple, gen_descend) =
|
||||
let fevents = genfam.Def.fevents in
|
||||
let witness_notes =
|
||||
Some
|
||||
(List.map
|
||||
(fun fe ->
|
||||
Array.map (fun (_, _, wnote) -> wnote) fe.Def.efam_witnesses)
|
||||
fevents
|
||||
|> Array.of_list)
|
||||
in
|
||||
let genfam = Translate.as_legacy_family genfam in
|
||||
let family = family_of_gen_family base (genfam, gen_couple, gen_descend) in
|
||||
{ family; base; witness_notes }
|
||||
|
||||
let no_family ifam =
|
||||
let nof = no_family ifam in
|
||||
Translate.legacy_to_def_family empty_string nof
|
||||
|
||||
let patch_family base ifam genfam =
|
||||
let fevents = genfam.Def.fevents in
|
||||
let genfam = Translate.as_legacy_family genfam in
|
||||
patch_family base ifam genfam;
|
||||
let witnotes = fwitness_notes_of_events fevents in
|
||||
PatchFam.set base ifam witnotes;
|
||||
clear_foi ifam
|
||||
|
||||
let insert_family base ifam genfam =
|
||||
let fevents = genfam.Def.fevents in
|
||||
let genfam = Translate.as_legacy_family genfam in
|
||||
insert_family base ifam genfam;
|
||||
let witnotes = fwitness_notes_of_events fevents in
|
||||
PatchFam.set base ifam witnotes;
|
||||
clear_foi ifam
|
||||
|
||||
let delete_family base ifam =
|
||||
Gwdb_legacy.Gwdb_driver.delete_family base ifam;
|
||||
clear_foi ifam
|
||||
|
||||
let get_children f = get_children f.family
|
||||
let get_comment f = get_comment f.family
|
||||
let get_divorce f = get_divorce f.family
|
||||
let get_father f = get_father f.family
|
||||
let get_fsources f = get_fsources f.family
|
||||
let get_ifam f = get_ifam f.family
|
||||
let get_marriage f = get_marriage f.family
|
||||
let get_marriage_note f = get_marriage_note f.family
|
||||
let get_marriage_place f = get_marriage_place f.family
|
||||
let get_marriage_src f = get_marriage_src f.family
|
||||
let get_mother f = get_mother f.family
|
||||
let get_origin_file f = get_origin_file f.family
|
||||
let get_parent_array f = get_parent_array f.family
|
||||
let get_relation f = get_relation f.family
|
||||
let get_witnesses f = get_witnesses f.family
|
||||
let gen_couple_of_family f = gen_couple_of_family f.family
|
||||
let gen_descend_of_family f = gen_descend_of_family f.family
|
||||
|
||||
let foi base ifam =
|
||||
match find_foi ifam with
|
||||
| Some f -> f
|
||||
| None ->
|
||||
let f = { family = foi base ifam; base; witness_notes = None } in
|
||||
set_foi ifam f;
|
||||
f
|
||||
|
||||
let families ?(select = fun _ -> true) base =
|
||||
let select f = select { family = f; base; witness_notes = None } in
|
||||
let coll = families ~select base in
|
||||
Collection.map (fun family -> { family; base; witness_notes = None }) coll
|
||||
|
||||
let wrap_pid clear patch insert delete =
|
||||
let patch b i d =
|
||||
patch b i d;
|
||||
clear i
|
||||
in
|
||||
let insert b i d =
|
||||
insert b i d;
|
||||
clear i
|
||||
in
|
||||
let delete b i =
|
||||
delete b i;
|
||||
clear i
|
||||
in
|
||||
(patch, insert, delete)
|
||||
|
||||
let wrap_iper_pid p i d = wrap_pid clear_poi p i d
|
||||
let wrap_ifam_pid p i d = wrap_pid clear_foi p i d
|
||||
|
||||
let patch_ascend, insert_ascend, delete_ascend =
|
||||
wrap_iper_pid patch_ascend insert_ascend delete_ascend
|
||||
|
||||
let patch_union, insert_union, delete_union =
|
||||
wrap_iper_pid patch_union insert_union delete_union
|
||||
|
||||
let patch_descend, insert_descend, delete_descend =
|
||||
wrap_ifam_pid patch_descend insert_descend delete_descend
|
||||
|
||||
let patch_couple, insert_couple, delete_couple =
|
||||
wrap_ifam_pid patch_couple insert_couple delete_couple
|
||||
|
||||
let load_clear_array load clear =
|
||||
let load_array base =
|
||||
set_fpoi_cache base false;
|
||||
load base
|
||||
in
|
||||
let clear_array base =
|
||||
clear base;
|
||||
set_fpoi_cache base true
|
||||
in
|
||||
(load_array, clear_array)
|
||||
|
||||
let load_ascends_array, clear_ascends_array =
|
||||
load_clear_array load_ascends_array clear_ascends_array
|
||||
|
||||
let load_descends_array, clear_descends_array =
|
||||
load_clear_array load_descends_array clear_descends_array
|
||||
|
||||
let load_unions_array, clear_unions_array =
|
||||
load_clear_array load_unions_array clear_unions_array
|
||||
|
||||
let load_couples_array, clear_couples_array =
|
||||
load_clear_array load_couples_array clear_couples_array
|
||||
|
||||
let close_base base =
|
||||
close_base base;
|
||||
PatchPer.close_data_file ();
|
||||
PatchFam.close_data_file ();
|
||||
clear_ascends_array base;
|
||||
clear_unions_array base;
|
||||
clear_couples_array base;
|
||||
clear_descends_array base;
|
||||
clear_strings_array base;
|
||||
clear_persons_array base;
|
||||
clear_families_array base;
|
||||
PatchPer.empty ();
|
||||
PatchFam.empty ();
|
||||
()
|
||||
end
|
||||
|
||||
module Driver = Compat.Make (Legacy_driver) (Legacy_driver)
|
||||
include Driver
|
||||
11
lib/gwdb/dune.in
Normal file
11
lib/gwdb/dune.in
Normal file
@@ -0,0 +1,11 @@
|
||||
(library
|
||||
(name geneweb_gwdb)
|
||||
(public_name geneweb.gwdb)
|
||||
(wrapped false)
|
||||
(preprocess (action (run %{bin:cppo} %%%CPPO_D%%% -V OCAML:%{ocaml_version} %{input-file})))
|
||||
(libraries unix
|
||||
geneweb.def
|
||||
geneweb.gwdb_driver
|
||||
geneweb.util
|
||||
)
|
||||
)
|
||||
298
lib/gwdb/gutil.ml
Normal file
298
lib/gwdb/gutil.ml
Normal file
@@ -0,0 +1,298 @@
|
||||
(* Copyright (c) 1998-2007 INRIA *)
|
||||
|
||||
open Def
|
||||
open Gwdb
|
||||
|
||||
let designation base p =
|
||||
let first_name = p_first_name base p in
|
||||
let nom = p_surname base p in
|
||||
first_name ^ "." ^ string_of_int (get_occ p) ^ " " ^ nom
|
||||
|
||||
let father = Adef.father
|
||||
let mother = Adef.mother
|
||||
|
||||
let couple multi fath moth =
|
||||
if not multi then Adef.couple fath moth else Adef.multi_couple fath moth
|
||||
|
||||
let parent_array = Adef.parent_array
|
||||
|
||||
let spouse ip cpl =
|
||||
if ip = get_father cpl then get_mother cpl else get_father cpl
|
||||
|
||||
let person_is_key base p k =
|
||||
let k = Name.crush_lower k in
|
||||
if k = Name.crush_lower (p_first_name base p ^ " " ^ p_surname base p) then
|
||||
true
|
||||
else if
|
||||
List.exists
|
||||
(fun x -> k = Name.crush_lower x)
|
||||
(person_misc_names base p get_titles)
|
||||
then true
|
||||
else false
|
||||
|
||||
let find_num s i =
|
||||
let rec loop start i =
|
||||
if i = String.length s then None
|
||||
else
|
||||
match s.[i] with
|
||||
| '0' .. '9' -> loop start (i + 1)
|
||||
| c ->
|
||||
if i = start then
|
||||
if c = ' ' then loop (start + 1) (start + 1) else None
|
||||
else Some (int_of_string (String.sub s start (i - start)), i)
|
||||
in
|
||||
loop i i
|
||||
|
||||
let split_key s i =
|
||||
let rec loop i =
|
||||
if i = String.length s then None
|
||||
else if s.[i] = '.' then
|
||||
match find_num s (i + 1) with
|
||||
| Some (occ, j) ->
|
||||
let first_name = String.sub s 0 i in
|
||||
let surname = String.sub s j (String.length s - j) in
|
||||
Some (i, first_name, occ, surname)
|
||||
| None -> loop (i + 1)
|
||||
else loop (i + 1)
|
||||
in
|
||||
loop i
|
||||
|
||||
let person_of_string_key base s =
|
||||
let rec loop i =
|
||||
match split_key s i with
|
||||
| Some (i, first_name, occ, surname) -> (
|
||||
match person_of_key base first_name surname occ with
|
||||
| Some ip -> Some ip
|
||||
| None -> loop (i + 1))
|
||||
| None -> None
|
||||
in
|
||||
loop 0
|
||||
|
||||
let rsplit_key s =
|
||||
let rec loop i =
|
||||
if i = 0 then None
|
||||
else if s.[i] = '.' then
|
||||
match find_num s (i + 1) with
|
||||
| Some (occ, j) ->
|
||||
let first_name = String.sub s 0 i in
|
||||
let surname = String.sub s j (String.length s - j) in
|
||||
Some (first_name, occ, surname)
|
||||
| None -> loop (i - 1)
|
||||
else loop (i - 1)
|
||||
in
|
||||
loop (String.length s - 1)
|
||||
|
||||
let person_of_string_dot_key base s =
|
||||
match rsplit_key s with
|
||||
| Some (first_name, occ, surname) -> person_of_key base first_name surname occ
|
||||
| None -> None
|
||||
|
||||
let person_not_a_key_find_all base s =
|
||||
let ipl = persons_of_name base s in
|
||||
let rec select = function
|
||||
| ip :: ipl ->
|
||||
if person_is_key base (poi base ip) s then
|
||||
let ipl = select ipl in
|
||||
if List.mem ip ipl then ipl else ip :: ipl
|
||||
else select ipl
|
||||
| [] -> []
|
||||
in
|
||||
select ipl
|
||||
|
||||
let person_ht_find_all base s =
|
||||
match person_of_string_key base s with
|
||||
| Some p -> [ p ]
|
||||
| None -> person_not_a_key_find_all base s
|
||||
|
||||
let find_same_name base p =
|
||||
let f = p_first_name base p in
|
||||
let s = p_surname base p in
|
||||
let ipl = person_ht_find_all base (f ^ " " ^ s) in
|
||||
let f = Name.strip_lower f in
|
||||
let s = Name.strip_lower s in
|
||||
let pl =
|
||||
List.fold_left
|
||||
(fun pl ip ->
|
||||
let p = poi base ip in
|
||||
if
|
||||
Name.strip_lower (p_first_name base p) = f
|
||||
&& Name.strip_lower (p_surname base p) = s
|
||||
then p :: pl
|
||||
else pl)
|
||||
[] ipl
|
||||
in
|
||||
List.sort (fun p1 p2 -> compare (get_occ p1) (get_occ p2)) pl
|
||||
|
||||
let trim_trailing_spaces s =
|
||||
let len = String.length s in
|
||||
let len' =
|
||||
let rec loop i =
|
||||
if i = -1 then 0
|
||||
else
|
||||
match String.unsafe_get s i with
|
||||
| ' ' | '\r' | '\n' | '\t' -> loop (i - 1)
|
||||
| _ -> i + 1
|
||||
in
|
||||
loop (len - 1)
|
||||
in
|
||||
if len' = 0 then "" else if len' = len then s else String.sub s 0 len'
|
||||
|
||||
let alphabetic_utf_8 n1 n2 =
|
||||
let rec loop i1 i2 =
|
||||
if i1 >= String.length n1 && i2 >= String.length n2 then i1 - i2
|
||||
else if i1 >= String.length n1 then -1
|
||||
else if i2 >= String.length n2 then 1
|
||||
else
|
||||
let cv1, ii1 = Name.unaccent_utf_8 false n1 i1 in
|
||||
let cv2, ii2 = Name.unaccent_utf_8 false n2 i2 in
|
||||
let c =
|
||||
if cv1 = cv2 then
|
||||
compare (String.sub n1 i1 (ii1 - i1)) (String.sub n2 i2 (ii2 - i2))
|
||||
else compare cv1 cv2
|
||||
in
|
||||
if c = 0 then loop ii1 ii2 else c
|
||||
in
|
||||
if n1 = n2 then 0 else loop 0 0
|
||||
|
||||
let alphabetic_value =
|
||||
let tab = Array.make 256 0 in
|
||||
for i = 0 to 255 do
|
||||
tab.(i) <- 10 * i
|
||||
done;
|
||||
tab.(Char.code '\xE0') <- (*'à'*) tab.(Char.code 'a') + 1;
|
||||
tab.(Char.code '\xE1') <- (*'á'*) tab.(Char.code 'a') + 2;
|
||||
tab.(Char.code '\xE2') <- (*'â'*) tab.(Char.code 'a') + 3;
|
||||
tab.(Char.code '\xE8') <- (*'è'*) tab.(Char.code 'e') + 1;
|
||||
tab.(Char.code '\xE9') <- (*'é'*) tab.(Char.code 'e') + 2;
|
||||
tab.(Char.code '\xEA') <- (*'ê'*) tab.(Char.code 'e') + 3;
|
||||
tab.(Char.code '\xEB') <- (*'ë'*) tab.(Char.code 'e') + 4;
|
||||
tab.(Char.code '\xF4') <- (*'ô'*) tab.(Char.code 'o') + 1;
|
||||
tab.(Char.code '\xC1') <- (*'Á'*) tab.(Char.code 'A') + 2;
|
||||
tab.(Char.code '\xC6') <- (*'Æ'*) tab.(Char.code 'A') + 5;
|
||||
tab.(Char.code '\xC8') <- (*'È'*) tab.(Char.code 'E') + 1;
|
||||
tab.(Char.code '\xC9') <- (*'É'*) tab.(Char.code 'E') + 2;
|
||||
tab.(Char.code '\xD6') <- (*'Ö'*) tab.(Char.code 'O') + 4;
|
||||
tab.(Char.code '?') <- 3000;
|
||||
fun x -> tab.(Char.code x)
|
||||
|
||||
let alphabetic_iso_8859_1 n1 n2 =
|
||||
let rec loop i1 i2 =
|
||||
if i1 = String.length n1 && i2 = String.length n2 then i1 - i2
|
||||
else if i1 = String.length n1 then -1
|
||||
else if i2 = String.length n2 then 1
|
||||
else
|
||||
let c1 = n1.[i1] in
|
||||
let c2 = n2.[i2] in
|
||||
if alphabetic_value c1 < alphabetic_value c2 then -1
|
||||
else if alphabetic_value c1 > alphabetic_value c2 then 1
|
||||
else loop (succ i1) (succ i2)
|
||||
in
|
||||
if n1 = n2 then 0 else loop (Mutil.initial n1) (Mutil.initial n2)
|
||||
|
||||
(* ??? *)
|
||||
let alphabetic n1 n2 =
|
||||
(*
|
||||
if Mutil.utf_8_db.val then alphabetic_utf_8 n1 n2 else alphabetic_iso_8859_1 n1 n2
|
||||
*)
|
||||
alphabetic_iso_8859_1 n1 n2
|
||||
|
||||
let alphabetic_order n1 n2 = alphabetic_utf_8 n1 n2
|
||||
|
||||
let arg_list_of_string line =
|
||||
let rec loop list i len quote =
|
||||
if i = String.length line then
|
||||
if len = 0 then List.rev list else List.rev (Buff.get len :: list)
|
||||
else
|
||||
match (quote, line.[i]) with
|
||||
| Some c1, c2 ->
|
||||
if c1 = c2 then loop list (i + 1) len None
|
||||
else loop list (i + 1) (Buff.store len c2) quote
|
||||
| None, ' ' ->
|
||||
let list = if len = 0 then list else Buff.get len :: list in
|
||||
loop list (i + 1) 0 quote
|
||||
| None, (('"' | '\'') as c) -> loop list (i + 1) 0 (Some c)
|
||||
| None, c -> loop list (i + 1) (Buff.store len c) None
|
||||
in
|
||||
loop [] 0 0 None
|
||||
|
||||
let sort_person_list_aux sort base =
|
||||
let default p1 p2 =
|
||||
match alphabetic (p_surname base p1) (p_surname base p2) with
|
||||
| 0 -> (
|
||||
match alphabetic (p_first_name base p1) (p_first_name base p2) with
|
||||
| 0 -> (
|
||||
match compare (get_occ p1) (get_occ p2) with
|
||||
| 0 -> compare (get_iper p1) (get_iper p2)
|
||||
| c -> c)
|
||||
| c -> c)
|
||||
| c -> c
|
||||
in
|
||||
sort (fun p1 p2 ->
|
||||
if get_iper p1 = get_iper p2 then 0
|
||||
else
|
||||
match
|
||||
match
|
||||
( Date.od_of_cdate (get_birth p1),
|
||||
get_death p1,
|
||||
Date.od_of_cdate (get_birth p2),
|
||||
get_death p2 )
|
||||
with
|
||||
| Some d1, _, Some d2, _ -> Date.compare_date d1 d2
|
||||
| Some d1, _, _, Death (_, d2) ->
|
||||
Date.compare_date d1 (Date.date_of_cdate d2)
|
||||
| _, Death (_, d1), Some d2, _ ->
|
||||
Date.compare_date (Date.date_of_cdate d1) d2
|
||||
| _, Death (_, d1), _, Death (_, d2) ->
|
||||
Date.compare_date (Date.date_of_cdate d1) (Date.date_of_cdate d2)
|
||||
| Some _, _, _, _ -> 1
|
||||
| _, Death (_, _), _, _ -> 1
|
||||
| _, _, Some _, _ -> -1
|
||||
| _, _, _, Death (_, _) -> -1
|
||||
| _ -> 0
|
||||
with
|
||||
| 0 -> default p1 p2
|
||||
| c -> c)
|
||||
|
||||
let sort_person_list = sort_person_list_aux List.sort
|
||||
let sort_uniq_person_list = sort_person_list_aux List.sort_uniq
|
||||
|
||||
let find_free_occ base f s =
|
||||
let ipl = persons_of_name base (f ^ " " ^ s) in
|
||||
let first_name = Name.lower f in
|
||||
let surname = Name.lower s in
|
||||
let list_occ =
|
||||
let rec loop list = function
|
||||
| ip :: ipl ->
|
||||
let p = poi base ip in
|
||||
if
|
||||
(not (List.mem (get_occ p) list))
|
||||
&& first_name = Name.lower (p_first_name base p)
|
||||
&& surname = Name.lower (p_surname base p)
|
||||
then loop (get_occ p :: list) ipl
|
||||
else loop list ipl
|
||||
| [] -> list
|
||||
in
|
||||
loop [] ipl
|
||||
in
|
||||
let list_occ = List.sort compare list_occ in
|
||||
let rec loop cnt1 = function
|
||||
| cnt2 :: list -> if cnt1 = cnt2 then loop (cnt1 + 1) list else cnt1
|
||||
| [] -> cnt1
|
||||
in
|
||||
loop 0 list_occ
|
||||
|
||||
let get_birth_death_date p =
|
||||
let birth_date, approx =
|
||||
match Date.od_of_cdate (get_birth p) with
|
||||
| None -> (Date.od_of_cdate (get_baptism p), true)
|
||||
| x -> (x, false)
|
||||
in
|
||||
let death_date, approx =
|
||||
match Date.date_of_death (get_death p) with
|
||||
| Some d -> (Some d, approx)
|
||||
| None -> (
|
||||
match get_burial p with
|
||||
| Buried cd | Cremated cd -> (Date.od_of_cdate cd, true)
|
||||
| UnknownBurial -> (None, approx))
|
||||
in
|
||||
(birth_date, death_date, approx)
|
||||
85
lib/gwdb/gutil.mli
Normal file
85
lib/gwdb/gutil.mli
Normal file
@@ -0,0 +1,85 @@
|
||||
(* Copyright (c) 1998-2007 INRIA *)
|
||||
|
||||
open Def
|
||||
open Gwdb
|
||||
|
||||
val spouse : iper -> family -> iper
|
||||
(** [spouse p f] returns spouse of giving person inside the family. *)
|
||||
|
||||
val person_not_a_key_find_all : base -> string -> iper list
|
||||
(** Returns list of persons having the giving name as one of the misc names. *)
|
||||
|
||||
val person_ht_find_all : base -> string -> iper list
|
||||
(** Returns list of persons from the giving key. If key has form {i "firstname.occ surname"}
|
||||
then returns list of one corresponding person. Otherwise calls [person_not_a_key_find_all] *)
|
||||
|
||||
val person_of_string_key : base -> string -> iper option
|
||||
(** [person_of_string_key base key]
|
||||
Finds a key inside [key] string of the form {i "firstname.occ surname"}
|
||||
and returns a corresponding person.
|
||||
The first occurence of an int preceded by a dot is used as occurence number.
|
||||
|
||||
If person doesn't exists or key isn't found then returns [None] *)
|
||||
|
||||
val person_of_string_dot_key : base -> string -> iper option
|
||||
(** [person_of_string_dot_key base key]
|
||||
Same as {!val:person_of_string_key}, but use the last occurence
|
||||
of an int preceded by a dot as occurence number. *)
|
||||
|
||||
val find_same_name : base -> person -> person list
|
||||
(** Returns list of persons having the same first name and surname
|
||||
as the specified person *)
|
||||
|
||||
val designation : base -> person -> string
|
||||
(** Returns person's key that has form {i "firstname.occ surname"} *)
|
||||
|
||||
val trim_trailing_spaces : string -> string
|
||||
(** Trim at the end of string *)
|
||||
|
||||
val alphabetic_utf_8 : string -> string -> int
|
||||
(** Compare two UTF-8 encoded strings by alphabetic order *)
|
||||
|
||||
val alphabetic : string -> string -> int
|
||||
(** Compare two ISO-8859-1 encoded strings by alphabetic order *)
|
||||
|
||||
val alphabetic_order : string -> string -> int
|
||||
(** Same as [alphabetic_utf_8] *)
|
||||
|
||||
val arg_list_of_string : string -> string list
|
||||
(** Parse line and extract separated arguments ("" and '' are used to indlude spaces
|
||||
inside the argument) *)
|
||||
|
||||
val sort_person_list : base -> person list -> person list
|
||||
(** Sort list of persons by comparison with following order:
|
||||
- Compare by birth and death date
|
||||
- Compare by surname
|
||||
- Compare by first name
|
||||
- Compare by occurence number
|
||||
- Compare by id *)
|
||||
|
||||
val sort_uniq_person_list : base -> person list -> person list
|
||||
(** Same as [sort_person_list] but also remove duplicates *)
|
||||
|
||||
val father : 'a gen_couple -> 'a
|
||||
(** Same as [Adef.father] *)
|
||||
|
||||
val mother : 'a gen_couple -> 'a
|
||||
(** Same as [Adef.mother] *)
|
||||
|
||||
val couple : bool -> 'a -> 'a -> 'a gen_couple
|
||||
(** [couple multi f m] creates a couple from father [f] and mother [m]. If
|
||||
[multi] true uses multiparent functionality *)
|
||||
|
||||
val parent_array : 'a gen_couple -> 'a array
|
||||
(** Same as [Adef.parent_array] *)
|
||||
|
||||
val find_free_occ : base -> string -> string -> int
|
||||
(** Find first free occurence number for the person with specified first name
|
||||
and surname. *)
|
||||
|
||||
val get_birth_death_date : person -> date option * date option * bool
|
||||
(** [get_birth_death p]
|
||||
Return [(birth, death, approx)]. If birth/death date can not be found,
|
||||
baptism/burial date is used and [approx] is set to [true] (it is [false]
|
||||
if both birth and death dates are found).
|
||||
*)
|
||||
250
lib/gwdb/gwdb.ml
Normal file
250
lib/gwdb/gwdb.ml
Normal file
@@ -0,0 +1,250 @@
|
||||
open Def
|
||||
include Gwdb_driver
|
||||
|
||||
(** [insert_person base p a u]
|
||||
Add a new person with its union and ascendants in the [base].
|
||||
Allocate and returns the fresh new id for this person.
|
||||
[p] SHOULD be defined using [dummy_iper].
|
||||
*)
|
||||
let insert_person base p a u =
|
||||
let iper = Gwdb_driver.new_iper base in
|
||||
let p = { p with key_index = iper } in
|
||||
Gwdb_driver.insert_ascend base iper a;
|
||||
Gwdb_driver.insert_union base iper u;
|
||||
Gwdb_driver.insert_person base iper p;
|
||||
iper
|
||||
|
||||
(** [insert_family base f c d]
|
||||
Add a new family with its couple and descendants the in the [base].
|
||||
Allocate and returns the fresh new id for this family.
|
||||
[f] SHOULD be defined using [dummy_ifam].
|
||||
*)
|
||||
let insert_family base f c d =
|
||||
let ifam = Gwdb_driver.new_ifam base in
|
||||
Gwdb_driver.insert_family base ifam f;
|
||||
Gwdb_driver.insert_couple base ifam c;
|
||||
Gwdb_driver.insert_descend base ifam d;
|
||||
ifam
|
||||
|
||||
(** DELETE *)
|
||||
|
||||
let getp fn b i = fn @@ Gwdb_driver.poi b i
|
||||
let get_gen_person = getp Gwdb_driver.gen_person_of_person
|
||||
let get_gen_ascend = getp Gwdb_driver.gen_ascend_of_person
|
||||
let get_gen_union = getp Gwdb_driver.gen_union_of_person
|
||||
let getf fn b i = fn @@ Gwdb_driver.foi b i
|
||||
let get_gen_family = getf Gwdb_driver.gen_family_of_family
|
||||
let get_gen_couple = getf Gwdb_driver.gen_couple_of_family
|
||||
let get_gen_descend = getf Gwdb_driver.gen_descend_of_family
|
||||
|
||||
let rec delete_person excl base ip =
|
||||
let iexcl, fexcl = excl in
|
||||
if ip = dummy_iper || List.mem ip iexcl then
|
||||
failwith
|
||||
("gwdb.delete_person(" ^ string_of_iper ip ^ ",["
|
||||
^ (List.map string_of_iper iexcl |> String.concat ",")
|
||||
^ "])");
|
||||
let a = get_gen_ascend base ip in
|
||||
(* if person is the single child and their parents are empty persons
|
||||
then [ipers] contains father and mother and [ifams] contains family *)
|
||||
let ipers, ifams =
|
||||
match a.parents with
|
||||
| Some ifam ->
|
||||
(* delete ascendants *)
|
||||
Gwdb_driver.delete_ascend base ip;
|
||||
(* remove person id from family descendants *)
|
||||
let children =
|
||||
(get_gen_descend base ifam).children |> Mutil.array_except ip
|
||||
in
|
||||
Gwdb_driver.patch_descend base ifam { children };
|
||||
if children = [| ip |] then
|
||||
let c = get_gen_couple base ifam in
|
||||
let fath = Adef.father c in
|
||||
let moth = Adef.mother c in
|
||||
if is_empty_p base fath ~ifam && is_empty_p base moth ~ifam then
|
||||
([ fath; moth ], [ ifam ])
|
||||
else ([], [])
|
||||
else ([], [])
|
||||
| None -> ([], [])
|
||||
in
|
||||
let del, ipers, ifams =
|
||||
let u = get_gen_union base ip in
|
||||
if u.family = [||] then (true, [], [])
|
||||
else
|
||||
Array.fold_left
|
||||
(fun (del, ipers, ifams) ifam ->
|
||||
let cpl = get_gen_couple base ifam in
|
||||
(* Test if ip is really in union in order to prevent "false positive" *)
|
||||
let fath = Adef.father cpl in
|
||||
let moth = Adef.mother cpl in
|
||||
if fath = ip || moth = ip then
|
||||
let d = get_gen_descend base ifam in
|
||||
if Array.length d.children > 1 then (false, ipers, ifams)
|
||||
else
|
||||
let sp = if ip = fath then moth else fath in
|
||||
if List.mem sp iexcl then (del, ipers, ifams)
|
||||
else if is_empty_p base sp ~ifam then
|
||||
(del, sp :: ipers, ifam :: ifams)
|
||||
else (false, ipers, ifams)
|
||||
else (
|
||||
(* Data are probably partially deleted.
|
||||
It is likely to happen when merging persons. *)
|
||||
rm_union base ifam ip;
|
||||
(del, ipers, ifams)))
|
||||
(true, ipers, ifams) u.family
|
||||
in
|
||||
if del then Gwdb_driver.delete_person base ip
|
||||
else
|
||||
Gwdb_driver.patch_person base ip
|
||||
{ (no_person ip) with first_name = quest_string; surname = quest_string };
|
||||
let iexcl = if del then ip :: iexcl else iexcl in
|
||||
let excl = (iexcl, fexcl) in
|
||||
let excl =
|
||||
List.fold_left (fun excl ip -> delete_person excl base ip) excl ipers
|
||||
in
|
||||
List.fold_left (fun excl ifam -> delete_family excl base ifam) excl ifams
|
||||
|
||||
and is_empty_p ?ifam base sp =
|
||||
(get_gen_ascend base sp).parents = None
|
||||
&& ((get_gen_union base sp).family
|
||||
= match ifam with Some i -> [| i |] | None -> [||])
|
||||
&& get_gen_person base sp
|
||||
= { (no_person sp) with first_name = quest_string; surname = quest_string }
|
||||
|
||||
and delete_family excl base ifam =
|
||||
let iexcl, fexcl = excl in
|
||||
if ifam = dummy_ifam || List.mem ifam fexcl then
|
||||
failwith
|
||||
("gwdb.delete_family(" ^ string_of_ifam ifam ^ ",["
|
||||
^ (List.map string_of_ifam fexcl |> String.concat ",")
|
||||
^ "])");
|
||||
let fam = foi base ifam in
|
||||
let fath = get_father fam in
|
||||
let moth = get_mother fam in
|
||||
let children = get_children fam in
|
||||
rm_union base ifam fath;
|
||||
rm_union base ifam moth;
|
||||
Array.iter (fun i -> patch_ascend base i no_ascend) children;
|
||||
Gwdb_driver.delete_family base ifam;
|
||||
Gwdb_driver.delete_couple base ifam;
|
||||
Gwdb_driver.delete_descend base ifam;
|
||||
let fexcl = ifam :: fexcl in
|
||||
let excl = (iexcl, fexcl) in
|
||||
let excl =
|
||||
if (not (List.mem fath iexcl)) && is_empty_p base fath then
|
||||
delete_person excl base fath
|
||||
else excl
|
||||
in
|
||||
let excl =
|
||||
if (not (List.mem moth iexcl)) && is_empty_p base moth then
|
||||
delete_person excl base moth
|
||||
else excl
|
||||
in
|
||||
Array.fold_left
|
||||
(fun excl i ->
|
||||
if (not (List.mem i iexcl)) && is_empty_p base i then
|
||||
delete_person excl base i
|
||||
else excl)
|
||||
excl children
|
||||
|
||||
and rm_union base ifam iper =
|
||||
{ family = (get_gen_union base iper).family |> Mutil.array_except ifam }
|
||||
|> patch_union base iper
|
||||
|
||||
(** [delete_person base iper] and [delete_family base ifam]
|
||||
recursively delete data trying to do clever things:
|
||||
- if data to be deleted is linked and useful,
|
||||
it is replaced by empty data (and is actually deleted otherwise)
|
||||
- if empty data is linked to deleted data, the former is deleted as well
|
||||
*)
|
||||
let delete_person base iper = ignore @@ delete_person ([], []) base iper
|
||||
|
||||
(** See {!val:delete_person} *)
|
||||
let delete_family base ifam = ignore @@ delete_family ([], []) base ifam
|
||||
|
||||
(**/**)
|
||||
|
||||
(** Misc *)
|
||||
|
||||
(** [nobtitles base allowed_titles denied_titles p] returns list of titles of a person [p]
|
||||
that apprears in [allowed_titles] and doesn't appears in [denied_titles]. If [allowed_titles]
|
||||
is empty the every title is allowed *)
|
||||
let nobtitles base allowed_titles denied_titles p =
|
||||
let list = get_titles p in
|
||||
match Lazy.force allowed_titles with
|
||||
| [] -> list
|
||||
| allowed_titles -> (
|
||||
let list =
|
||||
List.fold_right
|
||||
(fun t l ->
|
||||
let id = Name.lower (sou base t.t_ident) in
|
||||
let pl = Name.lower (sou base t.t_place) in
|
||||
if pl = "" then if List.mem id allowed_titles then t :: l else l
|
||||
else if
|
||||
List.mem (id ^ "/" ^ pl) allowed_titles
|
||||
|| List.mem (id ^ "/*") allowed_titles
|
||||
then t :: l
|
||||
else l)
|
||||
list []
|
||||
in
|
||||
match Lazy.force denied_titles with
|
||||
| [] -> list
|
||||
| denied_titles ->
|
||||
List.filter
|
||||
(fun t ->
|
||||
let id = Name.lower (sou base t.t_ident) in
|
||||
let pl = Name.lower (sou base t.t_place) in
|
||||
if
|
||||
List.mem (id ^ "/" ^ pl) denied_titles
|
||||
|| List.mem ("*/" ^ pl) denied_titles
|
||||
then false
|
||||
else true)
|
||||
list)
|
||||
|
||||
(** Returns first name of person *)
|
||||
let p_first_name base p = Mutil.nominative (sou base (get_first_name p))
|
||||
|
||||
(** Returns surname of person *)
|
||||
let p_surname base p = Mutil.nominative (sou base (get_surname p))
|
||||
|
||||
(** Returns array of surnames of person's husbands.
|
||||
First element of a couple in the array is husband's surname,
|
||||
second - is a husband's surname aliases *)
|
||||
let husbands base gp =
|
||||
let p = poi base gp.key_index in
|
||||
Array.map
|
||||
(fun ifam ->
|
||||
let fam = foi base ifam in
|
||||
let husband = poi base (get_father fam) in
|
||||
let husband_surname = get_surname husband in
|
||||
let husband_surnames_aliases = get_surnames_aliases husband in
|
||||
(husband_surname, husband_surnames_aliases))
|
||||
(get_family p)
|
||||
|
||||
(** Return person's father titles *)
|
||||
let father_titles_places base p (nobtit : person -> title list) =
|
||||
match get_parents (poi base p.key_index) with
|
||||
| Some ifam ->
|
||||
let fam = foi base ifam in
|
||||
let fath = poi base (get_father fam) in
|
||||
nobtit fath
|
||||
| None -> []
|
||||
|
||||
let gen_gen_person_misc_names base p nobtit nobtit_fun =
|
||||
Futil.gen_person_misc_names (sou base) empty_string quest_string p.first_name
|
||||
p.surname p.public_name p.qualifiers p.aliases p.first_names_aliases
|
||||
p.surnames_aliases nobtit
|
||||
(if p.sex = Female then husbands base p else [||])
|
||||
(father_titles_places base p nobtit_fun)
|
||||
|> List.map Name.lower
|
||||
|
||||
(** [person_misc_names base p nobtit] computes various mix between all kind of names of a person's entry [p]
|
||||
from the database [base]. [nobtit] is used to return a title entries for passed in argument person. *)
|
||||
let person_misc_names base p nobtit =
|
||||
gen_gen_person_misc_names base (gen_person_of_person p) (nobtit p) nobtit
|
||||
|
||||
(** Returns list of children ids for every family for giving person *)
|
||||
let children_of_p base p =
|
||||
Array.fold_right
|
||||
(fun ifam -> Array.fold_right List.cons (get_children @@ foi base ifam))
|
||||
(get_family p) []
|
||||
6
lib/gwdb_driver.mli/dune
Normal file
6
lib/gwdb_driver.mli/dune
Normal file
@@ -0,0 +1,6 @@
|
||||
(library
|
||||
(name gwdb_driver_mli)
|
||||
(public_name geneweb.gwdb_driver)
|
||||
(wrapped false)
|
||||
(libraries geneweb.def re)
|
||||
(virtual_modules gwdb_driver))
|
||||
668
lib/gwdb_driver.mli/gwdb_driver.mli
Normal file
668
lib/gwdb_driver.mli/gwdb_driver.mli
Normal file
@@ -0,0 +1,668 @@
|
||||
(* Copyright (c) 1998-2007 INRIA *)
|
||||
|
||||
type istr
|
||||
(** String id *)
|
||||
|
||||
type ifam
|
||||
(** Family id *)
|
||||
|
||||
type iper
|
||||
(** Person id *)
|
||||
|
||||
val string_of_iper : iper -> string
|
||||
(** Convert [iper] to string *)
|
||||
|
||||
val string_of_ifam : ifam -> string
|
||||
(** Convert [ifam] to string *)
|
||||
|
||||
val string_of_istr : istr -> string
|
||||
(** Convert [istr] to string *)
|
||||
|
||||
val iper_of_string : string -> iper
|
||||
(** Convert [iper] from string *)
|
||||
|
||||
val ifam_of_string : string -> ifam
|
||||
(** Convert [ifam] from string *)
|
||||
|
||||
val istr_of_string : string -> istr
|
||||
(** Convert [istr] from string *)
|
||||
|
||||
type person
|
||||
(** Person data structure *)
|
||||
|
||||
type family
|
||||
(** Family data structure *)
|
||||
|
||||
type relation = (iper, istr) Def.gen_relation
|
||||
(** Database implementation for [Def.gen_relation] *)
|
||||
|
||||
type title = istr Def.gen_title
|
||||
(** Database implementation for [Def.gen_title] *)
|
||||
|
||||
type pers_event = (iper, istr) Def.gen_pers_event
|
||||
(** Database implementation for [Def.pers_event] *)
|
||||
|
||||
type fam_event = (iper, istr) Def.gen_fam_event
|
||||
(** Database implementation for [Def.fam_event] *)
|
||||
|
||||
type string_person_index
|
||||
(** Data structure for optimised search throughout index by name
|
||||
(surname or first name). *)
|
||||
|
||||
type base
|
||||
(** The database representation. *)
|
||||
|
||||
val open_base : string -> base
|
||||
(** Open database associated with (likely situated in) the specified directory. *)
|
||||
|
||||
val close_base : base -> unit
|
||||
(** Close database. May perform some clean up tasks. *)
|
||||
|
||||
val dummy_iper : iper
|
||||
(** Dummy person id *)
|
||||
|
||||
val dummy_ifam : ifam
|
||||
(** Dummy family id *)
|
||||
|
||||
val eq_istr : istr -> istr -> bool
|
||||
(** [true] if strings with the giving ids are equal *)
|
||||
|
||||
val eq_ifam : ifam -> ifam -> bool
|
||||
(** [true] if families with the giving ids are equal *)
|
||||
|
||||
val eq_iper : iper -> iper -> bool
|
||||
(** [true] if persons with the giving ids are equal *)
|
||||
|
||||
val is_empty_string : istr -> bool
|
||||
(** [true] if string with the giving id is empty ("") *)
|
||||
|
||||
val is_quest_string : istr -> bool
|
||||
(** [true] if string with the giving id is a question mark ("?") *)
|
||||
|
||||
val empty_string : istr
|
||||
(** Id of the empty string ("") *)
|
||||
|
||||
val quest_string : istr
|
||||
(** Id of the question mark ("?") *)
|
||||
|
||||
val empty_person : base -> iper -> person
|
||||
(** Returns unitialised person with the giving id. *)
|
||||
|
||||
val empty_family : base -> ifam -> family
|
||||
(** Returns unitialised family with the giving id. *)
|
||||
|
||||
val iper_exists : base -> iper -> bool
|
||||
(** Tells if person with giving id exists in the base. *)
|
||||
|
||||
val ifam_exists : base -> ifam -> bool
|
||||
(** Tells if family with giving id exists in the base. *)
|
||||
|
||||
(** {2 Getters}
|
||||
Getters are used to extract information about person and family.
|
||||
If corresponding information part isn't present, driver load it from
|
||||
the disk and cache it so further gets will return result immediately. *)
|
||||
|
||||
val get_access : person -> Def.access
|
||||
(** Get privacy settings that define access to person's data *)
|
||||
|
||||
val get_aliases : person -> istr list
|
||||
(** Get person's aliases ids *)
|
||||
|
||||
val get_baptism : person -> Def.cdate
|
||||
(** Get person's baptism date *)
|
||||
|
||||
val get_baptism_note : person -> istr
|
||||
(** Get person's baptism note id *)
|
||||
|
||||
val get_baptism_place : person -> istr
|
||||
(** Get person's baptism place id *)
|
||||
|
||||
val get_baptism_src : person -> istr
|
||||
(** Get person's baptism source id *)
|
||||
|
||||
val get_birth : person -> Def.cdate
|
||||
(** Get person's birth date *)
|
||||
|
||||
val get_birth_note : person -> istr
|
||||
(** Get person's birth note id *)
|
||||
|
||||
val get_birth_place : person -> istr
|
||||
(** Get person's birth place id *)
|
||||
|
||||
val get_birth_src : person -> istr
|
||||
(** Get person's birth source id *)
|
||||
|
||||
val get_burial : person -> Def.burial
|
||||
(** Get information about person's burial *)
|
||||
|
||||
val get_burial_note : person -> istr
|
||||
(** Get person's burial note id *)
|
||||
|
||||
val get_burial_place : person -> istr
|
||||
(** Get person's burial place id *)
|
||||
|
||||
val get_burial_src : person -> istr
|
||||
(** Get person's burial source id *)
|
||||
|
||||
val get_children : family -> iper array
|
||||
(** Get array of family's children ids *)
|
||||
|
||||
val get_comment : family -> istr
|
||||
(** Get family's comment (notes) id *)
|
||||
|
||||
val get_consang : person -> Adef.fix
|
||||
(** Get person's consanguinity degree with his ascendants *)
|
||||
|
||||
val get_death : person -> Def.death
|
||||
(** Get person's death status *)
|
||||
|
||||
val get_death_note : person -> istr
|
||||
(** Get person's death note id *)
|
||||
|
||||
val get_death_place : person -> istr
|
||||
(** Get person's death place id *)
|
||||
|
||||
val get_death_src : person -> istr
|
||||
(** Get person's death source id *)
|
||||
|
||||
val get_divorce : family -> Def.divorce
|
||||
(** Get family's divorce status *)
|
||||
|
||||
val get_family : person -> ifam array
|
||||
(** Get array of family's ids to which a person belongs as parent (person's union) *)
|
||||
|
||||
val get_father : family -> iper
|
||||
(** Get family's father id (from the family's couple) *)
|
||||
|
||||
val get_fevents : family -> fam_event list
|
||||
(** Get family's event list *)
|
||||
|
||||
val get_first_name : person -> istr
|
||||
(** Get person's first name id *)
|
||||
|
||||
val get_first_names_aliases : person -> istr list
|
||||
(** Get list of person's first name aliases ids *)
|
||||
|
||||
val get_fsources : family -> istr
|
||||
(** Get family's sources id *)
|
||||
|
||||
val get_ifam : family -> ifam
|
||||
(** Get family's id *)
|
||||
|
||||
val get_image : person -> istr
|
||||
(** Get id of path to person's image *)
|
||||
|
||||
val get_iper : person -> iper
|
||||
(** Get person's id *)
|
||||
|
||||
val get_marriage : family -> Def.cdate
|
||||
(** Get family's marriage date *)
|
||||
|
||||
val get_marriage_note : family -> istr
|
||||
(** Get family's marriage note id *)
|
||||
|
||||
val get_marriage_place : family -> istr
|
||||
(** Get family's marriage place id *)
|
||||
|
||||
val get_marriage_src : family -> istr
|
||||
(** Get family's marriage source id *)
|
||||
|
||||
val get_mother : family -> iper
|
||||
(** Get family's mother id (from the family's couple) *)
|
||||
|
||||
val get_notes : person -> istr
|
||||
(** Get person's notes id *)
|
||||
|
||||
val get_occ : person -> int
|
||||
(** Get person's occurence number *)
|
||||
|
||||
val get_occupation : person -> istr
|
||||
(** Get person's occupation id *)
|
||||
|
||||
val get_origin_file : family -> istr
|
||||
(** Get family's origin file (e.g. a .gw or .ged filename) id *)
|
||||
|
||||
val get_parent_array : family -> iper array
|
||||
(** Get family's parents ids (father and mother from family's couple) *)
|
||||
|
||||
val get_parents : person -> ifam option
|
||||
(** Get person's family id to which his parents belong (as family's couple) *)
|
||||
|
||||
val get_pevents : person -> pers_event list
|
||||
(** Get person's event list *)
|
||||
|
||||
val get_psources : person -> istr
|
||||
(** Get person's sources id *)
|
||||
|
||||
val get_public_name : person -> istr
|
||||
(** Get person's public name id *)
|
||||
|
||||
val get_qualifiers : person -> istr list
|
||||
(** Get list of person's qualifiers ids *)
|
||||
|
||||
val get_related : person -> iper list
|
||||
(** Get person's related persons ids *)
|
||||
|
||||
val get_relation : family -> Def.relation_kind
|
||||
(** Get relation kind between couple in the family *)
|
||||
|
||||
val get_rparents : person -> relation list
|
||||
(** Get person's relations with not native parents *)
|
||||
|
||||
val get_sex : person -> Def.sex
|
||||
(** Get person's sex *)
|
||||
|
||||
val get_surname : person -> istr
|
||||
(** Get person's surname id *)
|
||||
|
||||
val get_surnames_aliases : person -> istr list
|
||||
(** Get person's surname aliases ids *)
|
||||
|
||||
val get_titles : person -> title list
|
||||
(** Get list of person's nobility titles *)
|
||||
|
||||
val get_witnesses : family -> iper array
|
||||
(** Get array of family's witnesses ids *)
|
||||
|
||||
val gen_couple_of_family : family -> iper Def.gen_couple
|
||||
(** Extract [gen_couple] from [family]. *)
|
||||
|
||||
val gen_descend_of_family : family -> iper Def.gen_descend
|
||||
(** Extract [gen_descend] from [family]. *)
|
||||
|
||||
val gen_family_of_family : family -> (iper, ifam, istr) Def.gen_family
|
||||
(** Extract [gen_family] from [family]. *)
|
||||
|
||||
val gen_person_of_person : person -> (iper, iper, istr) Def.gen_person
|
||||
(** Extract [gen_person] from [person]. *)
|
||||
|
||||
val gen_ascend_of_person : person -> ifam Def.gen_ascend
|
||||
(** Extract [gen_ascend] from [person]. *)
|
||||
|
||||
val gen_union_of_person : person -> ifam Def.gen_union
|
||||
(** Extract [gen_union] from [person]. *)
|
||||
|
||||
val family_of_gen_family :
|
||||
base ->
|
||||
(iper, ifam, istr) Def.gen_family * iper Def.gen_couple * iper Def.gen_descend ->
|
||||
family
|
||||
(** Create [family] from associated values. *)
|
||||
|
||||
val person_of_gen_person :
|
||||
base ->
|
||||
(iper, iper, istr) Def.gen_person * ifam Def.gen_ascend * ifam Def.gen_union ->
|
||||
person
|
||||
(** Create [person] from associated values. *)
|
||||
|
||||
val poi : base -> iper -> person
|
||||
(** Create uninitialised person with giving id *)
|
||||
|
||||
val foi : base -> ifam -> family
|
||||
(** Create uninitialised family with giving id *)
|
||||
|
||||
val sou : base -> istr -> string
|
||||
(** Returns string that has giving id from the base *)
|
||||
|
||||
val no_person : iper -> (iper, iper, istr) Def.gen_person
|
||||
(** Returns unitialised [gen_person] with giving id *)
|
||||
|
||||
val no_ascend : ifam Def.gen_ascend
|
||||
(** Returns unitialised [gen_ascend] *)
|
||||
|
||||
val no_union : ifam Def.gen_union
|
||||
(** Returns unitialised [gen_union] *)
|
||||
|
||||
val no_family : ifam -> (iper, ifam, istr) Def.gen_family
|
||||
(** Returns unitialised [gen_family] with giving id *)
|
||||
|
||||
val no_descend : iper Def.gen_descend
|
||||
(** Returns unitialised [gen_descend] *)
|
||||
|
||||
val no_couple : iper Def.gen_couple
|
||||
(** Returns unitialised [gen_couple] *)
|
||||
|
||||
val nb_of_persons : base -> int
|
||||
(** Returns number of persons inside the database *)
|
||||
|
||||
val nb_of_real_persons : base -> int
|
||||
(** Returns number of defined persons (without bogus definition "? ?")
|
||||
inside the database *)
|
||||
|
||||
val nb_of_families : base -> int
|
||||
(** Returns number of families inside the database *)
|
||||
|
||||
val bname : base -> string
|
||||
(** Returns database name *)
|
||||
|
||||
val patch_person : base -> iper -> (iper, iper, istr) Def.gen_person -> unit
|
||||
(** Modify/add person with the giving id in the base. New names are added
|
||||
to the patched name index for the cosidered person and for evey member of family to
|
||||
which he belongs. Modification stay blocked until call of [commit_patches]. *)
|
||||
|
||||
val patch_ascend : base -> iper -> ifam Def.gen_ascend -> unit
|
||||
(** Modify/add ascendants of a person with a giving id. Modification stay blocked until
|
||||
call of [commit_patches]. *)
|
||||
|
||||
val patch_union : base -> iper -> ifam Def.gen_union -> unit
|
||||
(** Modify/add union of a person with a giving id. Modification stay blocked until
|
||||
call of [commit_patches]. *)
|
||||
|
||||
val patch_family : base -> ifam -> (iper, ifam, istr) Def.gen_family -> unit
|
||||
(** Modify/add family with a giving id. Modification stay blocked until
|
||||
call of [commit_patches]. *)
|
||||
|
||||
val patch_descend : base -> ifam -> iper Def.gen_descend -> unit
|
||||
(** Modify/add descendants of a family with a giving id. Modification stay blocked until
|
||||
call of [commit_patches]. *)
|
||||
|
||||
val patch_couple : base -> ifam -> iper Def.gen_couple -> unit
|
||||
(** Modify/add couple of a family with a giving id. Modification stay blocked until
|
||||
call of [commit_patches]. *)
|
||||
|
||||
val insert_string : base -> string -> istr
|
||||
(** Modify/add string with a giving id. If string already exists return its id.
|
||||
Modification stay blocked until call of [commit_patches]. *)
|
||||
|
||||
val commit_patches : base -> unit
|
||||
(** Commit blocked modifications (patches) and update database files in order to
|
||||
apply modifications on the disk. *)
|
||||
|
||||
val commit_notes : base -> string -> string -> unit
|
||||
(** [commit_notes fname s] Update content of the notes/extended page file [fname] if exists. *)
|
||||
|
||||
val new_iper : base -> iper
|
||||
(** Retruns new unused person's id *)
|
||||
|
||||
val new_ifam : base -> ifam
|
||||
(** Retruns new unused family's id *)
|
||||
|
||||
val insert_person : base -> iper -> (iper, iper, istr) Def.gen_person -> unit
|
||||
(** Same as [patch_person] *)
|
||||
|
||||
val insert_ascend : base -> iper -> ifam Def.gen_ascend -> unit
|
||||
(** Same as [patch_ascend] *)
|
||||
|
||||
val insert_union : base -> iper -> ifam Def.gen_union -> unit
|
||||
(** Same as [patch_union] *)
|
||||
|
||||
val insert_family : base -> ifam -> (iper, ifam, istr) Def.gen_family -> unit
|
||||
(** Same as [patch_family] *)
|
||||
|
||||
val insert_descend : base -> ifam -> iper Def.gen_descend -> unit
|
||||
(** Same as [patch_couple] *)
|
||||
|
||||
val insert_couple : base -> ifam -> iper Def.gen_couple -> unit
|
||||
(** Same as [patch_descend] *)
|
||||
|
||||
val delete_person : base -> iper -> unit
|
||||
(** Remplace person with the giving id by bogus definition and clear
|
||||
person's data structure. *)
|
||||
|
||||
val delete_ascend : base -> iper -> unit
|
||||
(** Clear person's ascendants data structure *)
|
||||
|
||||
val delete_union : base -> iper -> unit
|
||||
(** Clear person's union data structure *)
|
||||
|
||||
val delete_family : base -> ifam -> unit
|
||||
(** Remplace family with the giving id by dummy family and clear
|
||||
family's data structure. *)
|
||||
|
||||
val delete_descend : base -> ifam -> unit
|
||||
(** Clear family's descendants data structure *)
|
||||
|
||||
val delete_couple : base -> ifam -> unit
|
||||
(** Clear family's couple data structure *)
|
||||
|
||||
val person_of_key : base -> string -> string -> int -> iper option
|
||||
(** [person_of_key first_name surname occ] returns person from his key information
|
||||
(first name, surname and occurence number) *)
|
||||
|
||||
val persons_of_name : base -> string -> iper list
|
||||
(** Return list of person ids that have giving name (could be one of the mix). *)
|
||||
|
||||
val persons_of_first_name : base -> string_person_index
|
||||
(** Returns data structure that allows to make optimised search throughout
|
||||
index by first name *)
|
||||
|
||||
val persons_of_surname : base -> string_person_index
|
||||
(** Returns data structure that allows to make optimised search throughout
|
||||
index by surname *)
|
||||
|
||||
val spi_first : string_person_index -> string -> istr
|
||||
(** Returns first [first/sur]name id starting with that string *)
|
||||
|
||||
val spi_next : string_person_index -> istr -> istr
|
||||
(** Retruns next [first/sur]name id that follows giving name's id by
|
||||
Gutil.alphabetical order *)
|
||||
|
||||
val spi_find : string_person_index -> istr -> iper list
|
||||
(** Retruns all persons id having that [first/sur]name. *)
|
||||
|
||||
val base_visible_get : base -> (person -> bool) -> iper -> bool
|
||||
(** [base_visible_get base fct ip] get visibility of person [ip] ([true] for not visible
|
||||
(restrited)) from the [base]. If file {i restrict} is present then read it to get
|
||||
visibility information. If person's visibility isn't known, then set it with [fct].
|
||||
Used when mode `use_restrict` is ativated *)
|
||||
|
||||
val base_visible_write : base -> unit
|
||||
(** Write updated visibility information to the {i restricted} file. *)
|
||||
|
||||
val base_particles : base -> Re.re
|
||||
(** Return regular expression that matches all defined in the [base] particles. *)
|
||||
|
||||
val base_strings_of_first_name : base -> string -> istr list
|
||||
(** [base_strings_of_first_name base x]
|
||||
Return the list of first names (as [istr]) being equal or to [x]
|
||||
using {!val:Name.crush_lower} comparison. [x] could be also a substring
|
||||
of the matched first name.
|
||||
*)
|
||||
|
||||
val base_strings_of_surname : base -> string -> istr list
|
||||
(** [base_strings_of_surname base x]
|
||||
Return the list of surnames (as [istr]) being equal to [x]
|
||||
using {!val:Name.crush_lower} comparison. [x] could be also a substring
|
||||
of the matched surname.
|
||||
*)
|
||||
|
||||
val load_ascends_array : base -> unit
|
||||
(** Load array of ascendants in the memory and cache it so it could be accessed
|
||||
instantly by other functions unless [clear_ascends_array] is called. *)
|
||||
|
||||
val load_unions_array : base -> unit
|
||||
(** Load array of unions in the memory and cache it so it could be accessed
|
||||
instantly by other functions unless [clear_unions_array] is called. *)
|
||||
|
||||
val load_couples_array : base -> unit
|
||||
(** Load array of couples in the memory and cache it so it could be accessed
|
||||
instantly by other functions unless [clear_couples_array] is called. *)
|
||||
|
||||
val load_descends_array : base -> unit
|
||||
(** Load array of descendants in the memory and cache it so it could be accessed
|
||||
instantly by other functions unless [clear_descends_array] is called. *)
|
||||
|
||||
val load_strings_array : base -> unit
|
||||
(** Load array of strings in the memory and cache it so it could be accessed
|
||||
instantly by other functions unless [clear_strings_array] is called. *)
|
||||
|
||||
val load_persons_array : base -> unit
|
||||
(** Load array of persons in the memory and cache it so it could be accessed
|
||||
instantly by other functions unless [clear_persons_array] is called. *)
|
||||
|
||||
val load_families_array : base -> unit
|
||||
(** Load array of families in the memory and cache it so it could be accessed
|
||||
instantly by other functions unless [clear_families_array] is called. *)
|
||||
|
||||
val clear_ascends_array : base -> unit
|
||||
(** Remove array of ascendants from the memory *)
|
||||
|
||||
val clear_unions_array : base -> unit
|
||||
(** Remove array of unions from the memory *)
|
||||
|
||||
val clear_couples_array : base -> unit
|
||||
(** Remove array of couples from the memory *)
|
||||
|
||||
val clear_descends_array : base -> unit
|
||||
(** Remove array of descendants from the memory *)
|
||||
|
||||
val clear_strings_array : base -> unit
|
||||
(** Remove array of strings from the memory *)
|
||||
|
||||
val clear_persons_array : base -> unit
|
||||
(** Remove array of persons from the memory *)
|
||||
|
||||
val clear_families_array : base -> unit
|
||||
(** Remove array of families from the memory *)
|
||||
|
||||
val base_notes_read : base -> string -> string
|
||||
(** [base_notes_read base fname] read and return content of [fname] note
|
||||
(either database note either extended page). *)
|
||||
|
||||
val base_notes_read_first_line : base -> string -> string
|
||||
(** [base_notes_read base fname] read and return first line of [fname] note *)
|
||||
|
||||
val base_notes_are_empty : base -> string -> bool
|
||||
(** Says if note has empty content *)
|
||||
|
||||
val base_notes_origin_file : base -> string
|
||||
(** Retruns origin file (.gw file) of the note *)
|
||||
|
||||
val base_notes_dir : base -> string
|
||||
(** Directory where extended pages are stored *)
|
||||
|
||||
val base_wiznotes_dir : base -> string
|
||||
(** Directory where wizard notes are stored *)
|
||||
|
||||
val date_of_last_change : base -> float
|
||||
(** Returns last modification time of the database on disk *)
|
||||
|
||||
(** Collections of elemetns *)
|
||||
module Collection : sig
|
||||
type 'a t
|
||||
(** Collections are sets of elements you want to traverse. *)
|
||||
|
||||
val length : 'a t -> int
|
||||
(** Return the number of elements of a colletion *)
|
||||
|
||||
val map : ('a -> 'b) -> 'a t -> 'b t
|
||||
(** [map fn c]
|
||||
Return a collection corresponding to [c]
|
||||
where [fn] would have been applied to each of its elements.
|
||||
*)
|
||||
|
||||
val iter : ('a -> unit) -> 'a t -> unit
|
||||
(** [iter fn c]
|
||||
Apply [fn] would have been applied to each elements of [c].
|
||||
*)
|
||||
|
||||
val iteri : (int -> 'a -> unit) -> 'a t -> unit
|
||||
(** [iter fn c]
|
||||
Apply [fn i] would have been applied to each elements of [c]
|
||||
where [i] is the index (starting with 0) of the element.
|
||||
*)
|
||||
|
||||
val fold : ?from:int -> ?until:int -> ('a -> 'b -> 'a) -> 'a -> 'b t -> 'a
|
||||
(** [fold fn acc c]
|
||||
Combine each element of [c] into a single value using [fn].
|
||||
[fn] first argument is the result computed so far as we traverse the
|
||||
collection, and second element is the current element being combined.
|
||||
[acc] is the starting combined value.
|
||||
Start at [from]-nth and finish with [until]-nth element (included).
|
||||
*)
|
||||
|
||||
val fold_until : ('a -> bool) -> ('a -> 'b -> 'a) -> 'a -> 'b t -> 'a
|
||||
(** [fold_until continue fn acc c]
|
||||
Same as [fold fn acc c], but computation stops as soon as [continue]
|
||||
is not satisfied by combined value anymore.
|
||||
*)
|
||||
|
||||
val iterator : 'a t -> unit -> 'a option
|
||||
(** [iterator c]
|
||||
Return a function returning [Some next_element] when it is called,
|
||||
or [None] if you reached the end of the collection.
|
||||
*)
|
||||
end
|
||||
|
||||
(** Markers for elements inside [Collection.t] *)
|
||||
module Marker : sig
|
||||
type ('k, 'v) t
|
||||
(** Markers are way to annotate (add extra information to) elements of a {!val:Collection.t}. *)
|
||||
|
||||
val get : ('k, 'v) t -> 'k -> 'v
|
||||
(** [get marker key]
|
||||
Return the annotation associated to [key].
|
||||
*)
|
||||
|
||||
val set : ('k, 'v) t -> 'k -> 'v -> unit
|
||||
(** [set marker key value]
|
||||
Set [value] as annotation associated to [key].
|
||||
*)
|
||||
end
|
||||
|
||||
(** {2 Useful collections} *)
|
||||
|
||||
val ipers : base -> iper Collection.t
|
||||
(** Collection of person's ids *)
|
||||
|
||||
val persons : base -> person Collection.t
|
||||
(** Collection of persons *)
|
||||
|
||||
val ifams : ?select:(ifam -> bool) -> base -> ifam Collection.t
|
||||
(** Collection of family's ids *)
|
||||
|
||||
val families : ?select:(family -> bool) -> base -> family Collection.t
|
||||
(** Collection of families *)
|
||||
|
||||
val dummy_collection : 'a -> 'a Collection.t
|
||||
(** [dummy_collection x] create a dummy collection with no element.
|
||||
[x] is only used for typing.
|
||||
Useful for placeholders or for typing purpose. *)
|
||||
|
||||
(** {2 Useful markers} *)
|
||||
|
||||
val iper_marker : iper Collection.t -> 'a -> (iper, 'a) Marker.t
|
||||
(** [iper_marker c v] create marker over collection of person's ids and initialise it
|
||||
for every element with [v] *)
|
||||
|
||||
val ifam_marker : ifam Collection.t -> 'a -> (ifam, 'a) Marker.t
|
||||
(** [ifam_marker c v] create marker over collection of family's ids and initialise it
|
||||
for every element with [v] *)
|
||||
|
||||
val dummy_marker : 'a -> 'b -> ('a, 'b) Marker.t
|
||||
(** [dummy_marker k v] create a dummy collection with no element.
|
||||
[k] and [v] are only used for typing.
|
||||
Useful for placeholders or for typing purpose. *)
|
||||
|
||||
(** {2 Database creation} *)
|
||||
|
||||
val make :
|
||||
string ->
|
||||
string list ->
|
||||
((int, int, int) Def.gen_person array
|
||||
* int Def.gen_ascend array
|
||||
* int Def.gen_union array)
|
||||
* ((int, int, int) Def.gen_family array
|
||||
* int Def.gen_couple array
|
||||
* int Def.gen_descend array)
|
||||
* string array
|
||||
* Def.base_notes ->
|
||||
base
|
||||
(** [make bname particles arrays] create a base with [bname] name and [arrays] as content. *)
|
||||
|
||||
val read_nldb : base -> (iper, ifam) Def.NLDB.t
|
||||
(** TODOOCP : doc *)
|
||||
|
||||
val write_nldb : base -> (iper, ifam) Def.NLDB.t -> unit
|
||||
|
||||
val sync : ?scratch:bool -> base -> unit
|
||||
(** [sync scratch base]
|
||||
Ensure that everything is synced on disk.
|
||||
|
||||
Depending on the backend,
|
||||
it may perform various operation such as indexes rebuilding,
|
||||
and it might be a lengthy operation.
|
||||
|
||||
Use [scratch] (default false) to sync and rebuild
|
||||
the whole database. Otherwise, only changes that occured
|
||||
since the last [sync] call are treated.
|
||||
*)
|
||||
656
lib/history.ml
Normal file
656
lib/history.ml
Normal file
@@ -0,0 +1,656 @@
|
||||
(* $Id: history.ml,v 5.14 2007-09-12 09:58:44 ddr Exp $ *)
|
||||
(* Copyright (c) 1998-2007 INRIA *)
|
||||
|
||||
open Config
|
||||
open Def
|
||||
open Gwdb
|
||||
open TemplAst
|
||||
open Util
|
||||
|
||||
(* S: Fail if conf.bname is undefined? *)
|
||||
let file_name conf =
|
||||
let bname =
|
||||
if Filename.check_suffix conf.bname ".gwb" then conf.bname
|
||||
else conf.bname ^ ".gwb"
|
||||
in
|
||||
Filename.concat (Util.bpath bname) "history"
|
||||
|
||||
(* Record history when committing updates *)
|
||||
|
||||
let ext_flags =
|
||||
[ Open_wronly; Open_append; Open_creat; Open_text; Open_nonblock ]
|
||||
|
||||
(* ********************************************************************** *)
|
||||
(* [Fonc] slash_name_of_key : string -> string -> int -> string *)
|
||||
|
||||
(* ********************************************************************** *)
|
||||
|
||||
(** [Description] : Renvoie la clé nom/prénom/occ.
|
||||
[Args] :
|
||||
- fn : string
|
||||
- sn : string
|
||||
- occ : int
|
||||
[Retour] : string
|
||||
[Rem] : Non exporté en clair hors de ce module. *)
|
||||
let slash_name_of_key fn sn occ =
|
||||
let space_to_unders = Mutil.tr ' ' '_' in
|
||||
let fn = space_to_unders (Name.lower fn) in
|
||||
let sn = space_to_unders (Name.lower sn) in
|
||||
sn ^ "/" ^ fn ^ "/" ^ string_of_int occ
|
||||
|
||||
(* ********************************************************************** *)
|
||||
(* [Fonc] diff_visibility :
|
||||
config -> base -> gen_person -> gen_person -> string array *)
|
||||
|
||||
(* ********************************************************************** *)
|
||||
|
||||
(** [Description] : Si la visibilité de la personne a changé (entre
|
||||
l'ancienne et la nouvelle), alors on revoie un tableau
|
||||
avec la nouvelle visibilité.
|
||||
[Args] :
|
||||
- conf : configuration de la base
|
||||
- base : base de donnée
|
||||
- op : la person avant les modifications
|
||||
- np : la person après les modifications
|
||||
[Retour] : string array
|
||||
[Rem] : Non exporté en clair hors de ce module. *)
|
||||
let diff_visibility conf base op np =
|
||||
let k = slash_name_of_key np.first_name np.surname np.occ in
|
||||
let empty_union = { family = [||] } in
|
||||
let empty_ascend = { parents = None; consang = Adef.fix (-1) } in
|
||||
let op = Futil.map_person_ps (fun p -> p) (Gwdb.insert_string base) op in
|
||||
let np = Futil.map_person_ps (fun p -> p) (Gwdb.insert_string base) np in
|
||||
let o_p = Gwdb.person_of_gen_person base (op, empty_ascend, empty_union) in
|
||||
let n_p = Gwdb.person_of_gen_person base (np, empty_ascend, empty_union) in
|
||||
let tmp_conf = { conf with wizard = false; friend = false } in
|
||||
let old_visibility = Util.authorized_age tmp_conf base o_p in
|
||||
let new_visibility = Util.authorized_age tmp_conf base n_p in
|
||||
if old_visibility <> new_visibility then
|
||||
[| "VISIBLE"; k; string_of_bool new_visibility |]
|
||||
else [||]
|
||||
|
||||
type kind_diff =
|
||||
| Diff_person of
|
||||
(iper, iper, string) gen_person * (iper, iper, string) gen_person
|
||||
| Diff_string of (string * string * int) * (string * string * int)
|
||||
|
||||
(* ********************************************************************** *)
|
||||
(* [Fonc] diff_key : gen_person -> gen_person -> string array *)
|
||||
|
||||
(* ********************************************************************** *)
|
||||
|
||||
(** [Description] : Si la clé de la personne a changé, alors on renvoie un
|
||||
tableau avec l'ancienne clé et la nouvelle clé.
|
||||
[Args] :
|
||||
- op : la person avant les modifications
|
||||
- np : la person après les modifications
|
||||
[Retour] : string array
|
||||
[Rem] : Non exporté en clair hors de ce module. *)
|
||||
let diff_key d =
|
||||
match d with
|
||||
| Diff_person (op, np) ->
|
||||
let o_key = slash_name_of_key op.first_name op.surname op.occ in
|
||||
let n_key = slash_name_of_key np.first_name np.surname np.occ in
|
||||
if o_key <> n_key then [| "KEY"; o_key; n_key |] else [||]
|
||||
| Diff_string ((ofn, osn, oocc), (fn, sn, occ)) ->
|
||||
let o_key = slash_name_of_key ofn osn oocc in
|
||||
let n_key = slash_name_of_key fn sn occ in
|
||||
if o_key <> n_key then [| "KEY"; o_key; n_key |] else [||]
|
||||
|
||||
(* ********************************************************************** *)
|
||||
(* [Fonc] diff_person :
|
||||
config -> base -> gen_person -> gen_person -> string array *)
|
||||
|
||||
(* ********************************************************************** *)
|
||||
|
||||
(** [Description] : Fonction qui ajouté des paramètres passés dans la
|
||||
ligne de commande de notify_change. Elle permet de
|
||||
savoir quelle genre de modifications ont été faites.
|
||||
[Args] :
|
||||
- conf : configuration de la base
|
||||
- base : base de donnée
|
||||
- op : la person avant les modifications
|
||||
- np : la person après les modifications
|
||||
[Retour] : string array
|
||||
[Rem] : Non exporté en clair hors de ce module. *)
|
||||
let diff_person conf base changed =
|
||||
match changed with
|
||||
| U_Add_person _ | U_Delete_person _ -> [||]
|
||||
| U_Modify_person (o, n) ->
|
||||
Array.append
|
||||
(diff_key (Diff_person (o, n)))
|
||||
(diff_visibility conf base o n)
|
||||
| U_Merge_person (p1, p2, p) ->
|
||||
let args_p1 =
|
||||
Array.append
|
||||
(diff_key (Diff_person (p1, p)))
|
||||
(diff_visibility conf base p1 p)
|
||||
in
|
||||
let args_p2 =
|
||||
Array.append
|
||||
(diff_key (Diff_person (p2, p)))
|
||||
(diff_visibility conf base p2 p)
|
||||
in
|
||||
Array.append args_p1 args_p2
|
||||
| U_Send_image _ | U_Delete_image _
|
||||
| U_Add_family (_, _)
|
||||
| U_Modify_family (_, _, _)
|
||||
| U_Delete_family (_, _)
|
||||
| U_Invert_family (_, _)
|
||||
| U_Merge_family (_, _, _, _)
|
||||
| U_Add_parent (_, _) ->
|
||||
[||]
|
||||
| U_Change_children_name (_, l) ->
|
||||
List.fold_left
|
||||
(fun accu ((ofn, osn, oocc, _), (fn, sn, occ, _)) ->
|
||||
Array.append accu
|
||||
(diff_key (Diff_string ((ofn, osn, oocc), (fn, sn, occ)))))
|
||||
[||] l
|
||||
| U_Multi (o, n, _) -> diff_key (Diff_person (o, n))
|
||||
| U_Notes (_, _) | U_Kill_ancestors _ -> [||]
|
||||
|
||||
(* ************************************************************************ *)
|
||||
(* [Fonc] notify_change : config -> base -> base_changed -> string -> unit *)
|
||||
|
||||
(* ************************************************************************ *)
|
||||
|
||||
(** [Description] : Appel le script défini par la variable notify_change du
|
||||
fichier gwf.
|
||||
[Args] :
|
||||
- conf : configuration de la base
|
||||
- base : base de donnée
|
||||
- changed : le type de modification (voir def.mli)
|
||||
- action : le code du type de modification
|
||||
[Retour] : Néant
|
||||
[Rem] : Non exporté en clair hors de ce module. *)
|
||||
let notify_change conf base changed action =
|
||||
if Sys.unix then
|
||||
match List.assoc_opt "notify_change" conf.base_env with
|
||||
| Some comm -> (
|
||||
let base_args =
|
||||
match changed with
|
||||
| U_Add_person p
|
||||
| U_Modify_person (_, p)
|
||||
| U_Delete_person p
|
||||
| U_Merge_person (_, _, p)
|
||||
| U_Send_image p
|
||||
| U_Delete_image p
|
||||
| U_Add_family (p, _)
|
||||
| U_Modify_family (p, _, _)
|
||||
| U_Delete_family (p, _)
|
||||
| U_Invert_family (p, _)
|
||||
| U_Merge_family (p, _, _, _)
|
||||
| U_Add_parent (p, _)
|
||||
| U_Kill_ancestors p
|
||||
| U_Change_children_name (p, _)
|
||||
| U_Multi (_, p, _) ->
|
||||
let key = slash_name_of_key p.first_name p.surname p.occ in
|
||||
[| key; string_of_iper p.key_index |]
|
||||
| U_Notes (Some num, file) -> [| file; string_of_int num |]
|
||||
| U_Notes (None, file) -> [| file |]
|
||||
in
|
||||
let optional_args = diff_person conf base changed in
|
||||
let args = Array.append base_args optional_args in
|
||||
let args =
|
||||
Array.append [| comm; conf.bname; conf.user; action |] args
|
||||
in
|
||||
match Unix.fork () with
|
||||
| 0 ->
|
||||
if Unix.fork () <> 0 then exit 0
|
||||
else (
|
||||
(try Unix.execvp comm args with _ -> ());
|
||||
exit 0)
|
||||
| id -> ignore (Unix.waitpid [] id))
|
||||
| None -> ()
|
||||
|
||||
(* ************************************************************************ *)
|
||||
(* [Fonc] gen_record : config -> base -> base_changed -> string -> unit *)
|
||||
|
||||
(* ************************************************************************ *)
|
||||
|
||||
(** [Description] : Enregistre dans le fichier historique si la variable
|
||||
"hitory" du fichier gwf est valorisée à "yes". Le fait qu'on ait des
|
||||
gen_person, nous permet de pouvoir faire un diff entre avant et après
|
||||
la modification d'une personne.
|
||||
[Args] :
|
||||
- conf : configuration de la base
|
||||
- base : base de donnée
|
||||
- changed : le type de modification (voir def.mli)
|
||||
- action : le code du type de modification
|
||||
[Retour] : Néant
|
||||
[Rem] : Non exporté en clair hors de ce module. *)
|
||||
let gen_record conf base changed action =
|
||||
(match List.assoc_opt "history" conf.base_env with
|
||||
| Some "yes" when not conf.manitou -> (
|
||||
let item =
|
||||
match changed with
|
||||
| U_Add_person p
|
||||
| U_Modify_person (_, p)
|
||||
| U_Delete_person p
|
||||
| U_Merge_person (_, _, p)
|
||||
| U_Send_image p
|
||||
| U_Delete_image p
|
||||
| U_Add_family (p, _)
|
||||
| U_Modify_family (p, _, _)
|
||||
| U_Delete_family (p, _)
|
||||
| U_Invert_family (p, _)
|
||||
| U_Merge_family (p, _, _, _)
|
||||
| U_Add_parent (p, _)
|
||||
| U_Kill_ancestors p
|
||||
| U_Change_children_name (p, _)
|
||||
| U_Multi (_, p, _) ->
|
||||
p.first_name ^ "." ^ string_of_int p.occ ^ " " ^ p.surname
|
||||
| U_Notes (Some num, file) ->
|
||||
let s = string_of_int num in
|
||||
if file = "" then s else file ^ "/" ^ s
|
||||
| U_Notes (None, file) -> file
|
||||
in
|
||||
let fname = file_name conf in
|
||||
match
|
||||
try Some (Secure.open_out_gen ext_flags 0o644 fname)
|
||||
with Sys_error _ -> None
|
||||
with
|
||||
| Some oc ->
|
||||
Printf.fprintf oc "%s [%s] %s %s\n"
|
||||
(Util.sprintf_today conf :> string)
|
||||
conf.user action item;
|
||||
close_out oc
|
||||
| None -> ())
|
||||
| _ -> ());
|
||||
HistoryDiff.record_diff conf base changed;
|
||||
(* Effet de bord des modifications en masse : on peut facilement *)
|
||||
(* créer 5000 nouveaux processus à chaque mise à jour. *)
|
||||
(* Pour éviter cela, on n'appelle jamais notify_change lors de la *)
|
||||
(* mise à jour de l'historique. *)
|
||||
match changed with
|
||||
| U_Multi (_, _, _) -> ()
|
||||
| _ -> notify_change conf base changed action
|
||||
|
||||
(* ************************************************************************ *)
|
||||
(* [Fonc] record : config -> base -> base_changed -> string -> unit *)
|
||||
|
||||
(* ************************************************************************ *)
|
||||
|
||||
(** [Description] : Suite à la mise à jour de la base, on réalise les
|
||||
traitements suivant :
|
||||
- mise à jour (si nécessaire) du fichier gwf pour le sosa_ref
|
||||
- mise à jour du fichier historique
|
||||
- appel du script notify_change
|
||||
[Args] :
|
||||
- conf : configuration de la base
|
||||
- base : base de donnée
|
||||
- changed : le type de modification (voir def.mli)
|
||||
- action : le code du type de modification
|
||||
[Retour] : Néant
|
||||
[Rem] : Non exporté en clair hors de ce module. *)
|
||||
let record conf base changed action =
|
||||
(* Mise à jour du fichier gwf si le sosa_ref a changé. *)
|
||||
(match changed with
|
||||
| U_Modify_person (_, p) ->
|
||||
let fn, sn, occ, ip = (p.first_name, p.surname, p.occ, p.key_index) in
|
||||
update_gwf_sosa conf base (ip, (fn, sn, occ))
|
||||
| U_Merge_person (p1, _, p) ->
|
||||
let fn, sn, occ, ip = (p1.first_name, p1.surname, p1.occ, p1.key_index) in
|
||||
update_gwf_sosa conf base (ip, (fn, sn, occ));
|
||||
(* On n'a pas besoin de faire un update sur "p2" *)
|
||||
(* parce qu'on le fait sur p dans tous les cas. *)
|
||||
let fn, sn, occ, ip = (p.first_name, p.surname, p.occ, p.key_index) in
|
||||
update_gwf_sosa conf base (ip, (fn, sn, occ))
|
||||
| U_Change_children_name (_, l) ->
|
||||
List.iter
|
||||
(fun (_, (fn, sn, occ, ip)) ->
|
||||
update_gwf_sosa conf base (ip, (fn, sn, occ)))
|
||||
l
|
||||
| _ -> ());
|
||||
(* Mise à jour du fichier historique et appel de notify_change. *)
|
||||
gen_record conf base changed action
|
||||
|
||||
(* ************************************************************************ *)
|
||||
(* [Fonc] notify : config -> base -> string -> unit *)
|
||||
|
||||
(* ************************************************************************ *)
|
||||
|
||||
(** [Description] : Appel explicite de notify_change suite à une modification
|
||||
de masse de la base (typiquement, le dico des lieux).
|
||||
On évite comme ça la création de 5000 processus.
|
||||
[Args] :
|
||||
- conf : configuration de la base
|
||||
- base : base de donnée
|
||||
- action : le code du type de modification
|
||||
[Retour] : Néant
|
||||
[Rem] : Non exporté en clair hors de ce module. *)
|
||||
let notify conf base action =
|
||||
let empty_person = Gwdb.empty_person base Gwdb.dummy_iper in
|
||||
let empty_person =
|
||||
Util.string_gen_person base (gen_person_of_person empty_person)
|
||||
in
|
||||
notify_change conf base (U_Multi (empty_person, empty_person, false)) action
|
||||
|
||||
(* Request for history printing *)
|
||||
|
||||
let line_tpl = "0000-00-00 00:00:00 xx ."
|
||||
|
||||
let line_fields line =
|
||||
if String.length line > String.length line_tpl then
|
||||
let time = String.sub line 0 19 in
|
||||
let user, i =
|
||||
match (line.[20], String.index_opt line ']') with
|
||||
| '[', Some i ->
|
||||
let user = String.sub line 21 (i - 21) in
|
||||
(user, i + 2)
|
||||
| _ -> ("", 20)
|
||||
in
|
||||
let action = String.sub line i 2 in
|
||||
let key =
|
||||
let i = i + 3 in
|
||||
if i >= String.length line then None
|
||||
else Some (String.sub line i (String.length line - i))
|
||||
in
|
||||
Some (time, user, action, key)
|
||||
else None
|
||||
|
||||
type hist_item = HI_notes of string * int option | HI_ind of person | HI_none
|
||||
|
||||
type 'a env =
|
||||
| Vcnt of int ref
|
||||
| Vinfo of string * string * string * hist_item * string
|
||||
| Vpos of int ref
|
||||
| Vsearch of (bool * string * int) option
|
||||
| Vother of 'a
|
||||
| Vnone
|
||||
|
||||
let get_env v env = try List.assoc v env with Not_found -> Vnone
|
||||
let get_vother = function Vother x -> Some x | _ -> None
|
||||
let set_vother x = Vother x
|
||||
|
||||
let possibly_highlight env s =
|
||||
match get_env "search" env with
|
||||
| Vsearch (Some (case_sens, h, _)) ->
|
||||
if in_text case_sens h s then html_highlight case_sens h s else s
|
||||
| _ -> s
|
||||
|
||||
let safe_val (s : Adef.safe_string) = VVstring (s :> string)
|
||||
|
||||
let rec eval_var conf base env _ _ = function
|
||||
| [ "count" ] -> (
|
||||
match get_env "count" env with
|
||||
| Vcnt c -> VVstring (string_of_int !c)
|
||||
| _ -> VVstring "")
|
||||
| [ "first_name" ] -> (
|
||||
match get_env "info" env with
|
||||
| Vinfo (_, _, _, HI_ind p, _) -> VVstring (p_first_name base p)
|
||||
| _ -> VVstring "")
|
||||
| [ "found" ] -> (
|
||||
match get_env "search" env with
|
||||
| Vsearch (Some _) -> VVbool true
|
||||
| _ -> VVbool false)
|
||||
| [ "incr_count" ] -> (
|
||||
match get_env "count" env with
|
||||
| Vcnt c ->
|
||||
incr c;
|
||||
VVstring ""
|
||||
| _ -> VVstring "")
|
||||
| [ "is_note" ] -> (
|
||||
match get_env "info" env with
|
||||
| Vinfo (_, _, _, HI_notes (_, _), _) -> VVbool true
|
||||
| _ -> VVbool false)
|
||||
| [ "key" ] -> (
|
||||
match get_env "info" env with
|
||||
| Vinfo (_, _, _, _, s) -> VVstring (possibly_highlight env s)
|
||||
| _ -> raise Not_found)
|
||||
| "note" :: "page" :: sl -> (
|
||||
match get_env "info" env with
|
||||
| Vinfo (_, _, _, HI_notes (s, _), _) ->
|
||||
let s =
|
||||
match sl with
|
||||
| [ "v" ] -> s
|
||||
| [] -> possibly_highlight env s
|
||||
| _ -> raise Not_found
|
||||
in
|
||||
VVstring s
|
||||
| _ -> raise Not_found)
|
||||
| [ "note"; "part" ] -> (
|
||||
match get_env "info" env with
|
||||
| Vinfo (_, _, _, HI_notes (_, Some x), _) -> VVstring (string_of_int x)
|
||||
| Vinfo (_, _, _, HI_notes (_, None), _) -> VVstring ""
|
||||
| _ -> raise Not_found)
|
||||
| [ "occ" ] -> (
|
||||
match get_env "info" env with
|
||||
| Vinfo (_, _, _, HI_ind p, _) -> VVstring (string_of_int (get_occ p))
|
||||
| _ -> VVstring "")
|
||||
| "person" :: sl -> (
|
||||
match get_env "info" env with
|
||||
| Vinfo (_, _, _, HI_ind p, _) -> eval_person_field_var conf base env p sl
|
||||
| _ -> raise Not_found)
|
||||
| [ "pos" ] -> (
|
||||
match get_env "pos" env with
|
||||
| Vpos r -> VVstring (string_of_int !r)
|
||||
| _ -> raise Not_found)
|
||||
| [ "reset_count" ] -> (
|
||||
match get_env "count" env with
|
||||
| Vcnt c ->
|
||||
c := 0;
|
||||
VVstring ""
|
||||
| _ -> VVstring "")
|
||||
| [ "surname" ] -> (
|
||||
match get_env "info" env with
|
||||
| Vinfo (_, _, _, HI_ind p, _) -> VVstring (p_surname base p)
|
||||
| _ -> VVstring "")
|
||||
| [ "time" ] -> (
|
||||
match get_env "info" env with
|
||||
| Vinfo (s, _, _, _, _) -> VVstring (possibly_highlight env s)
|
||||
| _ -> raise Not_found)
|
||||
| "update" :: sl -> (
|
||||
match get_env "info" env with
|
||||
| Vinfo (_, u, _, _, _) -> eval_string u sl
|
||||
| _ -> raise Not_found)
|
||||
| "user" :: sl -> (
|
||||
match get_env "info" env with
|
||||
| Vinfo (_, _, s, _, _) ->
|
||||
let s =
|
||||
match sl with
|
||||
| [ "v" ] -> s
|
||||
| [] -> possibly_highlight env s
|
||||
| _ -> raise Not_found
|
||||
in
|
||||
VVstring s
|
||||
| _ -> raise Not_found)
|
||||
| _ -> raise Not_found
|
||||
|
||||
and eval_string s = function
|
||||
| [ "var" ] -> VVother (eval_string s)
|
||||
| [] -> VVstring s
|
||||
| _ -> raise Not_found
|
||||
|
||||
and eval_person_field_var conf base env p = function
|
||||
| [ "access" ] -> safe_val (Util.acces conf base p :> Adef.safe_string)
|
||||
| [ "dates" ] -> safe_val (DateDisplay.short_dates_text conf base p)
|
||||
| [ "has_history" ] ->
|
||||
let fn = sou base (get_first_name p) in
|
||||
let sn = sou base (get_surname p) in
|
||||
let occ = get_occ p in
|
||||
let person_file = HistoryDiff.history_file fn sn occ in
|
||||
VVbool (Sys.file_exists (HistoryDiff.history_path conf person_file))
|
||||
| [ "history_file" ] ->
|
||||
let fn = sou base (get_first_name p) in
|
||||
let sn = sou base (get_surname p) in
|
||||
let occ = get_occ p in
|
||||
VVstring (HistoryDiff.history_file fn sn occ)
|
||||
| [ "is_invisible" ] ->
|
||||
let conf = { conf with wizard = false; friend = false } in
|
||||
VVbool (not (Util.authorized_age conf base p))
|
||||
| [ "title" ] -> safe_val (person_title conf base p)
|
||||
| [] ->
|
||||
VVstring
|
||||
(possibly_highlight env
|
||||
(simple_person_text conf base p : Adef.safe_string :> string))
|
||||
| _ -> VVstring "person..."
|
||||
|
||||
and simple_person_text conf base p =
|
||||
match main_title conf base p with
|
||||
| Some t -> titled_person_text conf base p t
|
||||
| None -> gen_person_text conf base p
|
||||
|
||||
let print_foreach conf base print_ast eval_expr =
|
||||
let eval_int_expr env ep e =
|
||||
let s = eval_expr env ep e in
|
||||
try int_of_string s with Failure _ -> raise Not_found
|
||||
in
|
||||
let rec print_foreach env xx _ s sl el al =
|
||||
match (s, sl) with
|
||||
| "history_line", [] -> print_foreach_history_line env xx el al
|
||||
| _, _ -> raise Not_found
|
||||
and print_foreach_history_line env xx el al =
|
||||
match
|
||||
try Some (Secure.open_in_bin (file_name conf)) with Sys_error _ -> None
|
||||
with
|
||||
| Some ic -> (
|
||||
try
|
||||
let k, pos, wiz =
|
||||
match el with
|
||||
| [ [ e1 ]; [ e2 ]; [ e3 ] ] ->
|
||||
let k = eval_int_expr env xx e1 in
|
||||
let pos =
|
||||
match get_env "search" env with
|
||||
| Vsearch (Some (_, _, pos)) -> pos
|
||||
| Vsearch None -> in_channel_length ic
|
||||
| _ -> (
|
||||
try eval_int_expr env xx e2
|
||||
with Not_found -> in_channel_length ic)
|
||||
in
|
||||
let wiz = eval_expr env xx e3 in
|
||||
(k, pos, wiz)
|
||||
| [] -> (3, in_channel_length ic, "")
|
||||
| _ -> raise Not_found
|
||||
in
|
||||
let pos =
|
||||
let vv = (ref (Bytes.create 0), ref 0) in
|
||||
let rec loop pos i =
|
||||
if i >= k then pos
|
||||
else
|
||||
match
|
||||
try Some (Mutil.rev_input_line ic pos vv)
|
||||
with End_of_file -> None
|
||||
with
|
||||
| Some (line, pos) ->
|
||||
let i = print_history_line2 env xx line wiz i al in
|
||||
loop pos i
|
||||
| None -> pos
|
||||
in
|
||||
loop pos 0
|
||||
in
|
||||
(match get_env "pos" env with Vpos r -> r := pos | _ -> ());
|
||||
close_in ic
|
||||
with e ->
|
||||
close_in ic;
|
||||
raise e)
|
||||
| None -> ()
|
||||
and print_history_line2 env xx line wiz i al =
|
||||
match line_fields line with
|
||||
| Some (time, user, action, keyo) ->
|
||||
if wiz = "" || user = wiz then (
|
||||
let hist_item =
|
||||
match keyo with
|
||||
| Some key -> (
|
||||
match action with
|
||||
| "mn" -> (
|
||||
let i, j =
|
||||
try
|
||||
let i = String.rindex key '/' in
|
||||
(i, i + 1)
|
||||
with Not_found -> (0, 0)
|
||||
in
|
||||
let pg = String.sub key 0 i in
|
||||
let s = String.sub key j (String.length key - j) in
|
||||
try HI_notes (pg, Some (int_of_string s))
|
||||
with Failure _ -> HI_notes (key, None))
|
||||
| _ -> (
|
||||
match Gutil.person_ht_find_all base key with
|
||||
| [ ip ] -> HI_ind (pget conf base ip)
|
||||
| _ -> HI_none))
|
||||
| None -> HI_none
|
||||
in
|
||||
let not_displayed =
|
||||
match hist_item with
|
||||
| HI_ind p ->
|
||||
is_hidden p
|
||||
|| (is_hide_names conf p && not (authorized_age conf base p))
|
||||
| _ -> false
|
||||
in
|
||||
if not_displayed then i
|
||||
else
|
||||
let key = match keyo with Some s -> s | None -> "" in
|
||||
let env =
|
||||
("info", Vinfo (time, action, user, hist_item, key)) :: env
|
||||
in
|
||||
List.iter (print_ast env xx) al;
|
||||
i + 1)
|
||||
else i
|
||||
| None -> i
|
||||
in
|
||||
print_foreach
|
||||
|
||||
let gen_print conf base hoo =
|
||||
let env =
|
||||
let env = [ ("pos", Vpos (ref 0)); ("count", Vcnt (ref 0)) ] in
|
||||
match hoo with Some ho -> ("search", Vsearch ho) :: env | None -> env
|
||||
in
|
||||
Hutil.interp conf "updhist"
|
||||
{
|
||||
Templ.eval_var = eval_var conf base;
|
||||
Templ.eval_transl = (fun _ -> Templ.eval_transl conf);
|
||||
Templ.eval_predefined_apply = (fun _ -> raise Not_found);
|
||||
Templ.get_vother;
|
||||
Templ.set_vother;
|
||||
Templ.print_foreach = print_foreach conf base;
|
||||
}
|
||||
env ()
|
||||
|
||||
let print conf base = gen_print conf base None
|
||||
|
||||
(* searching *)
|
||||
|
||||
let search_text conf base s =
|
||||
let s = if s = "" then " " else s in
|
||||
let case_sens = p_getenv conf.env "c" = Some "on" in
|
||||
let found =
|
||||
match
|
||||
try Some (Secure.open_in_bin (file_name conf)) with Sys_error _ -> None
|
||||
with
|
||||
| Some ic ->
|
||||
let pos =
|
||||
match p_getint conf.env "pos" with
|
||||
| Some pos -> pos
|
||||
| None -> in_channel_length ic
|
||||
in
|
||||
let vv = (ref (Bytes.create 0), ref 0) in
|
||||
let rec loop pos =
|
||||
match
|
||||
try Some (Mutil.rev_input_line ic pos vv) with End_of_file -> None
|
||||
with
|
||||
| Some (line, pos2) -> (
|
||||
match line_fields line with
|
||||
| Some (time, user, _, keyo) ->
|
||||
let key = match keyo with Some key -> key | None -> "" in
|
||||
if
|
||||
in_text case_sens s time || in_text case_sens s user
|
||||
|| in_text case_sens s key
|
||||
then Some pos
|
||||
else loop pos2
|
||||
| None -> None)
|
||||
| None -> None
|
||||
in
|
||||
loop pos
|
||||
| None -> None
|
||||
in
|
||||
let h =
|
||||
match found with Some pos -> Some (case_sens, s, pos) | None -> None
|
||||
in
|
||||
gen_print conf base (Some h)
|
||||
|
||||
let print_search conf base =
|
||||
if conf.wizard || conf.friend then
|
||||
match try Some (List.assoc "s" conf.env) with Not_found -> None with
|
||||
| Some s -> search_text conf base (Mutil.gen_decode false s)
|
||||
| None -> print conf base
|
||||
else print conf base
|
||||
40
lib/history.mli
Normal file
40
lib/history.mli
Normal file
@@ -0,0 +1,40 @@
|
||||
(* $Id: history.mli,v 5.4 2007-01-19 01:53:16 ddr Exp $ *)
|
||||
(* Copyright (c) 1998-2007 INRIA *)
|
||||
|
||||
open Config
|
||||
open Def
|
||||
open Gwdb
|
||||
|
||||
val file_name : config -> string
|
||||
(** Returns path to the file where history of current base updates is stored *)
|
||||
|
||||
val record :
|
||||
config -> base -> (iper, iper, ifam, string) base_changed -> string -> unit
|
||||
(** [record conf change action] records new modification in the history files
|
||||
(global file and specific for each concerned by modification person).
|
||||
Additionally it does:
|
||||
|
||||
- Updates [conf.default_sosa_ref] if concered by modification person is referenced by default_sosa_ref
|
||||
- Notify foreign {i notify_change} about modification on the base
|
||||
(doesn't notify if multiple modifications are done succesively) *)
|
||||
|
||||
val notify : config -> base -> string -> unit
|
||||
(** [notify conf base action]
|
||||
Explicit notification of foreign script {i notify_change}
|
||||
that modification action [action] was executed on the database.
|
||||
Since [record] already does notify script about unary modification on the base,
|
||||
this function is used exclusively to send notification about multiple
|
||||
modifications and avoid creating indefinite amount of processes for each modification
|
||||
(for example for each concerned person in the list of modified persons). *)
|
||||
|
||||
val print : config -> base -> unit
|
||||
(** Displays an history of updates *)
|
||||
|
||||
val print_search : config -> base -> unit
|
||||
(** Same as `print`, but simultaneously searches for text inside the history and higlhight all found matches.
|
||||
Search pattern is available with {i s} variable in environement [conf.env]. *)
|
||||
|
||||
(* Useful stuff for who (i.e. plugins) wants to read history file. *)
|
||||
|
||||
val line_fields : string -> (string * string * string * string option) option
|
||||
(** Parses one line of history file that delimits one modification record. *)
|
||||
292
lib/historyDiff.ml
Normal file
292
lib/historyDiff.ml
Normal file
@@ -0,0 +1,292 @@
|
||||
(* Copyright (c) 1998-2007 INRIA *)
|
||||
|
||||
open Config
|
||||
open Def
|
||||
open Gwdb
|
||||
|
||||
type gen_record = {
|
||||
date : Adef.safe_string;
|
||||
wizard : Adef.safe_string;
|
||||
gen_p : (iper, iper, string) gen_person;
|
||||
gen_f : (iper, ifam, string) gen_family list;
|
||||
gen_c : iper array list;
|
||||
}
|
||||
|
||||
(* Le nom du fichier historique (à partir de la clé personne). *)
|
||||
let history_file fn sn occ =
|
||||
let space_to_unders = Mutil.tr ' ' '_' in
|
||||
let f = space_to_unders (Name.lower fn) in
|
||||
let s = space_to_unders (Name.lower sn) in
|
||||
f ^ "." ^ string_of_int occ ^ "." ^ s
|
||||
|
||||
(* history directory path *)
|
||||
let history_d conf =
|
||||
let path =
|
||||
match List.assoc_opt "history_path" conf.base_env with
|
||||
| Some path when path <> "" -> path
|
||||
| _ -> "history_d"
|
||||
in
|
||||
if Filename.is_relative path then
|
||||
let bname =
|
||||
if Filename.check_suffix conf.bname ".gwb" then conf.bname
|
||||
else conf.bname ^ ".gwb"
|
||||
in
|
||||
Filename.concat (Util.bpath bname) path
|
||||
else path
|
||||
|
||||
(* Le chemin du fichier historique dans le dossier history_d. *)
|
||||
let history_path conf fname =
|
||||
if String.length fname >= 6 then
|
||||
let dirs =
|
||||
[ history_d conf; String.make 1 fname.[0]; String.make 1 fname.[1] ]
|
||||
in
|
||||
List.fold_right Filename.concat dirs fname
|
||||
else Filename.concat (history_d conf) fname
|
||||
|
||||
(* Créé tous les dossiers intermédiaires. *)
|
||||
let create_history_dirs conf fname =
|
||||
if String.length fname >= 6 then
|
||||
let dirs =
|
||||
[ history_d conf; String.make 1 fname.[0]; String.make 1 fname.[1] ]
|
||||
in
|
||||
Mutil.mkdir_p (List.fold_left Filename.concat "" dirs)
|
||||
|
||||
(* ************************************************************************ *)
|
||||
(* [Fonc] write_history_file : config -> string -> gen_record -> unit *)
|
||||
|
||||
(* ************************************************************************ *)
|
||||
|
||||
(** [Description] : Enregistre la personne dans son fichier historique.
|
||||
[Args] :
|
||||
- fname : le chemin du fichier
|
||||
- gr : le contenu de la personne
|
||||
[Retour] : Néant
|
||||
[Rem] : Non exporté en clair hors de ce module. *)
|
||||
let write_history_file conf person_file fname gr =
|
||||
(* On créé toujours les dossiers nécessaires (changement de clé ...). *)
|
||||
let () = create_history_dirs conf person_file in
|
||||
let ext_flags =
|
||||
[ Open_wronly; Open_append; Open_creat; Open_binary; Open_nonblock ]
|
||||
in
|
||||
match
|
||||
try Some (Secure.open_out_gen ext_flags 0o644 fname)
|
||||
with Sys_error _ -> None
|
||||
with
|
||||
| Some oc ->
|
||||
output_value oc (gr : gen_record);
|
||||
close_out oc
|
||||
| None -> ()
|
||||
|
||||
(* ************************************************************************ *)
|
||||
(* [Fonc] make_gen_record :
|
||||
config -> base -> bool -> gen_person -> gen_record *)
|
||||
|
||||
(* ************************************************************************ *)
|
||||
|
||||
(** [Description] : Crée un gen_record à partir d'une personne.
|
||||
[Args] :
|
||||
- conf : configuratino de la base
|
||||
- base : base de donnée
|
||||
- first : booléen pour savoir si c'est la première entrée de
|
||||
l'historique. Si c'est le cas, on ne connait pas la date de
|
||||
modification, donc on met "environ" une seconde avant.
|
||||
- gen_p : gen_person
|
||||
[Retour] :
|
||||
- gen_record
|
||||
[Rem] : Non exporté en clair hors de ce module. *)
|
||||
let make_gen_record conf base first gen_p =
|
||||
let date =
|
||||
let conf =
|
||||
(* On évite les calculs savant pour la date (ss - 1 avec une date *)
|
||||
(* autour de minuit ...). C'est simplement une indication. *)
|
||||
if first then
|
||||
let hh, mm, ss = conf.time in
|
||||
{ conf with time = (hh, mm, min 0 ss) }
|
||||
else conf
|
||||
in
|
||||
Util.sprintf_today conf
|
||||
in
|
||||
let p = poi base gen_p.key_index in
|
||||
let fam = get_family p in
|
||||
(* On fait en sorte qu'il y a une 'bijection' *)
|
||||
(* entre les familles et les enfants. *)
|
||||
let gen_f, gen_c =
|
||||
Array.fold_right
|
||||
(fun ifam (accu_fam, accu_child) ->
|
||||
let fam = foi base ifam in
|
||||
let children = get_children fam in
|
||||
let gen_f = gen_family_of_family fam in
|
||||
(Util.string_gen_family base gen_f :: accu_fam, children :: accu_child))
|
||||
fam ([], [])
|
||||
in
|
||||
{
|
||||
date;
|
||||
wizard = (Util.escape_html conf.user :> Adef.safe_string);
|
||||
gen_p;
|
||||
gen_f;
|
||||
gen_c;
|
||||
}
|
||||
|
||||
(* ************************************************************************ *)
|
||||
(* [Fonc] record_diff : config -> base -> base_changed -> unit *)
|
||||
|
||||
(* ************************************************************************ *)
|
||||
|
||||
(** [Description] : Met à jour le fichier historique d'une personne.
|
||||
[Args] :
|
||||
- conf : configuration de la base
|
||||
- base : base de donnée
|
||||
- changed : le type de modification (voir def.mli)
|
||||
[Retour] : Néant
|
||||
[Rem] : Exporté en clair hors de ce module. *)
|
||||
let record_diff conf base changed =
|
||||
match List.assoc_opt "history_diff" conf.base_env with
|
||||
| Some "yes" when not conf.manitou -> (
|
||||
let print_ind_add p =
|
||||
let person_file = history_file p.first_name p.surname p.occ in
|
||||
let fname = history_path conf person_file in
|
||||
let gr = make_gen_record conf base false p in
|
||||
write_history_file conf person_file fname gr
|
||||
in
|
||||
let print_ind_mod o p =
|
||||
let o_person_file = history_file o.first_name o.surname o.occ in
|
||||
let person_file = history_file p.first_name p.surname p.occ in
|
||||
let ofname = history_path conf o_person_file in
|
||||
let fname = history_path conf person_file in
|
||||
(* La clé a changé, on reprend l'ancien historique. *)
|
||||
(if o_person_file <> person_file && Sys.file_exists ofname then
|
||||
try
|
||||
let () = create_history_dirs conf person_file in
|
||||
Sys.rename ofname fname
|
||||
with Sys_error _ -> ());
|
||||
let gr = make_gen_record conf base false p in
|
||||
if Sys.file_exists fname then
|
||||
write_history_file conf person_file fname gr
|
||||
else
|
||||
let o_gr = make_gen_record conf base true o in
|
||||
write_history_file conf person_file fname o_gr;
|
||||
write_history_file conf person_file fname gr
|
||||
in
|
||||
match changed with
|
||||
| U_Add_person p -> print_ind_add p
|
||||
| U_Modify_person (o, p) -> print_ind_mod o p
|
||||
| U_Delete_person _ -> ()
|
||||
| U_Merge_person (_, o, p) ->
|
||||
let o_person_file = history_file o.first_name o.surname o.occ in
|
||||
let person_file = history_file p.first_name p.surname p.occ in
|
||||
let fname = history_path conf person_file in
|
||||
let gr = make_gen_record conf base false p in
|
||||
(* La clé a changé avec la fusion, on reprend l'ancien historique. *)
|
||||
if o_person_file <> person_file then (
|
||||
let ofname = history_path conf o_person_file in
|
||||
(try
|
||||
let () = create_history_dirs conf person_file in
|
||||
Sys.rename ofname fname
|
||||
with Sys_error _ -> ());
|
||||
write_history_file conf person_file fname gr)
|
||||
else write_history_file conf person_file fname gr
|
||||
| U_Delete_family (_p, _f) -> ()
|
||||
| U_Add_family (p, f)
|
||||
| U_Modify_family (p, _, f)
|
||||
| U_Merge_family (p, _, _, f)
|
||||
| U_Add_parent (p, f) ->
|
||||
let p_file = history_file p.first_name p.surname p.occ in
|
||||
let p_fname = history_path conf p_file in
|
||||
let cpl = foi base f.fam_index in
|
||||
let isp = Gutil.spouse p.key_index cpl in
|
||||
let sp = poi base isp in
|
||||
let sp_file =
|
||||
history_file
|
||||
(sou base (get_first_name sp))
|
||||
(sou base (get_surname sp))
|
||||
(get_occ sp)
|
||||
in
|
||||
let sp_fname = history_path conf sp_file in
|
||||
let gen_sp = gen_person_of_person sp in
|
||||
let gen_sp = Util.string_gen_person base gen_sp in
|
||||
let gr = make_gen_record conf base false p in
|
||||
write_history_file conf p_file p_fname gr;
|
||||
let gr = make_gen_record conf base false gen_sp in
|
||||
write_history_file conf sp_file sp_fname gr;
|
||||
(* Création des fichiers pour les enfants ajoutés. *)
|
||||
Array.iter
|
||||
(fun ip ->
|
||||
let p = poi base ip in
|
||||
let person_file =
|
||||
history_file
|
||||
(sou base (get_first_name p))
|
||||
(sou base (get_surname p))
|
||||
(get_occ p)
|
||||
in
|
||||
let fname = history_path conf person_file in
|
||||
if Sys.file_exists fname then ()
|
||||
else
|
||||
let gen_p = gen_person_of_person p in
|
||||
let gen_p = Util.string_gen_person base gen_p in
|
||||
let gr = make_gen_record conf base false gen_p in
|
||||
write_history_file conf person_file fname gr)
|
||||
(get_children cpl)
|
||||
| U_Change_children_name (_, list) ->
|
||||
List.iter
|
||||
(fun ((ofn, osn, oocc, _oip), (fn, sn, occ, ip)) ->
|
||||
let o_person_file = history_file ofn osn oocc in
|
||||
let person_file = history_file fn sn occ in
|
||||
if o_person_file <> person_file then (
|
||||
let ofname = history_path conf o_person_file in
|
||||
let fname = history_path conf person_file in
|
||||
(try Sys.rename ofname fname with Sys_error _ -> ());
|
||||
let p = poi base ip in
|
||||
let p =
|
||||
Futil.map_person_ps
|
||||
(fun p -> p)
|
||||
(sou base) (gen_person_of_person p)
|
||||
in
|
||||
let gr = make_gen_record conf base false p in
|
||||
write_history_file conf person_file fname gr))
|
||||
list
|
||||
| U_Multi (o, p, modified_key) ->
|
||||
if modified_key then print_ind_mod o p else print_ind_add p
|
||||
| _ -> ())
|
||||
| _ -> ()
|
||||
|
||||
(* avec zip ? *)
|
||||
(*
|
||||
let history = ref [] in
|
||||
let fname = history_path conf fname in
|
||||
if extract_zfile fname then
|
||||
do {
|
||||
read_history_file fname
|
||||
Sys.remove fname
|
||||
}
|
||||
else ();
|
||||
history.val
|
||||
*)
|
||||
|
||||
(* ************************************************************************ *)
|
||||
(* [Fonc] load_person_history : config -> string -> gen_record list *)
|
||||
|
||||
(* ************************************************************************ *)
|
||||
|
||||
(** [Description] : Charge la liste des modifications pour une personne.
|
||||
L'avantage est que les versions les plus récentes se trouvent en
|
||||
tête de liste.
|
||||
[Args] :
|
||||
- conf : configuration de la base
|
||||
- fname : le nom du fichier historique
|
||||
[Retour] :
|
||||
- gen_record list
|
||||
[Rem] : Non exporté en clair hors de ce module. *)
|
||||
let load_person_history conf fname =
|
||||
let history = ref [] in
|
||||
let fname = history_path conf fname in
|
||||
(match try Some (Secure.open_in_bin fname) with Sys_error _ -> None with
|
||||
| Some ic ->
|
||||
(try
|
||||
while true do
|
||||
let v : gen_record = input_value ic in
|
||||
history := v :: !history
|
||||
done
|
||||
with End_of_file -> ());
|
||||
close_in ic
|
||||
| None -> ());
|
||||
!history
|
||||
27
lib/historyDiff.mli
Normal file
27
lib/historyDiff.mli
Normal file
@@ -0,0 +1,27 @@
|
||||
type gen_record = {
|
||||
date : Adef.safe_string;
|
||||
wizard : Adef.safe_string;
|
||||
gen_p : (Gwdb.iper, Gwdb.iper, string) Def.gen_person;
|
||||
gen_f : (Gwdb.iper, Gwdb.ifam, string) Def.gen_family list;
|
||||
gen_c : Gwdb.iper array list;
|
||||
}
|
||||
(** Type that represents one update record stored in the history file for concerned person. *)
|
||||
|
||||
val history_file : string -> string -> int -> string
|
||||
(** Returns history filename for the person with the given key.
|
||||
Has format : {i firstname.occ.surname} *)
|
||||
|
||||
val history_path : Config.config -> string -> string
|
||||
(** Returns path to the history file inside {i history_d} with given filename *)
|
||||
|
||||
val record_diff :
|
||||
Config.config ->
|
||||
Gwdb.base ->
|
||||
(Gwdb.iper, Gwdb.iper, Gwdb.ifam, string) Def.base_changed ->
|
||||
unit
|
||||
(** [record_diff conf base change] records new updated information [change]
|
||||
inside the history files of concerned by [change] persons. *)
|
||||
|
||||
val load_person_history : Config.config -> string -> gen_record list
|
||||
(** Load list of modification records for a giving person's history file.
|
||||
The most recent modification is at the head of the list *)
|
||||
961
lib/historyDiffDisplay.ml
Normal file
961
lib/historyDiffDisplay.ml
Normal file
@@ -0,0 +1,961 @@
|
||||
(* Copyright (c) 1998-2007 INRIA *)
|
||||
|
||||
open Config
|
||||
open Def
|
||||
open Gwdb
|
||||
open TemplAst
|
||||
open Util
|
||||
open HistoryDiff
|
||||
|
||||
let escape_html s = (Util.escape_html s :> Adef.safe_string)
|
||||
|
||||
let print_clean conf =
|
||||
match p_getenv conf.env "f" with
|
||||
| Some f when f <> "" ->
|
||||
let title _ =
|
||||
transl conf "clean history"
|
||||
|> Utf8.capitalize_fst |> Output.print_sstring conf
|
||||
in
|
||||
Hutil.header conf title;
|
||||
Hutil.print_link_to_welcome conf true;
|
||||
Util.gen_print_tips conf
|
||||
("select the input you want to erase from the history" |> transl conf
|
||||
|> Utf8.capitalize_fst |> Adef.safe);
|
||||
let history = load_person_history conf f in
|
||||
Output.print_sstring conf {|<form method="post" action="|};
|
||||
Output.print_sstring conf conf.command;
|
||||
Output.print_sstring conf "\">";
|
||||
Util.hidden_input conf "m" ("HIST_CLEAN_OK" |> Adef.encoded);
|
||||
Util.hidden_input conf "f" (Mutil.encode f);
|
||||
Output.print_sstring conf "<ul>";
|
||||
let rec loop i = function
|
||||
| [] -> ()
|
||||
| gr :: l ->
|
||||
Output.print_sstring conf "<li><label>";
|
||||
Output.print_sstring conf {|<input type="checkbox" name="i|};
|
||||
Output.print_sstring conf (string_of_int i);
|
||||
Output.print_sstring conf {|" value="on">|};
|
||||
Output.print_string conf gr.date;
|
||||
Output.print_sstring conf {| |};
|
||||
Output.print_string conf gr.HistoryDiff.wizard;
|
||||
Output.print_sstring conf "</label></li>";
|
||||
loop (i + 1) l
|
||||
in
|
||||
loop 0 history;
|
||||
Output.print_sstring conf
|
||||
{|</ul><button type="submit" class="btn btn-primary btn-lg">|};
|
||||
transl_nth conf "validate/delete" 0
|
||||
|> Utf8.capitalize_fst |> Output.print_sstring conf;
|
||||
Output.print_sstring conf "</button></form>";
|
||||
Hutil.trailer conf
|
||||
| _ -> Hutil.incorrect_request conf
|
||||
|
||||
(* ************************************************************************ *)
|
||||
(* [Fonc] print_clean_ok : config -> unit *)
|
||||
|
||||
(* ************************************************************************ *)
|
||||
|
||||
(** [Description] : Ré-écrit le fichier historique lié à une personne en
|
||||
ayant supprimé les entrées non désirées.
|
||||
[Args] :
|
||||
- conf : configuration de la base
|
||||
[Retour] : Néant
|
||||
[Rem] : Exporté en clair hors de ce module. *)
|
||||
let print_clean_ok conf =
|
||||
let rec clean_history i history new_history =
|
||||
match history with
|
||||
| [] -> new_history
|
||||
| gr :: l ->
|
||||
let lab = "i" ^ string_of_int i in
|
||||
if p_getenv conf.env lab = Some "on" then
|
||||
clean_history (i + 1) l new_history
|
||||
else clean_history (i + 1) l (gr :: new_history)
|
||||
in
|
||||
match p_getenv conf.env "f" with
|
||||
| Some f when f <> "" ->
|
||||
let title _ =
|
||||
transl conf "history cleaned"
|
||||
|> Utf8.capitalize_fst |> Output.print_sstring conf
|
||||
in
|
||||
Hutil.header conf title;
|
||||
Hutil.print_link_to_welcome conf true;
|
||||
let history = load_person_history conf f in
|
||||
let new_history = clean_history 0 history [] in
|
||||
let fname = history_path conf f in
|
||||
(if new_history = [] then Mutil.rm fname
|
||||
else
|
||||
let ext_flags =
|
||||
[ Open_wronly; Open_trunc; Open_creat; Open_binary; Open_nonblock ]
|
||||
in
|
||||
match
|
||||
try Some (Secure.open_out_gen ext_flags 0o644 fname)
|
||||
with Sys_error _ -> None
|
||||
with
|
||||
| Some oc ->
|
||||
List.iter (fun v -> output_value oc (v : gen_record)) new_history;
|
||||
close_out oc
|
||||
| None -> ());
|
||||
Hutil.trailer conf
|
||||
| _ -> Hutil.incorrect_request conf
|
||||
|
||||
(**/**) (* Template *)
|
||||
|
||||
let person_of_gen_p_key base gen_p =
|
||||
match person_of_key base gen_p.first_name gen_p.surname gen_p.occ with
|
||||
| Some ip -> poi base ip
|
||||
| None -> Gwdb.empty_person base Gwdb.dummy_iper
|
||||
|
||||
(* N'est pas forcément très précis. En effet, on enregistre que *)
|
||||
(* les ipers. Or lors d'un nettoyage de la base, il se peut que *)
|
||||
(* ces ipers changent. On peut donc pointer vers une autre persone. *)
|
||||
let person_of_iper conf base ip =
|
||||
try
|
||||
let p = pget conf base ip in
|
||||
if authorized_age conf base p then gen_person_text conf base p
|
||||
else Adef.safe ""
|
||||
with _ -> Adef.safe ""
|
||||
|
||||
let person_of_iper_array conf base ipl =
|
||||
(Array.fold_right
|
||||
(fun ip acc ->
|
||||
let x = person_of_iper conf base ip in
|
||||
if (x :> string) = "" then acc else x :: acc)
|
||||
ipl []
|
||||
: Adef.safe_string list
|
||||
:> string list)
|
||||
|> String.concat ", " |> Adef.safe
|
||||
|
||||
let string_of_cdate conf cod =
|
||||
match Date.od_of_cdate cod with
|
||||
| Some d -> DateDisplay.string_slash_of_date conf d
|
||||
| None -> Adef.safe ""
|
||||
|
||||
let string_of_death conf death =
|
||||
match Date.date_of_death death with
|
||||
| Some cd -> DateDisplay.string_slash_of_date conf cd
|
||||
| None -> Adef.safe ""
|
||||
|
||||
let string_of_burial conf burial =
|
||||
match burial with
|
||||
| Buried cod | Cremated cod -> string_of_cdate conf cod
|
||||
| UnknownBurial -> Adef.safe ""
|
||||
|
||||
let string_of_title conf titles : Adef.safe_string =
|
||||
let string_of_t_name t =
|
||||
match t.t_name with Tname s -> escape_html s | _ -> Adef.safe ""
|
||||
in
|
||||
let one_title t =
|
||||
let name = escape_html (t.t_ident ^ " " ^ t.t_place) in
|
||||
let name = if (name :> string) = " " then Adef.safe "" else name in
|
||||
let dates =
|
||||
string_of_cdate conf t.t_date_start
|
||||
^^^ "-"
|
||||
^<^ string_of_cdate conf t.t_date_end
|
||||
in
|
||||
let dates =
|
||||
if (dates :> string) = "-" then Adef.safe "" else "(" ^<^ dates ^>^ ")"
|
||||
in
|
||||
let nth =
|
||||
let t_name = string_of_t_name t in
|
||||
if (t_name :> string) = "" then
|
||||
Adef.safe (if t.t_nth = 0 then "" else string_of_int t.t_nth)
|
||||
else t_name ^>^ " " ^ string_of_int t.t_nth
|
||||
in
|
||||
let nth =
|
||||
if (nth :> string) = "" then Adef.safe "" else "[" ^<^ nth ^>^ "]"
|
||||
in
|
||||
name
|
||||
^^^ (if (name :> string) = "" then "" else " ")
|
||||
^<^ nth
|
||||
^^^ (if (nth :> string) = "" then "" else " ")
|
||||
^<^ dates
|
||||
in
|
||||
List.fold_left
|
||||
(fun (acc : Adef.safe_string) t ->
|
||||
if (acc :> string) = "" then one_title t else acc ^^^ ", " ^<^ one_title t)
|
||||
(Adef.safe "") titles
|
||||
|
||||
let string_of_related conf base ip related : Adef.safe_string =
|
||||
List.fold_right
|
||||
(fun ic acc ->
|
||||
let p = person_of_iper conf base ip in
|
||||
if (p :> string) = "" then acc
|
||||
else
|
||||
let c = try pget conf base ic with _ -> Gwdb.empty_person base ic in
|
||||
let rel =
|
||||
let rec loop rp =
|
||||
match rp with
|
||||
| [] -> Adef.safe ""
|
||||
| r :: l -> (
|
||||
match r.r_fath with
|
||||
| Some ifath when ifath = ip ->
|
||||
Util.rchild_type_text conf r.r_type 2
|
||||
| _ -> loop l)
|
||||
in
|
||||
loop (get_rparents c)
|
||||
in
|
||||
(Utf8.capitalize_fst (rel : Adef.safe_string :> string)
|
||||
^<^ transl conf ":" ^<^ p)
|
||||
:: acc)
|
||||
related []
|
||||
|> (fun s -> String.concat ", " (s :> string list))
|
||||
|> Adef.safe
|
||||
|
||||
let string_of_rparents conf base rparents : Adef.safe_string =
|
||||
List.fold_right
|
||||
(fun rp accu ->
|
||||
match (rp.r_fath, rp.r_moth) with
|
||||
| Some ip1, Some ip2 -> (
|
||||
let rel =
|
||||
(Util.relation_type_text conf rp.r_type 2
|
||||
: Adef.safe_string
|
||||
:> string)
|
||||
|> Utf8.capitalize_fst
|
||||
in
|
||||
let fath = person_of_iper conf base ip1 in
|
||||
let moth = person_of_iper conf base ip2 in
|
||||
match ((fath :> string), (moth :> string)) with
|
||||
| "", "" -> accu
|
||||
| _, "" -> (rel ^<^ transl conf ":" ^<^ fath) :: accu
|
||||
| "", _ -> (rel ^<^ transl conf ":" ^<^ moth) :: accu
|
||||
| _, _ -> (rel ^<^ transl conf ":" ^<^ fath ^^^ ", " ^<^ moth) :: accu
|
||||
)
|
||||
| Some ip, None ->
|
||||
let p = person_of_iper conf base ip in
|
||||
if (p :> string) = "" then accu
|
||||
else
|
||||
(Utf8.capitalize_fst
|
||||
(Util.relation_type_text conf rp.r_type 2
|
||||
: Adef.safe_string
|
||||
:> string)
|
||||
^<^ transl conf ":" ^<^ p)
|
||||
:: accu
|
||||
| None, Some ip ->
|
||||
let p = person_of_iper conf base ip in
|
||||
if (p :> string) = "" then accu
|
||||
else
|
||||
(Utf8.capitalize_fst
|
||||
(Util.relation_type_text conf rp.r_type 2
|
||||
: Adef.safe_string
|
||||
:> string)
|
||||
^<^ transl conf ":" ^<^ p)
|
||||
:: accu
|
||||
| None, None -> accu)
|
||||
rparents []
|
||||
|> (fun s -> String.concat ", " (s : Adef.safe_string list :> string list))
|
||||
|> Adef.safe
|
||||
|
||||
let string_of_marriage conf marriage =
|
||||
let s =
|
||||
match marriage with
|
||||
| NotMarried | NoSexesCheckNotMarried -> "with"
|
||||
| Married | NoSexesCheckMarried -> "married"
|
||||
| Engaged -> "engaged"
|
||||
| NoMention | MarriageBann | MarriageContract | MarriageLicense | Pacs
|
||||
| Residence ->
|
||||
"with"
|
||||
in
|
||||
Adef.safe (transl conf s)
|
||||
|
||||
let string_of_divorce conf divorce =
|
||||
match divorce with
|
||||
| NotDivorced -> "" |> Adef.safe
|
||||
| Divorced cod -> transl conf "divorced" ^<^ " " ^<^ string_of_cdate conf cod
|
||||
| Separated -> transl conf "separated" |> Adef.safe
|
||||
|
||||
let string_of_event_witness conf base witnesses =
|
||||
Array.fold_right
|
||||
(fun (ip, wk) accu ->
|
||||
let witn = person_of_iper conf base ip in
|
||||
let kind = Util.string_of_witness_kind conf (get_sex @@ poi base ip) wk in
|
||||
if (witn :> string) = "" then (kind ^^^ transl conf ":" ^<^ witn) :: accu
|
||||
else accu)
|
||||
witnesses []
|
||||
|> fun s ->
|
||||
String.concat ", " (s : Adef.safe_string list :> string list) |> Adef.safe
|
||||
|
||||
let string_of_epers_name conf epers_name =
|
||||
match epers_name with
|
||||
| Epers_Birth -> Adef.safe @@ Utf8.capitalize_fst (transl conf "birth")
|
||||
| Epers_Baptism -> Adef.safe @@ Utf8.capitalize_fst (transl conf "baptism")
|
||||
| Epers_Death -> Adef.safe @@ Utf8.capitalize_fst (transl conf "death")
|
||||
| Epers_Burial -> Adef.safe @@ Utf8.capitalize_fst (transl conf "burial")
|
||||
| Epers_Cremation ->
|
||||
Adef.safe @@ Utf8.capitalize_fst (transl conf "cremation")
|
||||
| Epers_Accomplishment ->
|
||||
Adef.safe @@ Utf8.capitalize_fst (transl conf "accomplishment")
|
||||
| Epers_Acquisition ->
|
||||
Adef.safe @@ Utf8.capitalize_fst (transl conf "acquisition")
|
||||
| Epers_Adhesion -> Adef.safe @@ Utf8.capitalize_fst (transl conf "adhesion")
|
||||
| Epers_BaptismLDS ->
|
||||
Adef.safe @@ Utf8.capitalize_fst (transl conf "baptismLDS")
|
||||
| Epers_BarMitzvah ->
|
||||
Adef.safe @@ Utf8.capitalize_fst (transl conf "bar mitzvah")
|
||||
| Epers_BatMitzvah ->
|
||||
Adef.safe @@ Utf8.capitalize_fst (transl conf "bat mitzvah")
|
||||
| Epers_Benediction ->
|
||||
Adef.safe @@ Utf8.capitalize_fst (transl conf "benediction")
|
||||
| Epers_ChangeName ->
|
||||
Adef.safe @@ Utf8.capitalize_fst (transl conf "change name")
|
||||
| Epers_Circumcision ->
|
||||
Adef.safe @@ Utf8.capitalize_fst (transl conf "circumcision")
|
||||
| Epers_Confirmation ->
|
||||
Adef.safe @@ Utf8.capitalize_fst (transl conf "confirmation")
|
||||
| Epers_ConfirmationLDS ->
|
||||
Adef.safe @@ Utf8.capitalize_fst (transl conf "confirmation LDS")
|
||||
| Epers_Decoration ->
|
||||
Adef.safe @@ Utf8.capitalize_fst (transl conf "decoration")
|
||||
| Epers_DemobilisationMilitaire ->
|
||||
Adef.safe @@ Utf8.capitalize_fst (transl conf "demobilisationMilitaire")
|
||||
| Epers_Diploma -> Adef.safe @@ Utf8.capitalize_fst (transl conf "diploma")
|
||||
| Epers_Distinction ->
|
||||
Adef.safe @@ Utf8.capitalize_fst (transl conf "distinction")
|
||||
| Epers_Dotation -> Adef.safe @@ Utf8.capitalize_fst (transl conf "dotation")
|
||||
| Epers_DotationLDS ->
|
||||
Adef.safe @@ Utf8.capitalize_fst (transl conf "dotationLDS")
|
||||
| Epers_Education ->
|
||||
Adef.safe @@ Utf8.capitalize_fst (transl conf "education")
|
||||
| Epers_Election -> Adef.safe @@ Utf8.capitalize_fst (transl conf "election")
|
||||
| Epers_Emigration ->
|
||||
Adef.safe @@ Utf8.capitalize_fst (transl conf "emigration")
|
||||
| Epers_Excommunication ->
|
||||
Adef.safe @@ Utf8.capitalize_fst (transl conf "excommunication")
|
||||
| Epers_FamilyLinkLDS ->
|
||||
Adef.safe @@ Utf8.capitalize_fst (transl conf "familyLinkLDS")
|
||||
| Epers_FirstCommunion ->
|
||||
Adef.safe @@ Utf8.capitalize_fst (transl conf "firstCommunion")
|
||||
| Epers_Funeral -> Adef.safe @@ Utf8.capitalize_fst (transl conf "funeral")
|
||||
| Epers_Graduate -> Adef.safe @@ Utf8.capitalize_fst (transl conf "graduate")
|
||||
| Epers_Hospitalisation ->
|
||||
Adef.safe @@ Utf8.capitalize_fst (transl conf "hospitalisation")
|
||||
| Epers_Illness -> Adef.safe @@ Utf8.capitalize_fst (transl conf "illness")
|
||||
| Epers_Immigration ->
|
||||
Adef.safe @@ Utf8.capitalize_fst (transl conf "immigration")
|
||||
| Epers_ListePassenger ->
|
||||
Adef.safe @@ Utf8.capitalize_fst (transl conf "listePassenger")
|
||||
| Epers_MilitaryDistinction ->
|
||||
Adef.safe @@ Utf8.capitalize_fst (transl conf "militaryDistinction")
|
||||
| Epers_MilitaryPromotion ->
|
||||
Adef.safe @@ Utf8.capitalize_fst (transl conf "militaryPromotion")
|
||||
| Epers_MilitaryService ->
|
||||
Adef.safe @@ Utf8.capitalize_fst (transl conf "militaryService")
|
||||
| Epers_MobilisationMilitaire ->
|
||||
Adef.safe @@ Utf8.capitalize_fst (transl conf "mobilisationMilitaire")
|
||||
| Epers_Naturalisation ->
|
||||
Adef.safe @@ Utf8.capitalize_fst (transl conf "naturalisation")
|
||||
| Epers_Occupation ->
|
||||
Adef.safe
|
||||
@@ Utf8.capitalize_fst (transl_nth conf "occupation/occupations" 0)
|
||||
| Epers_Ordination ->
|
||||
Adef.safe @@ Utf8.capitalize_fst (transl conf "ordination")
|
||||
| Epers_Property -> Adef.safe @@ Utf8.capitalize_fst (transl conf "property")
|
||||
| Epers_Recensement ->
|
||||
Adef.safe @@ Utf8.capitalize_fst (transl conf "recensement")
|
||||
| Epers_Residence ->
|
||||
Adef.safe @@ Utf8.capitalize_fst (transl conf "residence")
|
||||
| Epers_Retired -> Adef.safe @@ Utf8.capitalize_fst (transl conf "retired")
|
||||
| Epers_ScellentChildLDS ->
|
||||
Adef.safe @@ Utf8.capitalize_fst (transl conf "scellentChildLDS")
|
||||
| Epers_ScellentParentLDS ->
|
||||
Adef.safe @@ Utf8.capitalize_fst (transl conf "scellentParentLDS")
|
||||
| Epers_ScellentSpouseLDS ->
|
||||
Adef.safe @@ Utf8.capitalize_fst (transl conf "scellentSpouseLDS")
|
||||
| Epers_VenteBien ->
|
||||
Adef.safe @@ Utf8.capitalize_fst (transl conf "venteBien")
|
||||
| Epers_Will -> Adef.safe @@ Utf8.capitalize_fst (transl conf "will")
|
||||
| Epers_Name n ->
|
||||
Adef.safe
|
||||
@@ Utf8.capitalize_fst (escape_html n : Adef.safe_string :> string)
|
||||
|
||||
let string_of_efam_name conf efam_name =
|
||||
match efam_name with
|
||||
| Efam_Marriage ->
|
||||
Adef.safe @@ Utf8.capitalize_fst (transl conf "marriage event")
|
||||
| Efam_NoMarriage ->
|
||||
Adef.safe @@ Utf8.capitalize_fst (transl conf "no marriage event")
|
||||
| Efam_NoMention ->
|
||||
Adef.safe @@ Utf8.capitalize_fst (transl conf "no mention")
|
||||
| Efam_Engage -> Adef.safe @@ Utf8.capitalize_fst (transl conf "engage event")
|
||||
| Efam_Divorce ->
|
||||
Adef.safe @@ Utf8.capitalize_fst (transl conf "divorce event")
|
||||
| Efam_Separated ->
|
||||
Adef.safe @@ Utf8.capitalize_fst (transl conf "separate event")
|
||||
| Efam_Annulation ->
|
||||
Adef.safe @@ Utf8.capitalize_fst (transl conf "annulation")
|
||||
| Efam_MarriageBann ->
|
||||
Adef.safe @@ Utf8.capitalize_fst (transl conf "marriage bann")
|
||||
| Efam_MarriageContract ->
|
||||
Adef.safe @@ Utf8.capitalize_fst (transl conf "marriage contract")
|
||||
| Efam_MarriageLicense ->
|
||||
Adef.safe @@ Utf8.capitalize_fst (transl conf "marriage licence")
|
||||
| Efam_PACS -> Adef.safe @@ Utf8.capitalize_fst (transl conf "PACS")
|
||||
| Efam_Residence -> Adef.safe @@ Utf8.capitalize_fst (transl conf "residence")
|
||||
| Efam_Name n ->
|
||||
Adef.safe
|
||||
@@ Utf8.capitalize_fst (escape_html n : Adef.safe_string :> string)
|
||||
|
||||
(* ************************************************************************ *)
|
||||
(* [Fonc] highlight_diff : char array -> bool array -> string *)
|
||||
|
||||
(* ************************************************************************ *)
|
||||
|
||||
(** [Description] : Converti un tableau de char en string, avec les parties
|
||||
modifiées encadrées par des balises <span>.
|
||||
[Args] :
|
||||
- arr : tableau à convertir
|
||||
- diff_arr : tableau des différences
|
||||
[Retour] :
|
||||
- string
|
||||
[Rem] : Non exporté en clair hors de ce module. *)
|
||||
let highlight_diff arr diff_arr =
|
||||
let rec loop i s =
|
||||
if i >= Array.length arr then s
|
||||
else if diff_arr.(i) then (
|
||||
let j = ref i in
|
||||
let accu = ref s in
|
||||
accu := !accu ^ "<span class=\"mark\">";
|
||||
while !j < Array.length diff_arr && diff_arr.(!j) do
|
||||
accu := !accu ^ Printf.sprintf "%c" arr.(!j);
|
||||
incr j
|
||||
done;
|
||||
accu := !accu ^ "</span>";
|
||||
loop !j !accu)
|
||||
else loop (i + 1) (s ^ Printf.sprintf "%c" arr.(i))
|
||||
in
|
||||
loop 0 ""
|
||||
|
||||
(* ************************************************************************ *)
|
||||
(* [Fonc] array_of_string : string -> char array *)
|
||||
|
||||
(* ************************************************************************ *)
|
||||
|
||||
(** [Description] : Converti une string en tableau de char afin de pouvoir
|
||||
faire un diff.
|
||||
[Args] :
|
||||
- s : string à convertir
|
||||
[Retour] :
|
||||
- char array
|
||||
[Rem] : Non exporté en clair hors de ce module. *)
|
||||
let array_of_string s =
|
||||
let s = (s :> string) in
|
||||
let len = String.length s in
|
||||
let a = Array.make len ' ' in
|
||||
let rec loop i =
|
||||
if i = len then a
|
||||
else (
|
||||
a.(i) <- s.[i];
|
||||
loop (i + 1))
|
||||
in
|
||||
loop 0
|
||||
|
||||
let diff_string (before : Adef.safe_string) (after : Adef.safe_string) :
|
||||
Adef.safe_string * Adef.safe_string =
|
||||
if before = after then (before, after)
|
||||
else if (before :> string) = "" then
|
||||
(before, "<span class=\"mark\">" ^<^ after ^>^ "</span>")
|
||||
else if (after :> string) = "" then
|
||||
("<span class=\"mark\">" ^<^ before ^>^ "</span>", after)
|
||||
else
|
||||
let aa = array_of_string (after :> string) in
|
||||
let bb = array_of_string (before :> string) in
|
||||
let bef_d, aft_d = Difference.f bb aa in
|
||||
let bef_s = highlight_diff bb bef_d in
|
||||
let aft_s = highlight_diff aa aft_d in
|
||||
(Adef.safe bef_s, Adef.safe aft_s)
|
||||
|
||||
type 'a env =
|
||||
| Vfam of
|
||||
(iper, ifam, string) gen_family option
|
||||
* (iper, ifam, string) gen_family option
|
||||
* bool
|
||||
| Vchild of iper array option * iper array option
|
||||
| Vfevent of
|
||||
(iper, string) gen_fam_event option
|
||||
* (iper, string) gen_fam_event option
|
||||
* bool
|
||||
| Vpevent of
|
||||
(iper, string) gen_pers_event option
|
||||
* (iper, string) gen_pers_event option
|
||||
| Vint of int
|
||||
| Vstring of string
|
||||
| Vother of 'a
|
||||
| Vnone
|
||||
|
||||
let get_env v env = try List.assoc v env with Not_found -> Vnone
|
||||
let get_vother = function Vother x -> Some x | _ -> None
|
||||
let set_vother x = Vother x
|
||||
let str_val x = VVstring x
|
||||
let safe_val (x : Adef.safe_string) = VVstring (x :> string)
|
||||
|
||||
let rec eval_var conf base env (bef, aft, p_auth) _loc sl =
|
||||
try eval_simple_var conf base env (bef, aft, p_auth) sl
|
||||
with Not_found -> eval_compound_var conf base env (bef, aft, p_auth) sl
|
||||
|
||||
and eval_simple_var conf base env (bef, aft, p_auth) :
|
||||
string list -> 'a expr_val = function
|
||||
| [ s ] -> eval_simple_str_var conf base env (bef, aft, p_auth) s
|
||||
| _ -> raise Not_found
|
||||
|
||||
and eval_compound_var conf base env (bef, aft, p_auth) sl : 'b expr_val =
|
||||
let loop = function
|
||||
| [ s ] -> eval_simple_str_var conf base env (bef, aft, p_auth) s
|
||||
| [ "evar"; s ] -> (
|
||||
match p_getenv conf.env s with
|
||||
| Some s -> safe_val (escape_html s)
|
||||
| None -> str_val "")
|
||||
| "before" :: sl ->
|
||||
fst (eval_gen_record conf base env (bef, aft, p_auth) sl)
|
||||
| "after" :: sl -> snd (eval_gen_record conf base env (bef, aft, p_auth) sl)
|
||||
| _ -> raise Not_found
|
||||
in
|
||||
loop sl
|
||||
|
||||
and eval_gen_record conf base env (bef, aft, p_auth) :
|
||||
string list -> 'a expr_val * 'b expr_val = function
|
||||
| [ "date" ] -> (safe_val bef.date, safe_val aft.date)
|
||||
| [ "wizard" ] ->
|
||||
(safe_val bef.HistoryDiff.wizard, safe_val aft.HistoryDiff.wizard)
|
||||
| [ s ] -> eval_str_gen_record conf base env (bef, aft, p_auth) s
|
||||
| _ -> raise Not_found
|
||||
|
||||
and eval_str_gen_record conf base env (bef, aft, p_auth) :
|
||||
string -> 'a expr_val * 'b expr_val =
|
||||
let diff_string a b =
|
||||
let a, b = diff_string a b in
|
||||
(safe_val a, safe_val b)
|
||||
in
|
||||
let aux g =
|
||||
if p_auth then
|
||||
diff_string (g bef :> Adef.safe_string) (g aft :> Adef.safe_string)
|
||||
else (str_val "", str_val "")
|
||||
in
|
||||
let aux' m_auth bef aft f =
|
||||
if p_auth && m_auth then
|
||||
match (bef, aft) with
|
||||
| Some b, Some a -> diff_string (f conf b) (f conf a)
|
||||
| None, Some a -> (str_val "", safe_val (f conf a))
|
||||
| Some b, None -> (safe_val (f conf b), str_val "")
|
||||
| None, None -> (str_val "", str_val "")
|
||||
else (str_val "", str_val "")
|
||||
in
|
||||
function
|
||||
| "first_name" -> aux (fun x -> Util.escape_html x.gen_p.first_name)
|
||||
| "surname" -> aux (fun x -> Util.escape_html x.gen_p.surname)
|
||||
| "occ" -> aux (fun x -> Adef.safe @@ string_of_int x.gen_p.occ)
|
||||
| "image" ->
|
||||
if not conf.no_image then aux (fun x -> Util.escape_html x.gen_p.image)
|
||||
else (str_val "", str_val "")
|
||||
| "public_name" -> aux (fun x -> Util.escape_html x.gen_p.public_name)
|
||||
| "qualifiers" ->
|
||||
aux (fun x -> Util.escape_html @@ String.concat ", " x.gen_p.qualifiers)
|
||||
| "aliases" ->
|
||||
aux (fun x -> Util.escape_html @@ String.concat ", " x.gen_p.aliases)
|
||||
| "first_names_aliases" ->
|
||||
aux (fun x ->
|
||||
Util.escape_html @@ String.concat ", " x.gen_p.first_names_aliases)
|
||||
| "surnames_aliases" ->
|
||||
aux (fun x ->
|
||||
Util.escape_html @@ String.concat ", " x.gen_p.surnames_aliases)
|
||||
| "titles" -> aux (fun x -> string_of_title conf x.gen_p.titles)
|
||||
| "relations" ->
|
||||
aux (fun x ->
|
||||
let r =
|
||||
string_of_related conf base x.gen_p.key_index x.gen_p.related
|
||||
in
|
||||
let rp = string_of_rparents conf base x.gen_p.rparents in
|
||||
if (r :> string) = "" then rp else r ^^^ ". " ^<^ rp)
|
||||
| "occupation" -> aux (fun x -> Util.safe_html x.gen_p.occupation)
|
||||
| "sex" ->
|
||||
aux (fun x ->
|
||||
Util.index_of_sex x.gen_p.sex
|
||||
|> transl_nth conf "male/female/neuter"
|
||||
|> Adef.safe)
|
||||
| "access" ->
|
||||
aux (fun x ->
|
||||
match x.gen_p.access with
|
||||
| IfTitles -> transl_nth conf "iftitles/public/private" 0 |> Adef.safe
|
||||
| Public -> transl_nth conf "iftitles/public/private" 1 |> Adef.safe
|
||||
| Private -> transl_nth conf "iftitles/public/private" 2 |> Adef.safe)
|
||||
| "birth" -> aux (fun x -> string_of_cdate conf x.gen_p.birth)
|
||||
| "birth_place" -> aux (fun x -> Util.escape_html x.gen_p.birth_place)
|
||||
| "birth_note" -> aux (fun x -> Util.escape_html x.gen_p.birth_note)
|
||||
| "birth_src" -> aux (fun x -> Util.escape_html x.gen_p.birth_src)
|
||||
| "baptism" -> aux (fun x -> string_of_cdate conf x.gen_p.baptism)
|
||||
| "baptism_place" -> aux (fun x -> Util.escape_html x.gen_p.baptism_place)
|
||||
| "baptism_note" -> aux (fun x -> Util.escape_html x.gen_p.baptism_note)
|
||||
| "baptism_src" -> aux (fun x -> Util.escape_html x.gen_p.baptism_src)
|
||||
| "death" -> aux (fun x -> string_of_death conf x.gen_p.death)
|
||||
| "death_place" -> aux (fun x -> Util.escape_html x.gen_p.death_place)
|
||||
| "death_note" -> aux (fun x -> Util.escape_html x.gen_p.death_note)
|
||||
| "death_src" -> aux (fun x -> Util.escape_html x.gen_p.death_src)
|
||||
| "burial" -> aux (fun x -> string_of_burial conf x.gen_p.burial)
|
||||
| "burial_place" -> aux (fun x -> Util.escape_html x.gen_p.burial_place)
|
||||
| "burial_note" -> aux (fun x -> Util.escape_html x.gen_p.burial_note)
|
||||
| "burial_src" -> aux (fun x -> Util.escape_html x.gen_p.burial_src)
|
||||
| "pevent_name" -> (
|
||||
match get_env "pevent" env with
|
||||
| Vpevent (bef, aft) ->
|
||||
aux' true bef aft (fun conf x ->
|
||||
string_of_epers_name conf x.epers_name)
|
||||
| _ -> raise Not_found)
|
||||
| "pevent_date" -> (
|
||||
match get_env "pevent" env with
|
||||
| Vpevent (bef, aft) ->
|
||||
aux' true bef aft (fun conf x -> string_of_cdate conf x.epers_date)
|
||||
| _ -> raise Not_found)
|
||||
| "pevent_place" -> (
|
||||
match get_env "pevent" env with
|
||||
| Vpevent (bef, aft) ->
|
||||
aux' true bef aft (fun _ x -> escape_html x.epers_place)
|
||||
| _ -> raise Not_found)
|
||||
| "pevent_note" -> (
|
||||
match get_env "pevent" env with
|
||||
| Vpevent (bef, aft) ->
|
||||
aux' (not conf.no_note) bef aft (fun _ x -> escape_html x.epers_note)
|
||||
| _ -> raise Not_found)
|
||||
| "pevent_src" -> (
|
||||
match get_env "pevent" env with
|
||||
| Vpevent (bef, aft) ->
|
||||
aux' true bef aft (fun _ x -> escape_html x.epers_src)
|
||||
| _ -> raise Not_found)
|
||||
| "pevent_witness" -> (
|
||||
match get_env "pevent" env with
|
||||
| Vpevent (bef, aft) ->
|
||||
aux' true bef aft (fun conf x ->
|
||||
string_of_event_witness conf base x.epers_witnesses)
|
||||
| _ -> raise Not_found)
|
||||
| "notes" ->
|
||||
if not conf.no_note then aux (fun x -> Util.escape_html x.gen_p.notes)
|
||||
else (str_val "", str_val "")
|
||||
| "psources" -> aux (fun x -> Util.escape_html x.gen_p.psources)
|
||||
| "spouse" -> (
|
||||
match get_env "fam" env with
|
||||
| Vfam (_f_bef, _f_aft, m_auth) ->
|
||||
if m_auth then
|
||||
(eval_string_env "spouse_bef" env, eval_string_env "spouse_aft" env)
|
||||
else (str_val "", str_val "")
|
||||
| _ -> raise Not_found)
|
||||
| "marriage" -> (
|
||||
match get_env "fam" env with
|
||||
| Vfam (bef, aft, m_auth) ->
|
||||
aux' m_auth bef aft (fun conf x -> string_of_cdate conf x.marriage)
|
||||
| _ -> raise Not_found)
|
||||
| "marriage_place" -> (
|
||||
match get_env "fam" env with
|
||||
| Vfam (bef, aft, m_auth) ->
|
||||
aux' m_auth bef aft (fun _ x -> escape_html x.marriage_place)
|
||||
| _ -> raise Not_found)
|
||||
| "marriage_src" -> (
|
||||
match get_env "fam" env with
|
||||
| Vfam (bef, aft, m_auth) ->
|
||||
aux' m_auth bef aft (fun _ x -> escape_html x.marriage_src)
|
||||
| _ -> raise Not_found)
|
||||
| "witnesses" -> (
|
||||
match get_env "fam" env with
|
||||
| Vfam (bef, aft, m_auth) ->
|
||||
aux' m_auth bef aft (fun conf x ->
|
||||
person_of_iper_array conf base x.witnesses)
|
||||
| _ -> raise Not_found)
|
||||
| "marriage_type" -> (
|
||||
match get_env "fam" env with
|
||||
| Vfam (bef, aft, m_auth) ->
|
||||
aux' m_auth bef aft (fun conf x -> string_of_marriage conf x.relation)
|
||||
| _ -> raise Not_found)
|
||||
| "divorce" -> (
|
||||
match get_env "fam" env with
|
||||
| Vfam (bef, aft, m_auth) ->
|
||||
aux' m_auth bef aft (fun conf x -> string_of_divorce conf x.divorce)
|
||||
| _ -> raise Not_found)
|
||||
| "fevent_name" -> (
|
||||
match get_env "fevent" env with
|
||||
| Vfevent (bef, aft, m_auth) ->
|
||||
aux' m_auth bef aft (fun conf x ->
|
||||
string_of_efam_name conf x.efam_name)
|
||||
| _ -> raise Not_found)
|
||||
| "fevent_date" -> (
|
||||
match get_env "fevent" env with
|
||||
| Vfevent (bef, aft, m_auth) ->
|
||||
aux' m_auth bef aft (fun conf x -> string_of_cdate conf x.efam_date)
|
||||
| _ -> raise Not_found)
|
||||
| "fevent_place" -> (
|
||||
match get_env "fevent" env with
|
||||
| Vfevent (bef, aft, m_auth) ->
|
||||
aux' m_auth bef aft (fun _ x -> escape_html x.efam_place)
|
||||
| _ -> raise Not_found)
|
||||
| "fevent_note" -> (
|
||||
match get_env "fevent" env with
|
||||
| Vfevent (bef, aft, m_auth) ->
|
||||
aux' (m_auth && not conf.no_note) bef aft (fun _ x ->
|
||||
escape_html x.efam_note)
|
||||
| _ -> raise Not_found)
|
||||
| "fevent_src" -> (
|
||||
match get_env "fevent" env with
|
||||
| Vfevent (bef, aft, m_auth) ->
|
||||
aux' m_auth bef aft (fun _ x -> escape_html x.efam_src)
|
||||
| _ -> raise Not_found)
|
||||
| "fevent_witness" -> (
|
||||
match get_env "fevent" env with
|
||||
| Vfevent (bef, aft, m_auth) ->
|
||||
aux' m_auth bef aft (fun conf x ->
|
||||
string_of_event_witness conf base x.efam_witnesses)
|
||||
| _ -> raise Not_found)
|
||||
| "comment" -> (
|
||||
match get_env "fam" env with
|
||||
| Vfam (bef, aft, m_auth) ->
|
||||
aux' (m_auth && not conf.no_note) bef aft (fun _ x ->
|
||||
escape_html x.comment)
|
||||
| _ -> raise Not_found)
|
||||
| "origin_file" -> (
|
||||
match get_env "fam" env with
|
||||
| Vfam (bef, aft, m_auth) ->
|
||||
aux' m_auth bef aft (fun _ x -> escape_html x.origin_file)
|
||||
| _ -> raise Not_found)
|
||||
| "fsources" -> (
|
||||
match get_env "fam" env with
|
||||
| Vfam (bef, aft, m_auth) ->
|
||||
aux' m_auth bef aft (fun _ x -> escape_html x.fsources)
|
||||
| _ -> raise Not_found)
|
||||
| "children" -> (
|
||||
match get_env "fam" env with
|
||||
| Vfam (_, _, m_auth) ->
|
||||
if m_auth then
|
||||
match get_env "child" env with
|
||||
| Vchild (bef, aft) ->
|
||||
aux' true bef aft (fun conf -> person_of_iper_array conf base)
|
||||
| _ -> raise Not_found
|
||||
else (str_val "", str_val "")
|
||||
| _ -> raise Not_found)
|
||||
| _ -> raise Not_found
|
||||
|
||||
and eval_simple_str_var conf base env (bef, aft, p_auth) : string -> 'a expr_val
|
||||
= function
|
||||
| "acces" ->
|
||||
person_of_gen_p_key base aft.gen_p
|
||||
|> acces conf base
|
||||
|> (safe_val :> Adef.escaped_string -> 'a expr_val)
|
||||
| "date" -> eval_string_env "date" env
|
||||
| "history_len" -> eval_int_env "history_len" env
|
||||
| "line" -> eval_int_env "line" env
|
||||
| "nb_families" ->
|
||||
max (List.length bef.gen_f) (List.length aft.gen_f)
|
||||
|> string_of_int |> str_val
|
||||
| "person" ->
|
||||
if p_auth then
|
||||
person_of_gen_p_key base aft.gen_p
|
||||
|> Util.gen_person_text conf base
|
||||
|> safe_val
|
||||
else eval_string_env "history_file" env
|
||||
| "wizard" -> eval_string_env "wizard" env
|
||||
| _ -> raise Not_found
|
||||
|
||||
and eval_string_env s env =
|
||||
match get_env s env with
|
||||
| Vstring s -> str_val s (* FIXME? *)
|
||||
| _ -> raise Not_found
|
||||
|
||||
and eval_int_env s env =
|
||||
match get_env s env with
|
||||
| Vint i -> str_val (string_of_int i)
|
||||
| _ -> raise Not_found
|
||||
|
||||
let print_foreach conf base print_ast _eval_expr =
|
||||
let rec print_foreach env xx _loc s sl _el al =
|
||||
match s :: sl with
|
||||
| [ "family" ] -> print_foreach_family env xx al
|
||||
| [ "fevent" ] -> print_foreach_fevent env xx al
|
||||
| [ "pevent" ] -> print_foreach_pevent env xx al
|
||||
| [ "history_line" ] -> print_foreach_history_line env xx al
|
||||
| _ -> raise Not_found
|
||||
and print_foreach_family env xx al =
|
||||
let bef, aft, p_auth = xx in
|
||||
let rec loop bef_f bef_c aft_f aft_c =
|
||||
match (bef_f, aft_f) with
|
||||
| [], [] -> ()
|
||||
| [], gen_f :: l ->
|
||||
let fam = foi base gen_f.fam_index in
|
||||
let isp = Gutil.spouse aft.gen_p.key_index fam in
|
||||
let sp = person_of_iper conf base isp in
|
||||
let m_auth = authorized_age conf base (poi base isp) && p_auth in
|
||||
let vfam = Vfam (None, Some gen_f, m_auth) in
|
||||
let vchild, c =
|
||||
match (bef_c, aft_c) with
|
||||
| [], gen_c :: l -> (Vchild (None, Some gen_c), l)
|
||||
| _ -> (* pas normal*) (Vchild (None, None), [])
|
||||
in
|
||||
let env =
|
||||
("fam", vfam) :: ("spouse_bef", Vstring "")
|
||||
:: ("spouse_aft", Vstring (sp :> string))
|
||||
:: ("child", vchild) :: env
|
||||
in
|
||||
List.iter (print_ast env xx) al;
|
||||
loop [] bef_c l c
|
||||
| gen_f :: l, [] ->
|
||||
let fam = foi base gen_f.fam_index in
|
||||
let isp = Gutil.spouse aft.gen_p.key_index fam in
|
||||
let sp = person_of_iper conf base isp in
|
||||
let m_auth = authorized_age conf base (poi base isp) && p_auth in
|
||||
let vfam = Vfam (Some gen_f, None, m_auth) in
|
||||
let vchild, c =
|
||||
match (bef_c, aft_c) with
|
||||
| gen_c :: l, [] -> (Vchild (Some gen_c, None), l)
|
||||
| _ -> (* pas normal*) (Vchild (None, None), [])
|
||||
in
|
||||
let env =
|
||||
("fam", vfam)
|
||||
:: ("spouse_bef", Vstring (sp :> string))
|
||||
:: ("spouse_aft", Vstring "") :: ("child", vchild) :: env
|
||||
in
|
||||
List.iter (print_ast env xx) al;
|
||||
loop l c [] aft_c
|
||||
| gen_f1 :: l1, gen_f2 :: l2 ->
|
||||
let fam = foi base gen_f2.fam_index in
|
||||
let isp1 = Gutil.spouse bef.gen_p.key_index fam in
|
||||
let isp2 = Gutil.spouse aft.gen_p.key_index fam in
|
||||
let sp1 = person_of_iper conf base isp1 in
|
||||
let sp2 = person_of_iper conf base isp2 in
|
||||
let m_auth = authorized_age conf base (poi base isp2) && p_auth in
|
||||
let vfam = Vfam (Some gen_f1, Some gen_f2, m_auth) in
|
||||
let vchild, c1, c2 =
|
||||
match (bef_c, aft_c) with
|
||||
| gen_c1 :: l1, gen_c2 :: l2 ->
|
||||
(Vchild (Some gen_c1, Some gen_c2), l1, l2)
|
||||
| _ -> (* pas normal*) (Vchild (None, None), [], [])
|
||||
in
|
||||
let env =
|
||||
("fam", vfam)
|
||||
:: ("spouse_bef", Vstring (sp1 :> string))
|
||||
:: ("spouse_aft", Vstring (sp2 :> string))
|
||||
:: ("child", vchild) :: env
|
||||
in
|
||||
List.iter (print_ast env xx) al;
|
||||
loop l1 c1 l2 c2
|
||||
in
|
||||
loop bef.gen_f bef.gen_c aft.gen_f aft.gen_c
|
||||
and print_foreach_fevent env xx al =
|
||||
let rec loop m_auth bef_fevents aft_fevents =
|
||||
match (bef_fevents, aft_fevents) with
|
||||
| [], [] -> ()
|
||||
| [], aft_evt :: l ->
|
||||
let env = ("fevent", Vfevent (None, Some aft_evt, m_auth)) :: env in
|
||||
List.iter (print_ast env xx) al;
|
||||
loop m_auth [] l
|
||||
| bef_evt :: l, [] ->
|
||||
let env = ("fevent", Vfevent (Some bef_evt, None, m_auth)) :: env in
|
||||
List.iter (print_ast env xx) al;
|
||||
loop m_auth l []
|
||||
| bef_evt :: l1, aft_evt :: l2 ->
|
||||
let env =
|
||||
("fevent", Vfevent (Some bef_evt, Some aft_evt, m_auth)) :: env
|
||||
in
|
||||
List.iter (print_ast env xx) al;
|
||||
loop m_auth l1 l2
|
||||
in
|
||||
match get_env "fam" env with
|
||||
| Vfam (bef, aft, m_auth) -> (
|
||||
match (bef, aft) with
|
||||
| Some b, Some a -> loop m_auth b.fevents a.fevents
|
||||
| None, Some a -> loop m_auth [] a.fevents
|
||||
| Some b, None -> loop m_auth b.fevents []
|
||||
| None, None -> ())
|
||||
| _ -> ()
|
||||
and print_foreach_pevent env xx al =
|
||||
let bef, aft, _p_auth = xx in
|
||||
let rec loop bef_pevents aft_pevents =
|
||||
match (bef_pevents, aft_pevents) with
|
||||
| [], [] -> ()
|
||||
| [], aft_evt :: l ->
|
||||
let env = ("pevent", Vpevent (None, Some aft_evt)) :: env in
|
||||
List.iter (print_ast env xx) al;
|
||||
loop [] l
|
||||
| bef_evt :: l, [] ->
|
||||
let env = ("pevent", Vpevent (Some bef_evt, None)) :: env in
|
||||
List.iter (print_ast env xx) al;
|
||||
loop l []
|
||||
| bef_evt :: l1, aft_evt :: l2 ->
|
||||
let env = ("pevent", Vpevent (Some bef_evt, Some aft_evt)) :: env in
|
||||
List.iter (print_ast env xx) al;
|
||||
loop l1 l2
|
||||
in
|
||||
loop bef.gen_p.pevents aft.gen_p.pevents
|
||||
and print_foreach_history_line env xx al =
|
||||
match get_env "history_file" env with
|
||||
| Vstring fname ->
|
||||
let history = load_person_history conf fname in
|
||||
let rec loop i list =
|
||||
match list with
|
||||
| [] -> ()
|
||||
| gr :: l ->
|
||||
let env =
|
||||
("line", Vint i)
|
||||
:: ("date", Vstring (gr.date : Adef.safe_string :> string))
|
||||
:: ( "wizard",
|
||||
Vstring
|
||||
(gr.HistoryDiff.wizard : Adef.safe_string :> string) )
|
||||
:: env
|
||||
in
|
||||
List.iter (print_ast env xx) al;
|
||||
loop (i + 1) l
|
||||
in
|
||||
loop 0 history
|
||||
| _ -> ()
|
||||
in
|
||||
print_foreach
|
||||
|
||||
let eval_predefined_apply conf _env f vl =
|
||||
let vl = List.map (function VVstring s -> s | _ -> raise Not_found) vl in
|
||||
match (f, vl) with
|
||||
| "transl_date", [ date_txt ] -> (
|
||||
(* date_tpl = "0000-00-00 00:00:00" *)
|
||||
try
|
||||
let year = int_of_string (String.sub date_txt 0 4) in
|
||||
let month = int_of_string (String.sub date_txt 5 2) in
|
||||
let day = int_of_string (String.sub date_txt 8 2) in
|
||||
let date =
|
||||
Dgreg ({ day; month; year; prec = Sure; delta = 0 }, Dgregorian)
|
||||
in
|
||||
let time = String.sub date_txt 11 8 in
|
||||
DateDisplay.string_of_date conf date ^>^ ", " ^ time
|
||||
with Failure _ -> escape_html date_txt)
|
||||
| _ -> raise Not_found
|
||||
|
||||
let print conf base =
|
||||
match p_getenv conf.env "t" with
|
||||
| Some ("SUM" | "DIFF") -> (
|
||||
match p_getenv conf.env "f" with
|
||||
| Some file when file <> "" ->
|
||||
let history = load_person_history conf file in
|
||||
let len = List.length history in
|
||||
let before, after =
|
||||
match (p_getint conf.env "old", p_getint conf.env "new") with
|
||||
| Some o, Some n ->
|
||||
let o =
|
||||
if o < 0 then 0 else if o > len - 1 then len - 1 else o
|
||||
in
|
||||
let n =
|
||||
if n < 0 then 0 else if n > len - 1 then len - 1 else n
|
||||
in
|
||||
(o, n)
|
||||
| _ -> (0, 0)
|
||||
in
|
||||
let before = List.nth history before in
|
||||
let after = List.nth history after in
|
||||
let p = person_of_gen_p_key base after.gen_p in
|
||||
let p_auth = authorized_age conf base p in
|
||||
let env =
|
||||
[ ("history_file", Vstring file); ("history_len", Vint len) ]
|
||||
in
|
||||
let eval_predefined_apply _env f vl =
|
||||
(eval_predefined_apply conf _env f vl :> string)
|
||||
in
|
||||
Hutil.interp conf "updhist_diff"
|
||||
{
|
||||
Templ.eval_var = eval_var conf base;
|
||||
Templ.eval_transl = (fun _ -> Templ.eval_transl conf);
|
||||
Templ.eval_predefined_apply;
|
||||
Templ.get_vother;
|
||||
Templ.set_vother;
|
||||
Templ.print_foreach = print_foreach conf base;
|
||||
}
|
||||
env (before, after, p_auth)
|
||||
| _ -> Hutil.incorrect_request conf)
|
||||
| _ -> Hutil.incorrect_request conf
|
||||
9
lib/historyDiffDisplay.mli
Normal file
9
lib/historyDiffDisplay.mli
Normal file
@@ -0,0 +1,9 @@
|
||||
val print_clean : Config.config -> unit
|
||||
(** Displays page that allows to select all revision of the history file in argument that user may want to clean *)
|
||||
|
||||
val print_clean_ok : Config.config -> unit
|
||||
(** Cleans the history associated to the history file in argument *)
|
||||
|
||||
val print : Config.config -> Gwdb.base -> unit
|
||||
(** Displays the page that allows to select (with variable {i t} = "SUM") and to view (with variable {i t} = "DIFF") the difference between all revisions of
|
||||
history file of concerned person in variable {i f}. Intepretate the template file {i updhist_diff.txt} *)
|
||||
281
lib/hutil.ml
Normal file
281
lib/hutil.ml
Normal file
@@ -0,0 +1,281 @@
|
||||
(* Copyright (c) 2007 INRIA *)
|
||||
|
||||
open Config
|
||||
open Def
|
||||
|
||||
let link_to_referer conf =
|
||||
let referer = Util.get_referer conf in
|
||||
let back = Utf8.capitalize_fst (Util.transl conf "back") in
|
||||
if (referer :> string) <> "" then
|
||||
({|<a href="|} ^<^ referer
|
||||
^>^ {|" class="btn btn-sm btn-link p-0 border-0" title="|} ^ back
|
||||
^ {|"><i class="fa fa-arrow-left-long fa-fw fa-sm"></i></a>|}
|
||||
:> Adef.safe_string)
|
||||
else Adef.safe ""
|
||||
|
||||
let gen_print_link_to_welcome f conf _right_aligned =
|
||||
Output.print_sstring conf "<div class=\"d-flex flex-column fix_top fix_left";
|
||||
Output.print_sstring conf "\">\n";
|
||||
f ();
|
||||
Output.print_sstring conf {|<a href="|};
|
||||
Output.print_string conf (Util.commd ~senv:false conf);
|
||||
Output.print_sstring conf {|" title="|};
|
||||
Output.print_sstring conf (Utf8.capitalize (Util.transl conf "home"));
|
||||
Output.print_sstring conf {|"><i class="fa fa-house fa-fw fa-sm"></i></a>|};
|
||||
let str = link_to_referer conf in
|
||||
if (str :> string) <> "" then Output.print_string conf str;
|
||||
Output.print_sstring conf "</div>"
|
||||
|
||||
let print_link_to_welcome = gen_print_link_to_welcome (fun () -> ())
|
||||
|
||||
(* S: use Util.include_template for "hed"? *)
|
||||
|
||||
let header_without_http_nor_home conf title =
|
||||
let str1 =
|
||||
Printf.sprintf {|<!DOCTYPE html>
|
||||
<html lang="%s">
|
||||
<head>
|
||||
<title>|} conf.lang
|
||||
in
|
||||
let str2 =
|
||||
Printf.sprintf
|
||||
{|</title>
|
||||
<meta name="robots" content="none">
|
||||
<meta charset="%s">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
|
||||
<link rel="shortcut icon" href="%s/favicon_gwd.png">
|
||||
<link rel="apple-touch-icon" href="%s/favicon_gwd.png">
|
||||
|}
|
||||
conf.charset
|
||||
(Util.images_prefix conf :> string)
|
||||
(Util.images_prefix conf :> string)
|
||||
in
|
||||
Output.print_sstring conf str1;
|
||||
title true;
|
||||
Output.print_sstring conf str2;
|
||||
Util.include_template conf [] "css" (fun () -> ());
|
||||
Output.print_sstring conf "</head>\n";
|
||||
let s =
|
||||
try " dir=\"" ^ Hashtbl.find conf.lexicon "!dir" ^ "\""
|
||||
with Not_found -> ""
|
||||
in
|
||||
let s = s ^ Util.body_prop conf in
|
||||
Output.printf conf "<body%s>\n" s;
|
||||
(match Util.open_etc_file conf "hed" with
|
||||
| Some (ic, _) -> Templ.copy_from_templ conf [] ic
|
||||
| None -> ());
|
||||
Util.message_to_wizard conf
|
||||
|
||||
let header_without_page_title conf title =
|
||||
Util.html conf;
|
||||
header_without_http_nor_home conf title;
|
||||
(* balancing </div> in gen_trailer *)
|
||||
Output.printf conf "<div class=\"container\">"
|
||||
|
||||
let header_without_http conf title =
|
||||
header_without_http_nor_home conf title;
|
||||
match Util.open_etc_file conf "home" with
|
||||
| Some (ic, _) -> Templ.copy_from_templ conf [] ic
|
||||
| None -> ()
|
||||
|
||||
let header_link_welcome conf title =
|
||||
header_without_page_title conf title;
|
||||
Output.print_sstring conf "<h1>";
|
||||
title false;
|
||||
Output.print_sstring conf "</h1>\n"
|
||||
|
||||
let header_no_page_title conf title =
|
||||
header_without_page_title conf title;
|
||||
match Util.p_getenv conf.env "title" with
|
||||
| None | Some "" -> ()
|
||||
| Some x -> Output.printf conf "<h1>%s</h1>\n" x
|
||||
|
||||
let header conf title =
|
||||
header_without_page_title conf title;
|
||||
Output.print_sstring conf "\n<h1>";
|
||||
title false;
|
||||
Output.print_sstring conf "</h1>\n"
|
||||
|
||||
let header_fluid conf title =
|
||||
header_without_http conf title;
|
||||
(* balancing </div> in gen_trailer *)
|
||||
Output.print_sstring conf "<div class=\"container-fluid\">";
|
||||
Output.print_sstring conf "\n<h1>";
|
||||
title false;
|
||||
Output.print_sstring conf "</h1>\n"
|
||||
|
||||
let rheader conf title =
|
||||
header_without_page_title conf title;
|
||||
Output.print_sstring conf "<h1 class=\"error\">";
|
||||
title false;
|
||||
Output.print_sstring conf "</h1>\n"
|
||||
|
||||
let trailer conf =
|
||||
let conf = { conf with is_printed_by_template = false } in
|
||||
(match Util.open_etc_file conf "trl" with
|
||||
| Some (ic, _) -> Templ.copy_from_templ conf [] ic
|
||||
| None -> ());
|
||||
Templ.print_copyright conf;
|
||||
Util.include_template conf [] "js" (fun () -> ());
|
||||
let query_time = Unix.gettimeofday () -. conf.query_start in
|
||||
Util.time_debug conf query_time !GWPARAM.nb_errors !GWPARAM.errors_undef
|
||||
!GWPARAM.errors_other !GWPARAM.set_vars;
|
||||
Output.print_sstring conf "</body>\n</html>\n"
|
||||
|
||||
let () =
|
||||
GWPARAM.wrap_output :=
|
||||
fun conf title content ->
|
||||
header conf (fun _ -> Output.print_string conf title);
|
||||
content ();
|
||||
trailer conf
|
||||
|
||||
let incorrect_request ?(comment = "") conf =
|
||||
!GWPARAM.output_error conf Def.Bad_Request ~content:(Adef.safe comment)
|
||||
|
||||
let error_cannot_access conf fname =
|
||||
!GWPARAM.output_error conf Def.Not_Found
|
||||
~content:
|
||||
("Cannot access file \""
|
||||
^<^ (Util.escape_html fname : Adef.escaped_string :> Adef.safe_string)
|
||||
^>^ ".txt\".")
|
||||
|
||||
let gen_interp header conf fname ifun env ep =
|
||||
Templ_parser.wrap fname (fun () ->
|
||||
match Templ.input_templ conf fname with
|
||||
| Some astl ->
|
||||
if header then Util.html conf;
|
||||
let full_name = Util.etc_file_name conf fname in
|
||||
Templ.interp_ast conf ifun env ep [ Ainclude (full_name, astl) ]
|
||||
| None -> error_cannot_access conf fname)
|
||||
|
||||
let interp_no_header conf fname ifun env ep =
|
||||
gen_interp false conf fname ifun env ep
|
||||
|
||||
let interp conf fname ifun env ep = gen_interp true conf fname ifun env ep
|
||||
|
||||
type 'a env = Vint of int | Vother of 'a | Vnone
|
||||
|
||||
let get_env v env = try List.assoc v env with Not_found -> Vnone
|
||||
let get_vother = function Vother x -> Some x | _ -> None
|
||||
let set_vother x = Vother x
|
||||
|
||||
let interp_no_env conf fname =
|
||||
interp_no_header conf fname
|
||||
{
|
||||
Templ.eval_var = (fun _ -> raise Not_found);
|
||||
Templ.eval_transl = (fun _ -> Templ.eval_transl conf);
|
||||
Templ.eval_predefined_apply = (fun _ -> raise Not_found);
|
||||
Templ.get_vother;
|
||||
Templ.set_vother;
|
||||
Templ.print_foreach = (fun _ -> raise Not_found);
|
||||
}
|
||||
[] ()
|
||||
|
||||
(* Calendar request *)
|
||||
|
||||
let eval_julian_day conf =
|
||||
let open Adef in
|
||||
let getint v = match Util.p_getint conf.env v with Some x -> x | _ -> 0 in
|
||||
List.fold_left
|
||||
(fun d (var, cal, conv, max_month) ->
|
||||
let yy =
|
||||
match Util.p_getenv conf.env ("y" ^ var) with
|
||||
| Some v -> (
|
||||
try
|
||||
let len = String.length v in
|
||||
if cal = Djulian && len > 2 && v.[len - 2] = '/' then
|
||||
int_of_string (String.sub v 0 (len - 2)) + 1
|
||||
else int_of_string v
|
||||
with Failure _ -> 0)
|
||||
| None -> 0
|
||||
in
|
||||
let mm = getint ("m" ^ var) in
|
||||
let dd = getint ("d" ^ var) in
|
||||
let dt = { day = dd; month = mm; year = yy; prec = Sure; delta = 0 } in
|
||||
match Util.p_getenv conf.env ("t" ^ var) with
|
||||
| Some _ -> conv dt
|
||||
| None -> (
|
||||
match
|
||||
( Util.p_getenv conf.env ("y" ^ var ^ "1"),
|
||||
Util.p_getenv conf.env ("y" ^ var ^ "2"),
|
||||
Util.p_getenv conf.env ("m" ^ var ^ "1"),
|
||||
Util.p_getenv conf.env ("m" ^ var ^ "2"),
|
||||
Util.p_getenv conf.env ("d" ^ var ^ "1"),
|
||||
Util.p_getenv conf.env ("d" ^ var ^ "2") )
|
||||
with
|
||||
| Some _, _, _, _, _, _ -> conv { dt with year = yy - 1 }
|
||||
| _, Some _, _, _, _, _ -> conv { dt with year = yy + 1 }
|
||||
| _, _, Some _, _, _, _ ->
|
||||
let yy, mm =
|
||||
if mm = 1 then (yy - 1, max_month) else (yy, mm - 1)
|
||||
in
|
||||
conv { dt with year = yy; month = mm }
|
||||
| _, _, _, Some _, _, _ ->
|
||||
let yy, mm =
|
||||
if mm = max_month then (yy + 1, 1) else (yy, mm + 1)
|
||||
in
|
||||
let r = conv { dt with year = yy; month = mm } in
|
||||
if r = conv dt then
|
||||
let yy, mm =
|
||||
if mm = max_month then (yy + 1, 1) else (yy, mm + 1)
|
||||
in
|
||||
conv { dt with year = yy; month = mm }
|
||||
else r
|
||||
| _, _, _, _, Some _, _ -> conv { dt with day = dd - 1 }
|
||||
| _, _, _, _, _, Some _ -> conv { dt with day = dd + 1 }
|
||||
| _ -> d))
|
||||
(Calendar.sdn_of_gregorian conf.today)
|
||||
[
|
||||
("g", Dgregorian, Calendar.sdn_of_gregorian, 12);
|
||||
("j", Djulian, Calendar.sdn_of_julian, 12);
|
||||
("f", Dfrench, Calendar.sdn_of_french, 13);
|
||||
("h", Dhebrew, Calendar.sdn_of_hebrew, 13);
|
||||
]
|
||||
|
||||
(* *)
|
||||
|
||||
let eval_var conf env jd _loc =
|
||||
let open TemplAst in
|
||||
function
|
||||
| [ "integer" ] -> (
|
||||
match get_env "integer" env with
|
||||
| Vint i -> VVstring (string_of_int i)
|
||||
| _ -> raise Not_found)
|
||||
| "date" :: sl -> TemplDate.eval_date_var conf jd sl
|
||||
| "today" :: sl ->
|
||||
TemplDate.eval_date_var conf (Calendar.sdn_of_gregorian conf.today) sl
|
||||
| _ -> raise Not_found
|
||||
|
||||
let print_foreach print_ast eval_expr =
|
||||
let eval_int_expr env jd e =
|
||||
let s = eval_expr env jd e in
|
||||
try int_of_string s with Failure _ -> raise Not_found
|
||||
in
|
||||
let rec print_foreach env jd _loc s sl el al =
|
||||
match (s, sl) with
|
||||
| "integer_range", [] -> print_integer_range env jd el al
|
||||
| _ -> raise Not_found
|
||||
and print_integer_range env jd el al =
|
||||
let i1, i2 =
|
||||
match el with
|
||||
| [ [ e1 ]; [ e2 ] ] -> (eval_int_expr env jd e1, eval_int_expr env jd e2)
|
||||
| _ -> raise Not_found
|
||||
in
|
||||
for i = i1 to i2 do
|
||||
let env = ("integer", Vint i) :: env in
|
||||
List.iter (print_ast env jd) al
|
||||
done
|
||||
in
|
||||
print_foreach
|
||||
|
||||
let print_calendar conf =
|
||||
interp conf "calendar"
|
||||
{
|
||||
Templ.eval_var = eval_var conf;
|
||||
Templ.eval_transl = (fun _ -> Templ.eval_transl conf);
|
||||
Templ.eval_predefined_apply = (fun _ -> raise Not_found);
|
||||
Templ.get_vother;
|
||||
Templ.set_vother;
|
||||
Templ.print_foreach;
|
||||
}
|
||||
[] (eval_julian_day conf)
|
||||
73
lib/hutil.mli
Normal file
73
lib/hutil.mli
Normal file
@@ -0,0 +1,73 @@
|
||||
(* Copyright (c) 2007 INRIA *)
|
||||
|
||||
open Config
|
||||
|
||||
val header_without_http : config -> (bool -> unit) -> unit
|
||||
(** [header_without_http conf title] prints HTML page header in the body of the current response on the socket.
|
||||
HTML page header consists of :
|
||||
|
||||
- <!DOCTYPE> Declaration
|
||||
- <head> tag where :
|
||||
|
||||
- content of <title> tag is printed with [title true]
|
||||
- <meta> and <link> tags are filled due to [conf]
|
||||
- content of {i css.txt} template is evaluated and printed
|
||||
- content of {i hed.txt} template is evaluated and printed
|
||||
|
||||
- Opening <body> tag with its attributes
|
||||
- If user is a wizard or a friend, then includes all messages send to him. *)
|
||||
|
||||
val header_without_page_title : config -> (bool -> unit) -> unit
|
||||
(** Calls for [Util.html] to print HTTP header and for [header_without_http] to print HTML page header.
|
||||
Additionaly prints opening container <div> tag on the socket. *)
|
||||
|
||||
val header : config -> (bool -> unit) -> unit
|
||||
(** [header conf title] calls for [header_without_page_title] to print HTTP header and HTML page header.
|
||||
Additionaly prints page title with [title true] (false to print browser tab title). *)
|
||||
|
||||
val header_no_page_title : config -> (bool -> unit) -> unit
|
||||
(** Same as [header] but takes page title from [conf.env]. *)
|
||||
|
||||
val header_fluid : config -> (bool -> unit) -> unit
|
||||
(** Prints HTML page header (without HTTP headers) and opens fluid container <div> (see Bootstrap). *)
|
||||
|
||||
val header_link_welcome : config -> (bool -> unit) -> unit
|
||||
(** Same as [header] but insert links to previous and home pages (with [print_link_to_welcome])
|
||||
before page title. *)
|
||||
|
||||
val trailer : config -> unit
|
||||
(** [trailer conf] prints HTML page trailer in the body of the current response on the socket.
|
||||
HTML page trailer consists of :
|
||||
|
||||
- Copyright message from template {i copyr.txt} with inserted logo
|
||||
- Scripts JS from template {i js.txt}
|
||||
- Closing <body> and <html> tags *)
|
||||
|
||||
val rheader : config -> (bool -> unit) -> unit
|
||||
(** Same as [header] except page's title informs about an occured error (red title). *)
|
||||
|
||||
val link_to_referer : config -> Adef.safe_string
|
||||
(** Returns the HTML link to the previous (referer) page *)
|
||||
|
||||
val gen_print_link_to_welcome : (unit -> unit) -> config -> bool -> unit
|
||||
(** [gen_print_link_to_welcome f conf right_alined] prints links to previous and to home pages.
|
||||
[f] is used to print additional content before links. *)
|
||||
|
||||
val print_link_to_welcome : config -> bool -> unit
|
||||
(** Calls [gen_print_link_to_welcome] with empty function [f]. *)
|
||||
|
||||
val incorrect_request : ?comment:string -> config -> unit
|
||||
(** Sends [Bad Request] HTTP response (same as [GWPARAM.output_error conf Bad_Request]) *)
|
||||
|
||||
(* TODOOCP *)
|
||||
val interp :
|
||||
config -> string -> ('a, 'b) Templ.interp_fun -> 'a Templ.env -> 'b -> unit
|
||||
|
||||
val interp_no_header :
|
||||
config -> string -> ('a, 'b) Templ.interp_fun -> 'a Templ.env -> 'b -> unit
|
||||
|
||||
val interp_no_env : config -> string -> unit
|
||||
|
||||
val print_calendar : config -> unit
|
||||
(** Displays the calendar; if no key is set, it will use today's date.
|
||||
Based on template file calendar.txt *)
|
||||
393
lib/image.ml
Normal file
393
lib/image.ml
Normal file
@@ -0,0 +1,393 @@
|
||||
open Config
|
||||
open Gwdb
|
||||
|
||||
let portrait_folder conf = Util.base_path [ "images" ] conf.bname
|
||||
|
||||
let carrousel_folder conf =
|
||||
Filename.concat (Util.base_path [ "src" ] conf.bname) "images"
|
||||
|
||||
(** [default_portrait_filename_of_key fn sn occ] is the default filename
|
||||
of the corresponding person's portrait. WITHOUT its file extenssion.
|
||||
e.g: default_portrait_filename_of_key "Jean Claude" "DUPOND" 3 is "jean_claude.3.dupond"
|
||||
*)
|
||||
let default_portrait_filename_of_key first_name surname occ =
|
||||
let space_to_unders = Mutil.tr ' ' '_' in
|
||||
let f = space_to_unders (Name.lower first_name) in
|
||||
let s = space_to_unders (Name.lower surname) in
|
||||
Format.sprintf "%s.%d.%s" f occ s
|
||||
|
||||
let default_portrait_filename base p =
|
||||
default_portrait_filename_of_key (p_first_name base p) (p_surname base p)
|
||||
(get_occ p)
|
||||
|
||||
let authorized_image_file_extension = [| ".jpg"; ".jpeg"; ".png"; ".gif" |]
|
||||
|
||||
let find_img_opt f =
|
||||
let exists ext =
|
||||
let fname = f ^ ext in
|
||||
if Sys.file_exists fname then Some fname else None
|
||||
in
|
||||
match exists ".url" with
|
||||
| Some f ->
|
||||
let ic = open_in f in
|
||||
let url = input_line ic in
|
||||
close_in ic;
|
||||
Some (`Url url)
|
||||
| None -> (
|
||||
match Mutil.array_find_map exists authorized_image_file_extension with
|
||||
| None -> None
|
||||
| Some f -> Some (`Path f))
|
||||
|
||||
(** [full_portrait_path conf base p] is [Some path] if [p] has a portrait.
|
||||
[path] is a the full path of the file with file extension. *)
|
||||
let full_portrait_path conf base p =
|
||||
(* TODO why is extension not in filename..? *)
|
||||
let s = default_portrait_filename base p in
|
||||
let f = Filename.concat (portrait_folder conf) s in
|
||||
match find_img_opt f with
|
||||
| Some (`Path _) as full_path -> full_path
|
||||
| Some (`Url _)
|
||||
(* should not happen, there is only ".url" file in carrousel folder *)
|
||||
| None ->
|
||||
None
|
||||
|
||||
let source_filename conf src =
|
||||
let fname1 = Filename.concat (carrousel_folder conf) src in
|
||||
if Sys.file_exists fname1 then fname1
|
||||
else
|
||||
List.fold_right Filename.concat [ Secure.base_dir (); "src"; "images" ] src
|
||||
|
||||
let path_of_filename src =
|
||||
let fname1 =
|
||||
List.fold_right Filename.concat [ Secure.base_dir (); "images" ] src
|
||||
in
|
||||
if Sys.file_exists fname1 then `Path fname1
|
||||
else `Path (Util.search_in_assets (Filename.concat "images" src))
|
||||
|
||||
let png_size ic =
|
||||
let magic = really_input_string ic 4 in
|
||||
if magic = "\137PNG" then (
|
||||
seek_in ic 16;
|
||||
let wid = input_binary_int ic in
|
||||
let hei = input_binary_int ic in
|
||||
Ok (wid, hei))
|
||||
else Error ()
|
||||
|
||||
let gif_size ic =
|
||||
let magic = really_input_string ic 4 in
|
||||
if magic = "GIF8" then (
|
||||
seek_in ic 6;
|
||||
let wid =
|
||||
let x = input_byte ic in
|
||||
(input_byte ic * 256) + x
|
||||
in
|
||||
let hei =
|
||||
let x = input_byte ic in
|
||||
(input_byte ic * 256) + x
|
||||
in
|
||||
Ok (wid, hei))
|
||||
else Error ()
|
||||
|
||||
let jpeg_size ic =
|
||||
let magic = really_input_string ic 10 in
|
||||
if
|
||||
Char.code magic.[0] = 0xff
|
||||
&& Char.code magic.[1] = 0xd8
|
||||
&&
|
||||
let m = String.sub magic 6 4 in
|
||||
m = "JFIF" || m = "Exif"
|
||||
then
|
||||
let exif_type = String.sub magic 6 4 = "Exif" in
|
||||
let rec loop found =
|
||||
while Char.code (input_char ic) <> 0xFF do
|
||||
()
|
||||
done;
|
||||
let ch =
|
||||
let rec loop ch =
|
||||
if Char.code ch = 0xFF then loop (input_char ic) else ch
|
||||
in
|
||||
loop (input_char ic)
|
||||
in
|
||||
if Char.code ch = 0xC0 || Char.code ch = 0xC3 then
|
||||
if exif_type && not found then loop true
|
||||
else (
|
||||
for _i = 1 to 3 do
|
||||
ignore @@ input_char ic
|
||||
done;
|
||||
let a = input_char ic in
|
||||
let b = input_char ic in
|
||||
let c = input_char ic in
|
||||
let d = input_char ic in
|
||||
let wid = (Char.code c lsl 8) lor Char.code d in
|
||||
let hei = (Char.code a lsl 8) lor Char.code b in
|
||||
Ok (wid, hei))
|
||||
else
|
||||
let a = input_char ic in
|
||||
let b = input_char ic in
|
||||
let len = (Char.code a lsl 8) lor Char.code b in
|
||||
let len = if len >= 32768 then 0 else len in
|
||||
for _i = 1 to len - 2 do
|
||||
ignore @@ input_char ic
|
||||
done;
|
||||
if Char.code ch <> 0xDA then loop found else Error ()
|
||||
in
|
||||
loop false
|
||||
else Error ()
|
||||
|
||||
let size_from_path fname =
|
||||
(* TODO: size and mime type should be in db *)
|
||||
let (`Path fname) = fname in
|
||||
let res =
|
||||
if fname = "" then Error ()
|
||||
else
|
||||
try
|
||||
let ic = Secure.open_in_bin fname in
|
||||
let r =
|
||||
try
|
||||
(* TODO: should match on mime type here *)
|
||||
match String.lowercase_ascii @@ Filename.extension fname with
|
||||
| ".jpeg" | ".jpg" -> jpeg_size ic
|
||||
| ".png" -> png_size ic
|
||||
| ".gif" -> gif_size ic
|
||||
| _s -> Error ()
|
||||
with End_of_file -> Error ()
|
||||
in
|
||||
close_in ic;
|
||||
r
|
||||
with Sys_error _e -> Error ()
|
||||
in
|
||||
res
|
||||
|
||||
let src_to_string = function `Url s | `Path s -> s
|
||||
|
||||
let scale_to_fit ~max_w ~max_h ~w ~h =
|
||||
let w, h =
|
||||
if h > max_h then
|
||||
let w = w * max_h / h in
|
||||
let h = max_h in
|
||||
(w, h)
|
||||
else (w, h)
|
||||
in
|
||||
let w, h =
|
||||
if w > max_w then
|
||||
let h = h * max_w / w in
|
||||
let w = max_w in
|
||||
(w, h)
|
||||
else (w, h)
|
||||
in
|
||||
(w, h)
|
||||
|
||||
let is_not_private_img _conf fname =
|
||||
not (Mutil.contains fname ("private" ^ Filename.dir_sep))
|
||||
|
||||
(** [has_access_to_portrait conf base p] is true iif we can see [p]'s portrait. *)
|
||||
let has_access_to_portrait conf base p =
|
||||
let img = get_image p in
|
||||
(conf.wizard || conf.friend)
|
||||
|| (not conf.no_image)
|
||||
&& Util.authorized_age conf base p
|
||||
&& ((not (is_empty_string img)) || full_portrait_path conf base p <> None)
|
||||
&& is_not_private_img conf (sou base img)
|
||||
(* TODO: privacy settings should be in db not in url *)
|
||||
|
||||
(** [has_access_to_carrousel conf base p] is true iif ???. *)
|
||||
let has_access_to_carrousel conf base p =
|
||||
(conf.wizard || conf.friend)
|
||||
|| (not conf.no_image)
|
||||
&& Util.authorized_age conf base p
|
||||
&& not (Util.is_hide_names conf p)
|
||||
|
||||
let get_portrait_path conf base p =
|
||||
if has_access_to_portrait conf base p then full_portrait_path conf base p
|
||||
else None
|
||||
|
||||
(* parse a string to an `Url or a `Path *)
|
||||
let urlorpath_of_string conf s =
|
||||
let http = "http://" in
|
||||
let https = "https://" in
|
||||
if Mutil.start_with http 0 s || Mutil.start_with https 0 s then `Url s
|
||||
else if Filename.is_implicit s then
|
||||
match List.assoc_opt "images_path" conf.base_env with
|
||||
| Some p when p <> "" -> `Path (Filename.concat p s)
|
||||
| Some _ | None ->
|
||||
let fname = Filename.concat (portrait_folder conf) s in
|
||||
`Path fname
|
||||
else `Path s
|
||||
|
||||
let src_of_string conf s =
|
||||
if s = "" then `Empty
|
||||
else
|
||||
let l = String.length s - 1 in
|
||||
if s.[l] = ')' then `Src_with_size_info s else urlorpath_of_string conf s
|
||||
|
||||
let parse_src_with_size_info conf s =
|
||||
let (`Src_with_size_info s) = s in
|
||||
let l = String.length s - 1 in
|
||||
try
|
||||
let pos1 = String.index s '(' in
|
||||
let pos2 = String.index_from s pos1 'x' in
|
||||
let w = String.sub s (pos1 + 1) (pos2 - pos1 - 1) |> int_of_string in
|
||||
let h = String.sub s (pos2 + 1) (l - pos2 - 1) |> int_of_string in
|
||||
let s = String.sub s 0 pos1 in
|
||||
Ok (urlorpath_of_string conf s, (w, h))
|
||||
with Not_found | Failure _ ->
|
||||
!GWPARAM.syslog `LOG_ERR
|
||||
(Format.sprintf "Error parsing portrait source with size info %s" s);
|
||||
Error "Failed to parse url with size info"
|
||||
|
||||
let get_portrait conf base p =
|
||||
if has_access_to_portrait conf base p then
|
||||
match src_of_string conf (sou base (get_image p)) with
|
||||
| `Src_with_size_info _s as s_info -> (
|
||||
match parse_src_with_size_info conf s_info with
|
||||
| Error _e -> None
|
||||
| Ok (s, _size) -> Some s)
|
||||
| `Url _s as url -> Some url
|
||||
| `Path p as path -> if Sys.file_exists p then Some path else None
|
||||
| `Empty -> full_portrait_path conf base p
|
||||
else None
|
||||
|
||||
(* In images/carrousel we store either
|
||||
- the image as the original image.jpg/png/tif image
|
||||
- the url to the image as content of a image.url text file
|
||||
*)
|
||||
let get_old_portrait conf base p =
|
||||
if has_access_to_portrait conf base p then
|
||||
let key = default_portrait_filename base p in
|
||||
let f =
|
||||
Filename.concat (Filename.concat (portrait_folder conf) "old") key
|
||||
in
|
||||
find_img_opt f
|
||||
else None
|
||||
|
||||
let rename_portrait conf base p (nfn, nsn, noc) =
|
||||
match get_portrait conf base p with
|
||||
| Some (`Path old_f) -> (
|
||||
let new_s = default_portrait_filename_of_key nfn nsn noc in
|
||||
let old_s = default_portrait_filename base p in
|
||||
let f = Filename.concat (portrait_folder conf) new_s in
|
||||
let old_ext = Filename.extension old_f in
|
||||
let new_f = f ^ old_ext in
|
||||
(try Sys.rename old_f new_f
|
||||
with Sys_error e ->
|
||||
!GWPARAM.syslog `LOG_ERR
|
||||
(Format.sprintf
|
||||
"Error renaming portrait: old_path=%s new_path=%s : %s" old_f
|
||||
new_f e));
|
||||
let new_s_f =
|
||||
String.concat Filename.dir_sep [ portrait_folder conf; "old"; new_s ]
|
||||
in
|
||||
let old_s_f =
|
||||
String.concat Filename.dir_sep [ portrait_folder conf; "old"; old_s ]
|
||||
in
|
||||
(if Sys.file_exists (old_s_f ^ old_ext) then
|
||||
try Sys.rename (old_s_f ^ old_ext) (new_s_f ^ old_ext)
|
||||
with Sys_error e ->
|
||||
!GWPARAM.syslog `LOG_ERR
|
||||
(Format.sprintf
|
||||
"Error renaming old portrait: old_path=%s new_path=%s : %s" old_f
|
||||
new_f e));
|
||||
let new_s_f =
|
||||
String.concat Filename.dir_sep [ carrousel_folder conf; new_s ]
|
||||
in
|
||||
let old_s_f =
|
||||
String.concat Filename.dir_sep [ carrousel_folder conf; old_s ]
|
||||
in
|
||||
if Sys.file_exists old_s_f then
|
||||
try Sys.rename old_s_f new_s_f
|
||||
with Sys_error e ->
|
||||
!GWPARAM.syslog `LOG_ERR
|
||||
(Format.sprintf
|
||||
"Error renaming carrousel store: old_path=%s new_path=%s : %s"
|
||||
old_f new_f e))
|
||||
| Some (`Url _url) -> () (* old url still applies *)
|
||||
| None -> ()
|
||||
|
||||
let get_portrait_with_size conf base p =
|
||||
if has_access_to_portrait conf base p then
|
||||
match src_of_string conf (sou base (get_image p)) with
|
||||
| `Src_with_size_info _s as s_info -> (
|
||||
match parse_src_with_size_info conf s_info with
|
||||
| Error _e -> None
|
||||
| Ok (s, size) -> Some (s, Some size))
|
||||
| `Url _s as url -> Some (url, None)
|
||||
| `Path p as path ->
|
||||
if Sys.file_exists p then
|
||||
Some (path, size_from_path path |> Result.to_option)
|
||||
else None
|
||||
| `Empty -> (
|
||||
match full_portrait_path conf base p with
|
||||
| None -> None
|
||||
| Some path -> Some (path, size_from_path path |> Result.to_option))
|
||||
else None
|
||||
|
||||
(* For carrousel ************************************ *)
|
||||
|
||||
let carrousel_file_path conf base p fname old =
|
||||
let dir =
|
||||
let dir = default_portrait_filename base p in
|
||||
if old then Filename.concat dir "old" else dir
|
||||
in
|
||||
String.concat Filename.dir_sep
|
||||
([ carrousel_folder conf; dir ] @ if fname = "" then [] else [ fname ])
|
||||
|
||||
let get_carrousel_file_content conf base p fname kind old =
|
||||
let fname =
|
||||
Filename.chop_extension (carrousel_file_path conf base p fname old) ^ kind
|
||||
in
|
||||
if Sys.file_exists fname then (
|
||||
let ic = Secure.open_in fname in
|
||||
let s = really_input_string ic (in_channel_length ic) in
|
||||
close_in ic;
|
||||
if s = "" then None else Some s)
|
||||
else None
|
||||
|
||||
(* get list of files in carrousel *)
|
||||
let get_carrousel_img_aux conf base p old =
|
||||
let get_carrousel_img_note fname =
|
||||
Option.value ~default:""
|
||||
(get_carrousel_file_content conf base p fname ".txt" false)
|
||||
in
|
||||
let get_carrousel_img_src fname =
|
||||
Option.value ~default:""
|
||||
(get_carrousel_file_content conf base p fname ".src" false)
|
||||
in
|
||||
let get_carrousel_img fname =
|
||||
let path = carrousel_file_path conf base p fname old in
|
||||
find_img_opt (Filename.chop_extension path)
|
||||
in
|
||||
if not (has_access_to_carrousel conf base p) then []
|
||||
else
|
||||
let f = carrousel_file_path conf base p "" old in
|
||||
try
|
||||
if Sys.file_exists f && Sys.is_directory f then
|
||||
Array.fold_left
|
||||
(fun acc f1 ->
|
||||
let ext = Filename.extension f1 in
|
||||
if
|
||||
f1 <> ""
|
||||
&& f1.[0] <> '.'
|
||||
&& (Array.mem ext authorized_image_file_extension || ext = ".url")
|
||||
then
|
||||
match get_carrousel_img f1 with
|
||||
| None -> acc
|
||||
| Some (`Path path) ->
|
||||
(path, "", get_carrousel_img_src f1, get_carrousel_img_note f1)
|
||||
:: acc
|
||||
| Some (`Url url) ->
|
||||
( Filename.chop_extension (Filename.basename f1) ^ ".url",
|
||||
url,
|
||||
get_carrousel_img_src f1,
|
||||
get_carrousel_img_note f1 )
|
||||
:: acc
|
||||
else acc)
|
||||
[] (Sys.readdir f)
|
||||
else []
|
||||
with Sys_error e ->
|
||||
!GWPARAM.syslog `LOG_ERR (Format.sprintf "carrousel error: %s, %s" f e);
|
||||
[]
|
||||
|
||||
let get_carrousel_imgs conf base p = get_carrousel_img_aux conf base p false
|
||||
let get_carrousel_old_imgs conf base p = get_carrousel_img_aux conf base p true
|
||||
|
||||
(* end carrousel ************************************ *)
|
||||
75
lib/image.mli
Normal file
75
lib/image.mli
Normal file
@@ -0,0 +1,75 @@
|
||||
open Config
|
||||
open Gwdb
|
||||
|
||||
val portrait_folder : config -> string
|
||||
val carrousel_folder : config -> string
|
||||
val authorized_image_file_extension : string array
|
||||
|
||||
val scale_to_fit : max_w:int -> max_h:int -> w:int -> h:int -> int * int
|
||||
(** [scale_to_fit ~max_w ~max_h ~w ~h] is the {(width, height)} of a proportionally scaled {(w, h)} rectangle so it can fit in a {(max_w, max_h)} rectangle *)
|
||||
|
||||
val source_filename : config -> string -> string
|
||||
(** Returns path to the image file with the giving name in directory {i src/}. *)
|
||||
|
||||
(* TODO this should be removed *)
|
||||
val default_portrait_filename : base -> person -> string
|
||||
(** [default_portrait_filename base p] is the default filename of [p]'s portrait. Without it's file extension.
|
||||
e.g: default_portrait_filename_of_key "Jean Claude" "DUPOND" 3 is "jean_claude.3.dupond"
|
||||
*)
|
||||
|
||||
val size_from_path : [ `Path of string ] -> (int * int, unit) result
|
||||
(** [size_from_path path]
|
||||
- Error () if failed to read or parse file
|
||||
- Ok (width, height) of the file.
|
||||
It works by opening the file and reading magic numbers *)
|
||||
|
||||
val path_of_filename : string -> [> `Path of string ]
|
||||
(** [path_of_filename fname] search for image {i images/fname} inside the base and assets directories.
|
||||
Return the path to found file or [fname] if file isn't found. *)
|
||||
|
||||
val rename_portrait : config -> base -> person -> string * string * int -> unit
|
||||
(** Rename portrait to match updated name *)
|
||||
|
||||
val src_to_string : [< `Path of string | `Url of string ] -> string
|
||||
(** [src_to_string src] is [src] as a string *)
|
||||
|
||||
(* TODO this should be removed *)
|
||||
val get_portrait_path : config -> base -> person -> [> `Path of string ] option
|
||||
(** [get_portrait_path conf base p] is
|
||||
- [None] if we don't have access to [p]'s portrait or it doesn't exist.
|
||||
- [Some path] with [path] the full path with extension of [p]'s portrait.
|
||||
*)
|
||||
|
||||
val get_portrait_with_size :
|
||||
config ->
|
||||
base ->
|
||||
person ->
|
||||
([> `Path of string | `Url of string ] * (int * int) option) option
|
||||
(** [get_portrait_with_size conf base p] is
|
||||
- [None] if we don't have access to [p]'s portrait or it doesn't exist.
|
||||
- [Some (src, size_opt)] with [src] the url or path of [p]'s portrait. [size_opt] is the (width,height) of the portrait if we could recover them *)
|
||||
|
||||
val get_portrait :
|
||||
config -> base -> person -> [> `Path of string | `Url of string ] option
|
||||
(** [get_portrait conf base p] is
|
||||
- [None] if we don't have access to [p]'s portrait or it doesn't exist.
|
||||
- [Some src] with [src] the url or path of [p]'s portrait.
|
||||
*)
|
||||
|
||||
val get_old_portrait :
|
||||
config -> base -> person -> [> `Path of string | `Url of string ] option
|
||||
(** [get_portrait conf base p] is
|
||||
- [None] if we don't have access to [p]'s portrait or it doesn't exist.
|
||||
- [Some src] with [src] the url or path of [p]'s portrait.
|
||||
*)
|
||||
|
||||
(* -- Carrousel -- *)
|
||||
|
||||
val get_carrousel_imgs :
|
||||
config -> base -> person -> (string * string * string * string) list
|
||||
|
||||
val get_carrousel_old_imgs :
|
||||
config -> base -> person -> (string * string * string * string) list
|
||||
|
||||
val is_not_private_img : config -> string -> bool
|
||||
(** determines if image is private (pathname contains "private/") *)
|
||||
756
lib/imageCarrousel.ml
Normal file
756
lib/imageCarrousel.ml
Normal file
@@ -0,0 +1,756 @@
|
||||
open Config
|
||||
open Def
|
||||
open Gwdb
|
||||
open Util
|
||||
|
||||
let rn fname s =
|
||||
try if Sys.file_exists fname then Sys.rename fname s
|
||||
with Failure _ ->
|
||||
Printf.eprintf "Rn failed: %s to %s\n" fname s;
|
||||
flush stderr
|
||||
|
||||
type image_type = JPEG | GIF | PNG
|
||||
|
||||
let extension_of_type = function
|
||||
| JPEG -> ".jpg"
|
||||
| GIF -> ".gif"
|
||||
| PNG -> ".png"
|
||||
|
||||
let image_types = [ JPEG; GIF; PNG ]
|
||||
let raise_modErr s = raise @@ Update.ModErr (Update.UERR s)
|
||||
|
||||
let incorrect conf str =
|
||||
Hutil.incorrect_request conf ~comment:str;
|
||||
failwith (__FILE__ ^ " (" ^ str ^ ")" :> string)
|
||||
|
||||
let incorrect_content_type conf base p s =
|
||||
let title _ =
|
||||
Output.print_sstring conf (Utf8.capitalize (Util.transl conf "error"))
|
||||
in
|
||||
Hutil.rheader conf title;
|
||||
Hutil.print_link_to_welcome conf true;
|
||||
Output.print_sstring conf "<p>\n<em style=\"font-size:smaller\">";
|
||||
Output.printf conf "Error: incorrect image content type: %s" s;
|
||||
Output.printf conf "</em>\n</p>\n<ul>\n<li>\n%s</li>\n</ul>\n"
|
||||
(Util.referenced_person_title_text conf base p :> string);
|
||||
Hutil.trailer conf;
|
||||
failwith (__FILE__ ^ " " ^ string_of_int __LINE__ :> string)
|
||||
|
||||
let error_too_big_image conf base p len max_len =
|
||||
let title _ =
|
||||
Output.print_sstring conf (Utf8.capitalize (Util.transl conf "error"))
|
||||
in
|
||||
Hutil.rheader conf title;
|
||||
Hutil.print_link_to_welcome conf true;
|
||||
Output.print_sstring conf "<p><em style=\"font-size:smaller\">";
|
||||
Output.printf conf "Error: this image is too big: %d bytes<br>\n" len;
|
||||
Output.printf conf "Maximum authorized in this database: %d bytes<br>\n"
|
||||
max_len;
|
||||
Output.printf conf "</em></p>\n<ul>\n<li>\n%s</li>\n</ul>\n"
|
||||
(Util.referenced_person_title_text conf base p :> string);
|
||||
Hutil.trailer conf;
|
||||
failwith (__FILE__ ^ " " ^ string_of_int __LINE__ :> string)
|
||||
|
||||
let raw_get conf key =
|
||||
try List.assoc key conf.env
|
||||
with Not_found -> incorrect conf ("raw_get" ^ key)
|
||||
|
||||
let insert_saved fname =
|
||||
let l = String.split_on_char Filename.dir_sep.[0] fname |> List.rev in
|
||||
let l = List.rev @@ match l with h :: t -> h :: "old" :: t | _ -> l in
|
||||
String.concat Filename.dir_sep l
|
||||
|
||||
let write_file fname content =
|
||||
let oc = Secure.open_out_bin fname in
|
||||
output_string oc content;
|
||||
flush oc;
|
||||
close_out oc
|
||||
|
||||
let move_file_to_save file dir =
|
||||
(* previous version iterated on file types *)
|
||||
try
|
||||
let save_dir = Filename.concat dir "old" in
|
||||
if not (Sys.file_exists save_dir) then Mutil.mkdir_p save_dir;
|
||||
let fname = Filename.basename file in
|
||||
let orig_file = Filename.concat dir fname in
|
||||
let saved_file = Filename.concat save_dir fname in
|
||||
(* TODO handle rn errors *)
|
||||
rn orig_file saved_file;
|
||||
let orig_file_t = Filename.remove_extension orig_file ^ ".txt" in
|
||||
let saved_file_t = Filename.remove_extension saved_file ^ ".txt" in
|
||||
if Sys.file_exists orig_file_t then rn orig_file_t saved_file_t;
|
||||
let orig_file_s = Filename.remove_extension orig_file ^ ".src" in
|
||||
let saved_file_s = Filename.remove_extension saved_file ^ ".src" in
|
||||
if Sys.file_exists orig_file_s then rn orig_file_s saved_file_s;
|
||||
1
|
||||
with _ -> 0
|
||||
|
||||
let normal_image_type s =
|
||||
if String.length s > 10 && Char.code s.[0] = 0xff && Char.code s.[1] = 0xd8
|
||||
then Some JPEG
|
||||
else if String.length s > 4 && String.sub s 0 4 = "\137PNG" then Some PNG
|
||||
else if String.length s > 4 && String.sub s 0 4 = "GIF8" then Some GIF
|
||||
else None
|
||||
|
||||
let string_search s v =
|
||||
let rec loop i j =
|
||||
if j = String.length v then Some (i - String.length v)
|
||||
else if i = String.length s then None
|
||||
else if s.[i] = v.[j] then loop (i + 1) (j + 1)
|
||||
else loop (i + 1) 0
|
||||
in
|
||||
loop 0 0
|
||||
|
||||
(* get the image type, possibly removing spurious header *)
|
||||
|
||||
let image_type s =
|
||||
match normal_image_type s with
|
||||
| Some t -> Some (t, s)
|
||||
| None -> (
|
||||
match string_search s "JFIF" with
|
||||
| Some i when i > 6 ->
|
||||
let s = String.sub s (i - 6) (String.length s - i + 6) in
|
||||
Some (JPEG, s)
|
||||
| _ -> (
|
||||
match string_search s "\137PNG" with
|
||||
| Some i ->
|
||||
let s = String.sub s i (String.length s - i) in
|
||||
Some (PNG, s)
|
||||
| _ -> (
|
||||
match string_search s "GIF8" with
|
||||
| Some i ->
|
||||
let s = String.sub s i (String.length s - i) in
|
||||
Some (GIF, s)
|
||||
| None -> None)))
|
||||
|
||||
let dump_bad_image conf s =
|
||||
match List.assoc_opt "dump_bad_images" conf.base_env with
|
||||
| Some "yes" -> (
|
||||
try
|
||||
(* Where will "bad-image"end up? *)
|
||||
let oc = Secure.open_out_bin "bad-image" in
|
||||
output_string oc s;
|
||||
flush oc;
|
||||
close_out oc
|
||||
with Sys_error _ -> ())
|
||||
| _ -> ()
|
||||
|
||||
(* swap files between new and old folder *)
|
||||
(* [| ".jpg"; ".jpeg"; ".png"; ".gif" |] *)
|
||||
|
||||
let swap_files_aux dir file ext old_ext =
|
||||
let old_file =
|
||||
String.concat Filename.dir_sep [ dir; "old"; Filename.basename file ]
|
||||
in
|
||||
let tmp_file = String.concat Filename.dir_sep [ dir; "tempfile.tmp" ] in
|
||||
if ext <> old_ext then (
|
||||
if Sys.file_exists file then rn file (Filename.chop_extension old_file ^ ext);
|
||||
if Sys.file_exists old_file then
|
||||
rn old_file (Filename.chop_extension file ^ old_ext))
|
||||
else (
|
||||
if Sys.file_exists file then rn file tmp_file;
|
||||
if Sys.file_exists old_file then rn old_file file;
|
||||
if Sys.file_exists tmp_file then rn tmp_file old_file)
|
||||
|
||||
let swap_files file ext old_ext =
|
||||
let dir = Filename.dirname file in
|
||||
let fname = Filename.basename file in
|
||||
swap_files_aux dir file ext old_ext;
|
||||
let txt_file =
|
||||
String.concat Filename.dir_sep
|
||||
[ dir; Filename.chop_extension fname ^ ".txt" ]
|
||||
in
|
||||
swap_files_aux dir txt_file ext old_ext;
|
||||
let src_file =
|
||||
String.concat Filename.dir_sep
|
||||
[ dir; Filename.chop_extension fname ^ ".src" ]
|
||||
in
|
||||
swap_files_aux dir src_file ext old_ext
|
||||
|
||||
let clean_saved_portrait file =
|
||||
let file = Filename.remove_extension file in
|
||||
Array.iter
|
||||
(fun ext -> Mutil.rm (file ^ ext))
|
||||
Image.authorized_image_file_extension
|
||||
|
||||
let get_extension conf saved fname =
|
||||
let f =
|
||||
if saved then
|
||||
String.concat Filename.dir_sep
|
||||
[ Util.base_path [ "images" ] conf.bname; "old"; fname ]
|
||||
else
|
||||
String.concat Filename.dir_sep
|
||||
[ Util.base_path [ "images" ] conf.bname; fname ]
|
||||
in
|
||||
if Sys.file_exists (f ^ ".jpg") then ".jpg"
|
||||
else if Sys.file_exists (f ^ ".jpeg") then ".jpeg"
|
||||
else if Sys.file_exists (f ^ ".png") then ".png"
|
||||
else if Sys.file_exists (f ^ ".gif") then ".gif"
|
||||
else if Sys.file_exists (f ^ ".url") then ".url"
|
||||
else "."
|
||||
|
||||
let print_confirm_c conf base save_m report =
|
||||
match Util.p_getint conf.env "i" with
|
||||
| Some ip ->
|
||||
let p = poi base (Gwdb.iper_of_string (string_of_int ip)) in
|
||||
let digest = Image.default_portrait_filename base p in
|
||||
let new_env =
|
||||
List.fold_left
|
||||
(fun accu (k, v) ->
|
||||
if k = "m" then ("m", Adef.encoded "REFRESH") :: accu
|
||||
else if k = "idigest" || k = "" || k = "file" then accu
|
||||
else (k, v) :: accu)
|
||||
[] conf.env
|
||||
in
|
||||
let new_env =
|
||||
if save_m = "REFRESH" then new_env
|
||||
else ("em", Adef.encoded save_m) :: new_env
|
||||
in
|
||||
let new_env =
|
||||
("idigest", Adef.encoded digest)
|
||||
:: ("report", Adef.encoded report)
|
||||
:: new_env
|
||||
in
|
||||
let conf = { conf with env = new_env } in
|
||||
Perso.interp_templ "carrousel" conf base p
|
||||
| None -> Hutil.incorrect_request conf
|
||||
|
||||
(* ************************************************************************ *)
|
||||
(* send, delete, reset and print functions *)
|
||||
(* *)
|
||||
(* ************************************************************************ *)
|
||||
|
||||
(* we need print_link_delete_image in the send function *)
|
||||
let print_link_delete_image conf base p =
|
||||
if Option.is_some @@ Image.get_portrait conf base p then (
|
||||
Output.print_sstring conf {|<p><a class="btn btn-primary" href="|};
|
||||
Output.print_string conf (commd conf);
|
||||
Output.print_sstring conf "m=DEL_IMAGE&i=";
|
||||
Output.print_string conf (get_iper p |> string_of_iper |> Mutil.encode);
|
||||
Output.print_sstring conf {|">|};
|
||||
transl conf "delete" |> Utf8.capitalize_fst |> Output.print_sstring conf;
|
||||
Output.print_sstring conf {| |};
|
||||
transl_nth conf "image/images" 0 |> Output.print_sstring conf;
|
||||
Output.print_sstring conf "</a></p>")
|
||||
|
||||
let print_send_image conf base p =
|
||||
let title h =
|
||||
if Option.is_some @@ Image.get_portrait conf base p then
|
||||
transl_nth conf "image/images" 0
|
||||
|> transl_decline conf "modify"
|
||||
|> Utf8.capitalize_fst |> Output.print_sstring conf
|
||||
else
|
||||
transl_nth conf "image/images" 0
|
||||
|> transl_decline conf "add" |> Utf8.capitalize_fst
|
||||
|> Output.print_sstring conf;
|
||||
if not h then (
|
||||
Output.print_sstring conf (transl conf ":");
|
||||
Output.print_sstring conf " ";
|
||||
Output.print_string conf (Util.escape_html (p_first_name base p));
|
||||
Output.print_sstring conf (Format.sprintf ".%d " (get_occ p));
|
||||
Output.print_string conf (Util.escape_html (p_surname base p)))
|
||||
in
|
||||
let digest = Update.digest_person (UpdateInd.string_person_of base p) in
|
||||
Perso.interp_notempl_with_menu title "perso_header" conf base p;
|
||||
Output.print_sstring conf "<h2>\n";
|
||||
title false;
|
||||
Output.print_sstring conf "</h2>\n";
|
||||
Output.printf conf
|
||||
"<form method=\"post\" action=\"%s\" enctype=\"multipart/form-data\">\n"
|
||||
conf.command;
|
||||
Output.print_sstring conf "<p>\n";
|
||||
Util.hidden_env conf;
|
||||
Util.hidden_input conf "m" (Adef.encoded "SND_IMAGE_OK");
|
||||
Util.hidden_input conf "i" (get_iper p |> string_of_iper |> Mutil.encode);
|
||||
Util.hidden_input conf "digest" (Mutil.encode digest);
|
||||
Output.print_sstring conf (Utf8.capitalize_fst (transl conf "file"));
|
||||
Output.print_sstring conf (Util.transl conf ":");
|
||||
Output.print_sstring conf " ";
|
||||
Output.print_sstring conf
|
||||
{| <input type="file" class="form-control-file" name="file" accept="image/*"></p>|};
|
||||
(match
|
||||
Option.map int_of_string @@ List.assoc_opt "max_images_size" conf.base_env
|
||||
with
|
||||
| Some len ->
|
||||
Output.print_sstring conf "<p>(maximum authorized size = ";
|
||||
Output.print_sstring conf (string_of_int len);
|
||||
Output.print_sstring conf " bytes)</p>"
|
||||
| None -> ());
|
||||
Output.print_sstring conf
|
||||
{|<button type="submit" class="btn btn-primary mt-2">|};
|
||||
transl_nth conf "validate/delete" 0
|
||||
|> Utf8.capitalize_fst |> Output.print_sstring conf;
|
||||
Output.print_sstring conf "</button></form>";
|
||||
print_link_delete_image conf base p;
|
||||
Hutil.trailer conf
|
||||
|
||||
let print_sent conf base p =
|
||||
let title _ =
|
||||
transl conf "image received"
|
||||
|> Utf8.capitalize_fst |> Output.print_sstring conf
|
||||
in
|
||||
Hutil.header conf title;
|
||||
Output.print_sstring conf "<ul><li>";
|
||||
Output.print_string conf (referenced_person_text conf base p);
|
||||
Output.print_sstring conf "</li></ul>";
|
||||
Hutil.trailer conf
|
||||
|
||||
let effective_send_ok conf base p file =
|
||||
let mode =
|
||||
try (List.assoc "mode" conf.env :> string) with Not_found -> "portraits"
|
||||
in
|
||||
let strm = Stream.of_string file in
|
||||
let request, content = Wserver.get_request_and_content strm in
|
||||
let content =
|
||||
let s =
|
||||
let rec loop len (strm__ : _ Stream.t) =
|
||||
match Stream.peek strm__ with
|
||||
| Some x ->
|
||||
Stream.junk strm__;
|
||||
loop (Buff.store len x) strm
|
||||
| _ -> Buff.get len
|
||||
in
|
||||
loop 0 strm
|
||||
in
|
||||
(content :> string) ^ s
|
||||
in
|
||||
let typ, content =
|
||||
match image_type content with
|
||||
| None ->
|
||||
dump_bad_image conf content;
|
||||
Mutil.extract_param "content-type: " '\n' request
|
||||
|> incorrect_content_type conf base p
|
||||
| Some (typ, content) -> (
|
||||
match
|
||||
Option.map int_of_string
|
||||
@@ List.assoc_opt "max_images_size" conf.base_env
|
||||
with
|
||||
| Some len when String.length content > len ->
|
||||
error_too_big_image conf base p (String.length content) len
|
||||
| _ -> (typ, content))
|
||||
in
|
||||
let fname = Image.default_portrait_filename base p in
|
||||
let dir = Util.base_path [ "images" ] conf.bname in
|
||||
if not (Sys.file_exists dir) then Mutil.mkdir_p dir;
|
||||
let fname =
|
||||
Filename.concat dir
|
||||
(if mode = "portraits" then fname ^ extension_of_type typ else fname)
|
||||
in
|
||||
let _moved = move_file_to_save fname dir in
|
||||
write_file fname content;
|
||||
let changed =
|
||||
U_Send_image (Util.string_gen_person base (gen_person_of_person p))
|
||||
in
|
||||
History.record conf base changed "si";
|
||||
print_sent conf base p
|
||||
|
||||
let print_send_ok conf base =
|
||||
let ip =
|
||||
try raw_get conf "i" |> Mutil.decode |> iper_of_string
|
||||
with Failure _ -> incorrect conf "print send ok"
|
||||
in
|
||||
let p = poi base ip in
|
||||
let digest = Update.digest_person (UpdateInd.string_person_of base p) in
|
||||
if (digest :> string) = Mutil.decode (raw_get conf "digest") then
|
||||
raw_get conf "file" |> Adef.as_string |> effective_send_ok conf base p
|
||||
else Update.error_digest conf
|
||||
|
||||
(* carrousel *)
|
||||
let effective_send_c_ok conf base p file file_name =
|
||||
let mode =
|
||||
try (List.assoc "mode" conf.env :> string) with Not_found -> "portraits"
|
||||
in
|
||||
let image_url =
|
||||
try (List.assoc "image_url" conf.env :> string) with Not_found -> ""
|
||||
in
|
||||
let image_name =
|
||||
try (List.assoc "image_name" conf.env :> string) with Not_found -> ""
|
||||
in
|
||||
let note =
|
||||
match Util.p_getenv conf.env "note" with
|
||||
| Some v ->
|
||||
Util.safe_html
|
||||
(Util.only_printable_or_nl (Mutil.strip_all_trailing_spaces v))
|
||||
| None -> Adef.safe ""
|
||||
in
|
||||
let source =
|
||||
match Util.p_getenv conf.env "source" with
|
||||
| Some v ->
|
||||
Util.safe_html
|
||||
(Util.only_printable_or_nl (Mutil.strip_all_trailing_spaces v))
|
||||
| None -> Adef.safe ""
|
||||
in
|
||||
let strm = Stream.of_string file in
|
||||
let request, content = Wserver.get_request_and_content strm in
|
||||
let content =
|
||||
if mode = "note" || mode = "source" || image_url <> "" then ""
|
||||
else
|
||||
let s =
|
||||
let rec loop len (strm__ : _ Stream.t) =
|
||||
match Stream.peek strm__ with
|
||||
| Some x ->
|
||||
Stream.junk strm__;
|
||||
loop (Buff.store len x) strm
|
||||
| _ -> Buff.get len
|
||||
in
|
||||
loop 0 strm
|
||||
in
|
||||
(content :> string) ^ s
|
||||
in
|
||||
let typ, content =
|
||||
if content <> "" then
|
||||
match image_type content with
|
||||
| None ->
|
||||
let ct = Mutil.extract_param "Content-Type: " '\n' request in
|
||||
dump_bad_image conf content;
|
||||
incorrect_content_type conf base p ct
|
||||
| Some (typ, content) -> (
|
||||
match List.assoc_opt "max_images_size" conf.base_env with
|
||||
| Some len when String.length content > int_of_string len ->
|
||||
error_too_big_image conf base p (String.length content)
|
||||
(int_of_string len)
|
||||
| _ -> (typ, content))
|
||||
else (GIF, content (* we dont care which type, content = "" *))
|
||||
in
|
||||
let fname = Image.default_portrait_filename base p in
|
||||
let dir =
|
||||
if mode = "portraits" then
|
||||
String.concat Filename.dir_sep [ Util.base_path [ "images" ] conf.bname ]
|
||||
else
|
||||
String.concat Filename.dir_sep
|
||||
[ Util.base_path [ "src" ] conf.bname; "images"; fname ]
|
||||
in
|
||||
if not (Sys.file_exists dir) then Mutil.mkdir_p dir;
|
||||
let fname =
|
||||
Filename.concat dir
|
||||
(if mode = "portraits" then fname ^ extension_of_type typ else file_name)
|
||||
in
|
||||
if mode = "portraits" then
|
||||
match Image.get_portrait conf base p with
|
||||
| Some (`Path portrait) ->
|
||||
if move_file_to_save portrait dir = 0 then
|
||||
incorrect conf "effective send (portrait)"
|
||||
| Some (`Url url) -> (
|
||||
let fname = Image.default_portrait_filename base p in
|
||||
let dir = Filename.concat dir "old" in
|
||||
if not (Sys.file_exists dir) then Mutil.mkdir_p dir;
|
||||
let fname = Filename.concat dir fname ^ ".url" in
|
||||
try write_file fname url
|
||||
with _ ->
|
||||
incorrect conf
|
||||
(Printf.sprintf "effective send (effective send url portrait %s)"
|
||||
fname)
|
||||
(* TODO update person to supress url image *))
|
||||
| _ -> ()
|
||||
else if content <> "" then
|
||||
if Sys.file_exists fname then
|
||||
if move_file_to_save fname dir = 0 then
|
||||
incorrect conf "effective send (image)";
|
||||
let fname =
|
||||
if image_url <> "" then Filename.concat dir image_name ^ ".url" else fname
|
||||
in
|
||||
if content <> "" then
|
||||
try write_file fname content
|
||||
with _ ->
|
||||
incorrect conf
|
||||
(Printf.sprintf "effective send (writing content file %s)" fname)
|
||||
else if image_url <> "" then
|
||||
try write_file fname image_url
|
||||
with _ ->
|
||||
incorrect conf
|
||||
(Printf.sprintf "effective send (writing .url file %s)" fname)
|
||||
else ();
|
||||
if note <> Adef.safe "" then
|
||||
let fname = Filename.remove_extension fname ^ ".txt" in
|
||||
try write_file fname (note :> string)
|
||||
with _ ->
|
||||
incorrect conf
|
||||
(Printf.sprintf "effective send (writing .txt file %s)" fname)
|
||||
else ();
|
||||
if source <> Adef.safe "" then
|
||||
let fname = Filename.remove_extension fname ^ ".src" in
|
||||
try write_file fname (source :> string)
|
||||
with _ ->
|
||||
incorrect conf
|
||||
(Printf.sprintf "effective send (writing .txt file %s)" fname)
|
||||
else ();
|
||||
let changed =
|
||||
U_Send_image (Util.string_gen_person base (gen_person_of_person p))
|
||||
in
|
||||
History.record conf base changed
|
||||
(if mode = "portraits" then "si"
|
||||
else if file_name <> "" && note <> Adef.safe "" && source <> Adef.safe ""
|
||||
then "sb"
|
||||
else if file_name <> "" then "so"
|
||||
else if note <> Adef.safe "" then "sc"
|
||||
else if source <> Adef.safe "" then "ss"
|
||||
else "sn");
|
||||
file_name
|
||||
|
||||
(* Delete *)
|
||||
let print_delete_image conf base p =
|
||||
let title h =
|
||||
transl_nth conf "image/images" 0
|
||||
|> transl_decline conf "delete"
|
||||
|> Utf8.capitalize_fst |> Output.print_sstring conf;
|
||||
if not h then (
|
||||
let fn = p_first_name base p in
|
||||
let sn = p_surname base p in
|
||||
let occ = get_occ p in
|
||||
Output.print_sstring conf (Util.transl conf ":");
|
||||
Output.print_sstring conf " ";
|
||||
Output.print_string conf (Util.escape_html fn);
|
||||
Output.print_sstring conf ".";
|
||||
Output.print_sstring conf (string_of_int occ);
|
||||
Output.print_sstring conf " ";
|
||||
Output.print_string conf (Util.escape_html sn))
|
||||
in
|
||||
Hutil.header conf title;
|
||||
Output.printf conf "<form method=\"post\" action=\"%s\">" conf.command;
|
||||
Util.hidden_env conf;
|
||||
Util.hidden_input conf "m" (Adef.encoded "DEL_IMAGE_OK");
|
||||
Util.hidden_input conf "i" (get_iper p |> string_of_iper |> Mutil.encode);
|
||||
Output.print_sstring conf
|
||||
{|<p><button type="submit" class="btn btn-primary">|};
|
||||
transl_nth conf "validate/delete" 1
|
||||
|> Utf8.capitalize_fst |> Output.print_sstring conf;
|
||||
Output.print_sstring conf {|</button></p></form>|};
|
||||
Hutil.trailer conf
|
||||
|
||||
let print_deleted conf base p =
|
||||
let title _ =
|
||||
transl conf "image deleted"
|
||||
|> Utf8.capitalize_fst |> Output.print_sstring conf
|
||||
in
|
||||
Hutil.header conf title;
|
||||
Output.print_sstring conf "<ul><li>";
|
||||
Output.print_string conf (referenced_person_text conf base p);
|
||||
Output.print_sstring conf "</li></ul>";
|
||||
Hutil.trailer conf
|
||||
|
||||
let effective_delete_ok conf base p =
|
||||
let fname = Image.default_portrait_filename base p in
|
||||
let ext = get_extension conf false fname in
|
||||
let dir = Util.base_path [ "images" ] conf.bname in
|
||||
if move_file_to_save (fname ^ ext) dir = 0 then
|
||||
incorrect conf "effective delete";
|
||||
let changed =
|
||||
U_Delete_image (Util.string_gen_person base (gen_person_of_person p))
|
||||
in
|
||||
History.record conf base changed "di";
|
||||
print_deleted conf base p
|
||||
|
||||
let print_del_ok conf base =
|
||||
match p_getenv conf.env "i" with
|
||||
| Some ip ->
|
||||
let p = poi base (iper_of_string ip) in
|
||||
effective_delete_ok conf base p
|
||||
| None -> incorrect conf "print del ok"
|
||||
|
||||
let print_del conf base =
|
||||
match p_getenv conf.env "i" with
|
||||
| None -> Hutil.incorrect_request conf
|
||||
| Some ip -> (
|
||||
let p = poi base (iper_of_string ip) in
|
||||
match Image.get_portrait conf base p with
|
||||
| Some _ -> print_delete_image conf base p
|
||||
| None -> Hutil.incorrect_request conf)
|
||||
|
||||
(*carrousel *)
|
||||
(* removes portrait or other image and saves it into old folder *)
|
||||
(* if delete=on permanently deletes the file in old folder *)
|
||||
|
||||
let effective_delete_c_ok conf base p =
|
||||
let fname = Image.default_portrait_filename base p in
|
||||
let file_name =
|
||||
try List.assoc "file_name" conf.env with Not_found -> Adef.encoded ""
|
||||
in
|
||||
let file_name = (Mutil.decode file_name :> string) in
|
||||
let mode =
|
||||
try (List.assoc "mode" conf.env :> string) with Not_found -> "portraits"
|
||||
in
|
||||
let delete =
|
||||
try List.assoc "delete" conf.env = Adef.encoded "on"
|
||||
with Not_found -> false
|
||||
in
|
||||
let ext = get_extension conf delete fname in
|
||||
let file = if file_name = "" then fname ^ ext else file_name in
|
||||
let dir =
|
||||
if mode = "portraits" then Util.base_path [ "images" ] conf.bname
|
||||
else
|
||||
String.concat Filename.dir_sep
|
||||
[ Util.base_path [ "src" ] conf.bname; "images"; fname ]
|
||||
in
|
||||
if not (Sys.file_exists dir) then Mutil.mkdir_p dir;
|
||||
(* TODO verify we dont destroy a saved image
|
||||
having the same name as portrait! *)
|
||||
if delete then Mutil.rm (String.concat Filename.dir_sep [ dir; "old"; file ])
|
||||
else if move_file_to_save file dir = 0 then incorrect conf "effective delete";
|
||||
let changed =
|
||||
U_Delete_image (Util.string_gen_person base (gen_person_of_person p))
|
||||
in
|
||||
History.record conf base changed (if mode = "portraits" then "di" else "do");
|
||||
file_name
|
||||
|
||||
(* carrousel *)
|
||||
(* reset portrait or image from old folder to portrait or others *)
|
||||
|
||||
let effective_reset_c_ok conf base p =
|
||||
let mode =
|
||||
try (List.assoc "mode" conf.env :> string) with Not_found -> "portraits"
|
||||
in
|
||||
let carrousel = Image.default_portrait_filename base p in
|
||||
let file_name =
|
||||
try List.assoc "file_name" conf.env with Not_found -> Adef.encoded ""
|
||||
in
|
||||
let file_name = (Mutil.decode file_name :> string) in
|
||||
let file_name = if mode = "portraits" then carrousel else file_name in
|
||||
let ext = get_extension conf false file_name in
|
||||
let old_ext = get_extension conf true file_name in
|
||||
let ext =
|
||||
match Image.get_portrait conf base p with
|
||||
| Some src ->
|
||||
if Mutil.start_with "http" 0 (Image.src_to_string src) then ".url"
|
||||
else ext
|
||||
| _ -> ext
|
||||
in
|
||||
let file_in_new =
|
||||
if mode = "portraits" then
|
||||
String.concat Filename.dir_sep
|
||||
[ Util.base_path [ "images" ] conf.bname; file_name ^ ext ]
|
||||
else
|
||||
String.concat Filename.dir_sep
|
||||
[ Util.base_path [ "src" ] conf.bname; "images"; carrousel; file_name ]
|
||||
in
|
||||
(if Sys.file_exists file_in_new then ()
|
||||
else
|
||||
match Image.get_portrait conf base p with
|
||||
| Some (`Url url) -> (
|
||||
try write_file file_in_new url
|
||||
with _ ->
|
||||
incorrect conf
|
||||
(Printf.sprintf "reset portrait (swap file %s)" file_in_new))
|
||||
| _ -> ());
|
||||
swap_files file_in_new ext old_ext;
|
||||
file_name
|
||||
|
||||
(* ************************************************************************** *)
|
||||
(* [Fonc] print : Config.config -> Gwdb.base -> unit *)
|
||||
(* ************************************************************************** *)
|
||||
|
||||
(* most functions in GeneWeb end with a COMMAND_OK confirmation step *)
|
||||
(* for carrousel, we have chosen to ignore this step and refresh *)
|
||||
(* the updated page directly *)
|
||||
(* if em="" this is the first pass, do it *)
|
||||
|
||||
let print_main_c conf base =
|
||||
match Util.p_getenv conf.env "em" with
|
||||
| None -> (
|
||||
match Util.p_getenv conf.env "m" with
|
||||
| Some m -> (
|
||||
let save_m = m in
|
||||
match Util.p_getenv conf.env "i" with
|
||||
| Some ip -> (
|
||||
let p = poi base (Gwdb.iper_of_string ip) in
|
||||
let digest = Image.default_portrait_filename base p in
|
||||
let conf, report =
|
||||
match Util.p_getenv conf.env "m" with
|
||||
| Some "SND_IMAGE_C_OK" ->
|
||||
let mode =
|
||||
try (List.assoc "mode" conf.env :> string)
|
||||
with Not_found -> "portraits"
|
||||
in
|
||||
let file_name =
|
||||
try (List.assoc "file_name" conf.env :> string)
|
||||
with Not_found -> ""
|
||||
in
|
||||
let file_name =
|
||||
if file_name = "" then
|
||||
try (List.assoc "file_name_2" conf.env :> string)
|
||||
with Not_found -> ""
|
||||
else file_name
|
||||
in
|
||||
let file_name =
|
||||
(Mutil.decode (Adef.encoded file_name) :> string)
|
||||
in
|
||||
let file_name_2 = Filename.remove_extension file_name in
|
||||
let new_env =
|
||||
List.fold_left
|
||||
(fun accu (k, v) ->
|
||||
if k = "file_name_2" then
|
||||
(k, Adef.encoded file_name_2) :: accu
|
||||
else (k, v) :: accu)
|
||||
[] conf.env
|
||||
in
|
||||
let conf = { conf with env = new_env } in
|
||||
let file =
|
||||
if mode = "note" || mode = "source" then "file_name"
|
||||
else (raw_get conf "file" :> string)
|
||||
in
|
||||
let idigest =
|
||||
try (List.assoc "idigest" conf.env :> string)
|
||||
with Not_found -> ""
|
||||
in
|
||||
if digest = idigest then
|
||||
(conf, effective_send_c_ok conf base p file file_name)
|
||||
else (conf, "idigest error")
|
||||
| Some "DEL_IMAGE_C_OK" ->
|
||||
let idigest =
|
||||
try (List.assoc "idigest" conf.env :> string)
|
||||
with Not_found -> ""
|
||||
in
|
||||
if digest = idigest then
|
||||
(conf, effective_delete_c_ok conf base p)
|
||||
else (conf, "idigest error")
|
||||
| Some "RESET_IMAGE_C_OK" ->
|
||||
let idigest =
|
||||
try (List.assoc "idigest" conf.env :> string)
|
||||
with Not_found -> ""
|
||||
in
|
||||
if digest = idigest then
|
||||
(conf, effective_reset_c_ok conf base p)
|
||||
else (conf, "idigest error")
|
||||
| Some "IMAGE_C" -> (conf, "image")
|
||||
| _ -> (conf, "incorrect request")
|
||||
in
|
||||
match report with
|
||||
| "idigest error" ->
|
||||
failwith
|
||||
(__FILE__ ^ " idigest error, line " ^ string_of_int __LINE__
|
||||
:> string)
|
||||
| "incorrect request" -> Hutil.incorrect_request conf
|
||||
| _ -> print_confirm_c conf base save_m report)
|
||||
| None -> Hutil.incorrect_request conf)
|
||||
| None -> Hutil.incorrect_request conf)
|
||||
(* em!="" second pass, ignore *)
|
||||
| Some _ -> print_confirm_c conf base "REFRESH" ""
|
||||
|
||||
let print conf base =
|
||||
match p_getenv conf.env "i" with
|
||||
| None -> Hutil.incorrect_request conf
|
||||
| Some ip ->
|
||||
let p = poi base (iper_of_string ip) in
|
||||
let fn = p_first_name base p in
|
||||
let sn = p_surname base p in
|
||||
if fn = "?" || sn = "?" then Hutil.incorrect_request conf
|
||||
else print_send_image conf base p
|
||||
|
||||
(* carrousel *)
|
||||
let print_c ?(saved = false) conf base =
|
||||
match (Util.p_getenv conf.env "s", Util.find_person_in_env conf base "") with
|
||||
| Some f, Some p ->
|
||||
let k = Image.default_portrait_filename base p in
|
||||
let f = Filename.concat k f in
|
||||
ImageDisplay.print_source conf (if saved then insert_saved f else f)
|
||||
| Some f, _ -> ImageDisplay.print_source conf f
|
||||
| _, Some p -> (
|
||||
match
|
||||
(if saved then Image.get_old_portrait else Image.get_portrait)
|
||||
conf base p
|
||||
with
|
||||
| Some (`Path f) ->
|
||||
Result.fold ~ok:ignore
|
||||
~error:(fun _ -> Hutil.incorrect_request conf)
|
||||
(ImageDisplay.print_image_file conf f)
|
||||
| _ -> Hutil.incorrect_request conf)
|
||||
| _, _ -> Hutil.incorrect_request conf
|
||||
159
lib/imageDisplay.ml
Normal file
159
lib/imageDisplay.ml
Normal file
@@ -0,0 +1,159 @@
|
||||
(* $Id: image.ml,v 5.8 2009-03-11 09:22:39 ddr Exp $ *)
|
||||
(* Copyright (c) 1998-2007 INRIA *)
|
||||
|
||||
open Config
|
||||
|
||||
let print_placeholder_gendered_portrait conf p size =
|
||||
let open Gwdb in
|
||||
let image, alt =
|
||||
match get_sex p with
|
||||
| Male -> ("male.png", "M")
|
||||
| Female -> ("female.png", "F")
|
||||
| Neuter -> ("sexunknown.png", "?")
|
||||
in
|
||||
Output.printf conf
|
||||
{|<img src="%s/%s" alt="%s" title="sex" width="%d" height="%d">|}
|
||||
(Util.images_prefix conf) image alt size size
|
||||
|
||||
(* ************************************************************************** *)
|
||||
(* [Fonc] content : string -> int -> string -> unit *)
|
||||
(* ************************************************************************** *)
|
||||
|
||||
(** [Description] : Envoie les en-têtes de contenu et de cache pour un fichier
|
||||
image, pdf ou html sur le flux HTTP sortant de Wserver.
|
||||
[Args] :
|
||||
- ct : le content_type MIME du fichier, par exemple "image/png",
|
||||
"image/jpeg" ou "application/pdf"
|
||||
- len : la taille en octet du fichier
|
||||
- fname : le nom du fichier
|
||||
[Retour] : aucun
|
||||
[Rem] : Ne pas utiliser en dehors de ce module. *)
|
||||
let content conf ct len fname =
|
||||
Output.status conf Def.OK;
|
||||
Output.header conf "Content-type: %s" ct;
|
||||
Output.header conf "Content-length: %d" len;
|
||||
Output.header conf "Content-disposition: inline; filename=%s"
|
||||
(Filename.basename fname);
|
||||
(* TODO: Utiliser un cache public pour les images non personelles. *)
|
||||
Output.header conf "Cache-control: private, max-age=%d" (60 * 60 * 24 * 365);
|
||||
Output.flush conf
|
||||
|
||||
let print_image_file conf fname =
|
||||
let res =
|
||||
List.find_opt
|
||||
(fun (suff, _ctype) ->
|
||||
if
|
||||
Filename.check_suffix fname suff
|
||||
|| Filename.check_suffix fname (String.uppercase_ascii suff)
|
||||
then true
|
||||
else false)
|
||||
[
|
||||
(".png", "image/png");
|
||||
(".jpg", "image/jpeg");
|
||||
(".jpeg", "image/jpeg");
|
||||
(".pjpeg", "image/jpeg");
|
||||
(".gif", "image/gif");
|
||||
(".pdf", "application/pdf");
|
||||
(".htm", "text/html");
|
||||
(".html", "text/html");
|
||||
]
|
||||
in
|
||||
match res with
|
||||
| None ->
|
||||
Error
|
||||
(Format.sprintf "Could not find mime type from extension for file: %s"
|
||||
fname)
|
||||
| Some (_suff, ctype) -> (
|
||||
try
|
||||
let ic = Secure.open_in_bin fname in
|
||||
let buf = Bytes.create 1024 in
|
||||
let len = in_channel_length ic in
|
||||
content conf ctype len fname;
|
||||
let rec loop len =
|
||||
if len = 0 then ()
|
||||
else
|
||||
let olen = min (Bytes.length buf) len in
|
||||
really_input ic buf 0 olen;
|
||||
Output.print_sstring conf (Bytes.sub_string buf 0 olen);
|
||||
loop (len - olen)
|
||||
in
|
||||
loop len;
|
||||
close_in ic;
|
||||
Ok ()
|
||||
with Sys_error e ->
|
||||
!GWPARAM.syslog `LOG_ERR
|
||||
(Format.sprintf "Error printing image file content for %s : %s" fname
|
||||
e);
|
||||
Error e)
|
||||
|
||||
(* ************************************************************************** *)
|
||||
(* [Fonc] print_portrait : Config.config -> Gwdb.base -> Gwdb.person -> unit *)
|
||||
(* ************************************************************************** *)
|
||||
|
||||
(** [Description] : Affiche l'image d'une personne en réponse HTTP.
|
||||
[Args] :
|
||||
- conf : configuration de la requête
|
||||
- base : base de donnée sélectionnée
|
||||
- p : personne dans la base dont il faut afficher l'image
|
||||
[Retour] : aucun
|
||||
[Rem] : Ne pas utiliser en dehors de ce module. *)
|
||||
let print_portrait conf base p =
|
||||
match Image.get_portrait conf base p with
|
||||
| Some (`Path path) ->
|
||||
Result.fold ~ok:ignore
|
||||
~error:(fun _ -> Hutil.incorrect_request conf)
|
||||
(print_image_file conf path)
|
||||
| Some (`Url url) ->
|
||||
Util.html conf;
|
||||
Output.print_sstring conf "<head><title>";
|
||||
Output.print_sstring conf (Util.transl_nth conf "image/images" 0);
|
||||
Output.print_sstring conf "</title></head><body>";
|
||||
Output.print_sstring conf (Printf.sprintf {|<img src=%s>|} url);
|
||||
Output.print_sstring conf "</body></html>"
|
||||
| None -> Hutil.incorrect_request conf
|
||||
|
||||
let print_source conf f =
|
||||
let fname = if f.[0] = '/' then String.sub f 1 (String.length f - 1) else f in
|
||||
let fname = Image.source_filename conf fname in
|
||||
if (conf.wizard || conf.friend) || Image.is_not_private_img conf fname then
|
||||
Result.fold ~ok:ignore
|
||||
~error:(fun _ -> Hutil.incorrect_request conf)
|
||||
(print_image_file conf fname)
|
||||
else Hutil.incorrect_request conf
|
||||
|
||||
let print conf base =
|
||||
match Util.p_getenv conf.env "s" with
|
||||
| Some f -> print_source conf f
|
||||
| None -> (
|
||||
match Util.find_person_in_env conf base "" with
|
||||
| Some p -> print_portrait conf base p
|
||||
| None -> Hutil.incorrect_request conf)
|
||||
|
||||
let print_html conf =
|
||||
let ext =
|
||||
match Util.p_getenv conf.env "s" with
|
||||
| Some f -> Filename.extension f
|
||||
| _ -> ""
|
||||
in
|
||||
match ext with
|
||||
| ".htm" | ".html" | ".pdf" ->
|
||||
let title _ = Output.print_sstring conf "Error" in
|
||||
Hutil.header conf title;
|
||||
Output.print_sstring conf
|
||||
"<body><ul><li>DOCH not available for html and pdf.";
|
||||
Hutil.trailer conf
|
||||
| _ ->
|
||||
Util.html conf;
|
||||
Output.print_sstring conf "<head><title>";
|
||||
Output.print_sstring conf (Util.transl_nth conf "image/images" 0);
|
||||
Output.print_sstring conf "</title></head><body><img src=\"";
|
||||
Output.print_string conf (Util.commd conf);
|
||||
Mutil.list_iter_first
|
||||
(fun first (k, v) ->
|
||||
let v = if k = "m" then Adef.encoded "IM" else v in
|
||||
if not first then Output.print_sstring conf "&";
|
||||
Output.print_sstring conf k;
|
||||
Output.print_sstring conf "=";
|
||||
Output.print_string conf v)
|
||||
conf.env;
|
||||
Output.print_sstring conf "\"></body></html>"
|
||||
24
lib/imageDisplay.mli
Normal file
24
lib/imageDisplay.mli
Normal file
@@ -0,0 +1,24 @@
|
||||
open Config
|
||||
|
||||
val print_image_file : config -> string -> (unit, string) result
|
||||
(** [print_image_file conf fname] send HTTP respose with content of an image
|
||||
file at the path [fname].
|
||||
MIME type of an image is deducted from [fname] extension. Returns [false]
|
||||
if image wasn't found or couldn't be send. *)
|
||||
|
||||
val print_source : config -> string -> unit
|
||||
(** Display an image of given filename in images folder
|
||||
Filename may contain sub-folders, but cannot point outside images *)
|
||||
|
||||
val print : config -> Gwdb.base -> unit
|
||||
(** Searches image's filename in the environement [conf.env] and sends
|
||||
HTTP response with its content on the socket. If filename isn't presented,
|
||||
looks up personal image (portrait) for person mentionned in [conf.env] *)
|
||||
|
||||
val print_html : config -> unit
|
||||
(** Sends HTTP response with HTML page containg just image specified in
|
||||
arguments. *)
|
||||
|
||||
val print_placeholder_gendered_portrait : config -> Gwdb.person -> int -> unit
|
||||
(** prints html `<img>` tag of the default gendered portrait with square
|
||||
size [size] *)
|
||||
5
lib/json_export/dune
Normal file
5
lib/json_export/dune
Normal file
@@ -0,0 +1,5 @@
|
||||
(library
|
||||
(name geneweb_export)
|
||||
(public_name geneweb.export)
|
||||
(libraries unix geneweb_def geneweb_def_show geneweb_gwdb)
|
||||
(modules json_converter))
|
||||
242
lib/json_export/json_converter.ml
Normal file
242
lib/json_export/json_converter.ml
Normal file
@@ -0,0 +1,242 @@
|
||||
open Def
|
||||
|
||||
module type ConverterDriver = sig
|
||||
type t
|
||||
|
||||
val str : string -> t
|
||||
val int : int -> t
|
||||
val obj : (string * t) array -> t
|
||||
val null : t
|
||||
val array : 't array -> t
|
||||
val list : 't list -> t
|
||||
val bool : bool -> t
|
||||
end
|
||||
|
||||
(* e.g. js_of_ocaml *)
|
||||
(* module ConverterDriver = struct
|
||||
* type t = Js.Unsafe.any
|
||||
* let str x = Js.Unsafe.inject @@ Js.string x
|
||||
* let int x = Js.Unsafe.inject x
|
||||
* let obj x = Js.Unsafe.inject @@ Js.Unsafe.obj x
|
||||
* let null = Js.Unsafe.inject Js.null
|
||||
* let array x = Js.Unsafe.inject @@ Js.array x
|
||||
* let list x = array (Array.of_list x)
|
||||
* let bool x = Js.Unsafe.inject @@ if x then Js._true else Js._false
|
||||
* end *)
|
||||
|
||||
(* e.g. yojson *)
|
||||
(* module ConverterDriver = struct
|
||||
* type t = Yojson.Basic.t
|
||||
* let str x = `String x
|
||||
* let int x = `Int x
|
||||
* let obj x = `Assoc x
|
||||
* let null = `Null
|
||||
* let array x = `List (Array.to_list x)
|
||||
* let list x = `List x
|
||||
* let bool x = `Bool x
|
||||
* end *)
|
||||
|
||||
module Make (D : ConverterDriver) = struct
|
||||
let str = D.str
|
||||
let int = D.int
|
||||
let obj = D.obj
|
||||
let null = D.null
|
||||
let array = D.array
|
||||
let list = D.list
|
||||
let bool = D.bool
|
||||
let opt fn = function Some x -> fn x | None -> null
|
||||
|
||||
(** gwdb to json *)
|
||||
|
||||
let conv_dmy dmy =
|
||||
obj
|
||||
[|
|
||||
("day", int dmy.day);
|
||||
("delta", if dmy.delta = 0 then null else int dmy.delta);
|
||||
("month", int dmy.month);
|
||||
("year", int dmy.year);
|
||||
|]
|
||||
|
||||
let conv_dmy2 dmy =
|
||||
obj
|
||||
[|
|
||||
("day", int dmy.day2); ("month", int dmy.month2); ("year", int dmy.year2);
|
||||
|]
|
||||
|
||||
let conv_date_cal dt cal =
|
||||
let prec =
|
||||
match dt.prec with
|
||||
| Sure -> str "Sure"
|
||||
| About -> str "About"
|
||||
| Maybe -> str "Maybe"
|
||||
| Before -> str "Before"
|
||||
| After -> str "After"
|
||||
| OrYear _ -> str "OrYear"
|
||||
| YearInt _ -> str "YearInt"
|
||||
in
|
||||
let dmy2 =
|
||||
match dt.prec with
|
||||
| OrYear dmy2 -> conv_dmy2 dmy2
|
||||
| YearInt dmy2 -> conv_dmy2 dmy2
|
||||
| _ -> null
|
||||
in
|
||||
obj
|
||||
[|
|
||||
("prec", prec);
|
||||
("dmy1", conv_dmy dt);
|
||||
("dmy2", dmy2);
|
||||
("calendar", str cal);
|
||||
|]
|
||||
|
||||
let conv_date oc =
|
||||
match oc with
|
||||
| Dgreg (d, c) -> conv_date_cal d (Def_show.show_calendar c)
|
||||
| Dtext t -> str t
|
||||
|
||||
let conv_cdate cd =
|
||||
match Date.od_of_cdate cd with None -> null | Some date -> conv_date date
|
||||
|
||||
let conv_pevent_name x =
|
||||
str
|
||||
@@ Def_show.show_gen_pers_event_name
|
||||
(fun fmt -> Format.fprintf fmt "Epers_Name %s")
|
||||
x
|
||||
|
||||
let conv_event_witness_kind x = str @@ Def_show.show_witness_kind x
|
||||
let handler_of_iper i = str @@ Gwdb_driver.string_of_iper i
|
||||
let handler_of_ifam i = str @@ Gwdb_driver.string_of_ifam i
|
||||
|
||||
let conv_event_witness (i, kind) =
|
||||
obj
|
||||
[|
|
||||
("person", handler_of_iper i); ("kind", conv_event_witness_kind kind);
|
||||
|]
|
||||
|
||||
let conv_pevent pevent =
|
||||
obj
|
||||
[|
|
||||
("place", str pevent.epers_place);
|
||||
("reason", str pevent.epers_reason);
|
||||
("note", str pevent.epers_note);
|
||||
("src", str pevent.epers_src);
|
||||
("name", conv_pevent_name pevent.epers_name);
|
||||
("date", conv_cdate pevent.epers_date);
|
||||
( "witnesses",
|
||||
array @@ Array.map conv_event_witness pevent.epers_witnesses );
|
||||
|]
|
||||
|
||||
let conv_title_name = function
|
||||
| Tmain -> str ""
|
||||
| Tname s -> str s
|
||||
| Tnone -> null
|
||||
|
||||
let conv_title gen_title =
|
||||
obj
|
||||
[|
|
||||
("name", conv_title_name gen_title.t_name);
|
||||
("date_start", conv_cdate gen_title.t_date_start);
|
||||
("date_end", conv_cdate gen_title.t_date_end);
|
||||
("nth", int gen_title.t_nth);
|
||||
("ident", str gen_title.t_ident);
|
||||
("place", str gen_title.t_place);
|
||||
|]
|
||||
|
||||
let conv_relation_kind x = str @@ Def_show.show_relation_kind x
|
||||
|
||||
let conv_fevent_name x =
|
||||
str
|
||||
@@ Def_show.show_gen_fam_event_name
|
||||
(fun fmt -> Format.fprintf fmt "Efam_Name %s")
|
||||
x
|
||||
|
||||
let conv_fevent fevent =
|
||||
obj
|
||||
[|
|
||||
("date", conv_cdate fevent.efam_date);
|
||||
("name", conv_fevent_name fevent.efam_name);
|
||||
("note", str fevent.efam_note);
|
||||
("place", str fevent.efam_place);
|
||||
("reason", str fevent.efam_reason);
|
||||
("src", str fevent.efam_src);
|
||||
( "witnesses",
|
||||
array @@ Array.map conv_event_witness fevent.efam_witnesses );
|
||||
|]
|
||||
|
||||
let conv_divorce = function
|
||||
| NotDivorced -> bool false
|
||||
| Divorced date -> conv_cdate date
|
||||
| Separated -> bool true
|
||||
|
||||
let conv_relation_type x = str @@ Def_show.show_relation_type x
|
||||
|
||||
let conv_rparent gen_relation =
|
||||
obj
|
||||
[|
|
||||
("father", opt handler_of_iper gen_relation.r_fath);
|
||||
("mother", opt handler_of_iper gen_relation.r_moth);
|
||||
("source", str gen_relation.r_sources);
|
||||
("type", conv_relation_type gen_relation.r_type);
|
||||
|]
|
||||
|
||||
let conv_death = function
|
||||
| Def.NotDead -> str "NotDead"
|
||||
| Death (Killed, _) -> str "Killed"
|
||||
| Death (Murdered, _) -> str "Murdered"
|
||||
| Death (Executed, _) -> str "Executed"
|
||||
| Death (Disappeared, _) -> str "Disappeared"
|
||||
| Death (Unspecified, _) -> str "Unspecified"
|
||||
| DeadYoung -> str "DeadYoung"
|
||||
| DeadDontKnowWhen -> str "DeadDontKnowWhen"
|
||||
| DontKnowIfDead -> str "DontKnowIfDead"
|
||||
| OfCourseDead -> str "OfCourseDead"
|
||||
|
||||
let conv_person base p =
|
||||
let pp = Gwdb.gen_person_of_person p in
|
||||
let pp = Futil.map_person_ps (fun i -> i) (Gwdb.sou base) pp in
|
||||
let pa = Gwdb.gen_ascend_of_person p in
|
||||
let pu = Gwdb.gen_union_of_person p in
|
||||
obj
|
||||
[|
|
||||
( "access",
|
||||
int (match pp.access with Private -> 2 | Public -> 1 | _ -> 0) );
|
||||
("aliases", list (List.map str pp.aliases));
|
||||
("first_names_aliases", list (List.map str pp.first_names_aliases));
|
||||
("firstname", str pp.first_name);
|
||||
("image", str pp.image);
|
||||
("iper", handler_of_iper pp.key_index);
|
||||
("lastname", str pp.surname);
|
||||
("note", str pp.notes);
|
||||
("occ", int pp.occ);
|
||||
("occupation", str pp.occupation);
|
||||
("parents", opt handler_of_ifam pa.parents);
|
||||
( "consang",
|
||||
if pa.consang = Adef.no_consang then null
|
||||
else int (Adef.fix_repr pa.consang) );
|
||||
("pevents", list (List.map conv_pevent pp.pevents));
|
||||
("psources", str pp.psources);
|
||||
("public_name", str pp.public_name);
|
||||
("qualifiers", list (List.map str pp.qualifiers));
|
||||
("related", list (List.map handler_of_iper pp.related));
|
||||
("rparents", list (List.map conv_rparent pp.rparents));
|
||||
("sex", int (match pp.sex with Male -> 1 | Female -> 2 | _ -> 0));
|
||||
("surnames_aliases", list (List.map str pp.surnames_aliases));
|
||||
("titles", list (List.map conv_title pp.titles));
|
||||
("unions", list (List.map handler_of_ifam (Array.to_list pu.family)));
|
||||
|]
|
||||
|
||||
let conv_family base f =
|
||||
let ff = Gwdb.gen_family_of_family f in
|
||||
let ff = Futil.map_family_ps (fun i -> i) (fun i -> i) (Gwdb.sou base) ff in
|
||||
let fc = Gwdb.gen_couple_of_family f in
|
||||
let fd = Gwdb.gen_descend_of_family f in
|
||||
obj
|
||||
[|
|
||||
("fevents", list (List.map conv_fevent ff.fevents));
|
||||
("comment", str ff.comment);
|
||||
("origin_file", str ff.origin_file);
|
||||
("fsources", str ff.fsources);
|
||||
("witnesses", array (Array.map handler_of_iper ff.witnesses));
|
||||
("children", array (Array.map handler_of_iper fd.children));
|
||||
("parents", array (Array.map handler_of_iper @@ Adef.parent_array fc));
|
||||
|]
|
||||
end
|
||||
80
lib/json_export/json_converter.mli
Normal file
80
lib/json_export/json_converter.mli
Normal file
@@ -0,0 +1,80 @@
|
||||
(** Json converter driver *)
|
||||
module type ConverterDriver = sig
|
||||
type t
|
||||
(** Json value *)
|
||||
|
||||
val str : string -> t
|
||||
(** Convert to JSON string *)
|
||||
|
||||
val int : int -> t
|
||||
(** Convert to JSON integer *)
|
||||
|
||||
val obj : (string * t) array -> t
|
||||
(** Convert to JSON object *)
|
||||
|
||||
val null : t
|
||||
(** Convert to JSON null value *)
|
||||
|
||||
val array : 't array -> t
|
||||
(** Convert array to JSON list *)
|
||||
|
||||
val list : 't list -> t
|
||||
(** Convert list to JSON list *)
|
||||
|
||||
val bool : bool -> t
|
||||
(** Convert to JSON boolean *)
|
||||
end
|
||||
|
||||
(** Functor building JSON convertion functions of the Geneweb data types. *)
|
||||
module Make : functor (D : ConverterDriver) -> sig
|
||||
val conv_dmy : Def.dmy -> D.t
|
||||
(** Convert [dmy] to JSON *)
|
||||
|
||||
val conv_dmy2 : Def.dmy2 -> D.t
|
||||
(** Convert [dmy2] to JSON *)
|
||||
|
||||
val conv_cdate : Def.cdate -> D.t
|
||||
(** Convert [cdate] to JSON *)
|
||||
|
||||
val conv_pevent_name : string Def.gen_pers_event_name -> D.t
|
||||
(** Convert [gen_pers_event_name] to JSON *)
|
||||
|
||||
val conv_event_witness_kind : Def.witness_kind -> D.t
|
||||
(** Convert [witness_kind] to JSON *)
|
||||
|
||||
val conv_pevent : (Gwdb_driver.iper, string) Def.gen_pers_event -> D.t
|
||||
(** Convert [gen_pers_event] to JSON *)
|
||||
|
||||
val conv_title_name : string Def.gen_title_name -> D.t
|
||||
(** Convert [gen_title_name] to JSON *)
|
||||
|
||||
val conv_title : string Def.gen_title -> D.t
|
||||
(** Convert [gen_title] to JSON *)
|
||||
|
||||
val conv_relation_kind : Def.relation_kind -> D.t
|
||||
(** Convert [relation_kind] to JSON *)
|
||||
|
||||
val conv_fevent_name : string Def.gen_fam_event_name -> D.t
|
||||
(** Convert [gen_fam_event_name] to JSON *)
|
||||
|
||||
val conv_fevent : (Gwdb_driver.iper, string) Def.gen_fam_event -> D.t
|
||||
(** Convert [gen_fam_event] to JSON *)
|
||||
|
||||
val conv_divorce : Def.divorce -> D.t
|
||||
(** Convert [divorce] to JSON *)
|
||||
|
||||
val conv_relation_type : Def.relation_type -> D.t
|
||||
(** Convert [relation_type] to JSON *)
|
||||
|
||||
val conv_rparent : (Gwdb_driver.iper, string) Def.gen_relation -> D.t
|
||||
(** Convert [gen_relation] to JSON *)
|
||||
|
||||
val conv_death : Def.death -> D.t
|
||||
(** Convert [death] to JSON *)
|
||||
|
||||
val conv_person : Gwdb.base -> Gwdb.person -> D.t
|
||||
(** Convert [person] to JSON *)
|
||||
|
||||
val conv_family : Gwdb.base -> Gwdb.family -> D.t
|
||||
(** Convert [family] to JSON *)
|
||||
end
|
||||
153
lib/mergeDisplay.ml
Normal file
153
lib/mergeDisplay.ml
Normal file
@@ -0,0 +1,153 @@
|
||||
(* $Id: merge.ml, v7-exp 2018-09-26 07:34:44 ddr Exp $ *)
|
||||
(* Copyright (c) 1998-2007 INRIA *)
|
||||
|
||||
open Config
|
||||
open Gwdb
|
||||
open Util
|
||||
|
||||
let print_someone conf base p =
|
||||
Output.printf conf "%s%s %s" (p_first_name base p)
|
||||
(if get_occ p = 0 then "" else "." ^ string_of_int (get_occ p))
|
||||
(p_surname base p)
|
||||
|
||||
let print conf base p =
|
||||
let title h =
|
||||
Output.print_sstring conf
|
||||
(Utf8.capitalize_fst (transl_decline conf "merge" ""));
|
||||
if not h then (
|
||||
Output.print_sstring conf " ";
|
||||
print_someone conf base p;
|
||||
Output.print_sstring conf " ";
|
||||
Output.print_sstring conf (transl_decline conf "with" "");
|
||||
Output.print_sstring conf (transl conf ":"))
|
||||
in
|
||||
let list = Gutil.find_same_name base p in
|
||||
let list =
|
||||
List.fold_right
|
||||
(fun p1 pl -> if get_iper p1 = get_iper p then pl else p1 :: pl)
|
||||
list []
|
||||
in
|
||||
Perso.interp_notempl_with_menu title "perso_header" conf base p;
|
||||
Output.print_sstring conf "<h2>";
|
||||
title false;
|
||||
Output.print_sstring conf "</h2>";
|
||||
Output.print_sstring conf {|<form method="get" action="|};
|
||||
Output.print_sstring conf conf.command;
|
||||
Output.print_sstring conf {|"class="mx-3 mb-3">|};
|
||||
Util.hidden_env conf;
|
||||
Util.hidden_input conf "m" (Adef.encoded "MRG_IND");
|
||||
Util.hidden_input conf "i" (get_iper p |> string_of_iper |> Mutil.encode);
|
||||
Output.print_sstring conf
|
||||
"<span class=\"form-row align-items-center\"><span \
|
||||
class=\"col-auto\"><span class=\"custom-control custom-radio\"><input \
|
||||
type=\"radio\" class=\"custom-control-input\" name=\"select\" \
|
||||
id=\"input\" value=\"input\" checked><label \
|
||||
class=\"custom-control-label\"for=\"input\">";
|
||||
Output.print_sstring conf (transl conf "any individual in the base");
|
||||
Output.print_sstring conf
|
||||
"</label></span></span><span class=\"col-auto\"><input type=\"text\" \
|
||||
class=\"form-control\" name=\"n\" placeholder=\"";
|
||||
Output.print_sstring conf (transl_nth conf "first name/first names" 0);
|
||||
Output.print_sstring conf ".";
|
||||
Output.print_sstring conf (transl conf "number");
|
||||
Output.print_sstring conf " ";
|
||||
Output.print_sstring conf (transl_nth conf "surname/surnames" 0);
|
||||
Output.print_sstring conf "\" title=\"";
|
||||
Output.print_sstring conf (transl_nth conf "first name/first names" 0);
|
||||
Output.print_sstring conf ".";
|
||||
Output.print_sstring conf (transl conf "number");
|
||||
Output.print_sstring conf " ";
|
||||
Output.print_sstring conf (transl_nth conf "surname/surnames" 0);
|
||||
Output.print_sstring conf
|
||||
"\" size=\"50\" id=\"inlineinput\" autofocus></span></span>";
|
||||
if list <> [] then
|
||||
List.iter
|
||||
(fun p ->
|
||||
Output.print_sstring conf "<div class=\"custom-control custom-radio\">";
|
||||
Output.print_sstring conf
|
||||
"<input type=\"radio\" class=\"custom-control-input\" \
|
||||
name=\"select\" id=\"";
|
||||
Output.print_string conf (get_iper p |> string_of_iper |> Mutil.encode);
|
||||
Output.print_sstring conf "\" value=\"";
|
||||
Output.print_string conf (get_iper p |> string_of_iper |> Mutil.encode);
|
||||
Output.print_sstring conf "\">\n";
|
||||
Output.print_sstring conf "<label class=\"custom-control-label\" for=\"";
|
||||
Output.print_string conf (get_iper p |> string_of_iper |> Mutil.encode);
|
||||
Output.print_sstring conf "\">";
|
||||
let cop = (Util.child_of_parent conf base p :> string) in
|
||||
let cop = if cop = "" then "" else ", " ^ cop in
|
||||
let hbw = (Util.husband_wife conf base p true :> string) in
|
||||
let hbw = if hbw = "" then "" else ", " ^ hbw in
|
||||
Output.print_sstring conf
|
||||
(Printf.sprintf "%s.%d %s%s%s"
|
||||
(get_first_name p |> sou base)
|
||||
(get_occ p)
|
||||
(get_surname p |> sou base)
|
||||
cop hbw);
|
||||
Output.print_sstring conf "</label></div>")
|
||||
list;
|
||||
Output.print_sstring conf
|
||||
{|<button type="submit" class="btn btn-primary btn-lg mt-2">|};
|
||||
Output.print_sstring conf
|
||||
(Utf8.capitalize_fst (transl_nth conf "validate/delete" 0));
|
||||
Output.print_sstring conf "</button></form>\n";
|
||||
Hutil.trailer conf
|
||||
|
||||
let print_possible_continue_merging conf base =
|
||||
let open Adef in
|
||||
match (p_getenv conf.env "ini1", p_getenv conf.env "ini2") with
|
||||
| Some ini1, Some ini2 ->
|
||||
let ini1 = iper_of_string ini1 in
|
||||
let ini2 = iper_of_string ini2 in
|
||||
let p1 = poi base ini1 in
|
||||
let p2 = poi base ini2 in
|
||||
Output.print_sstring conf {|<p><a href="|};
|
||||
Output.print_string conf (commd conf);
|
||||
Output.print_sstring conf {|m=MRG_IND&i=|};
|
||||
Output.print_string conf (string_of_iper ini1 |> Mutil.encode);
|
||||
Output.print_sstring conf {|&i2=|};
|
||||
Output.print_string conf (string_of_iper ini2 |> Mutil.encode);
|
||||
Output.print_sstring conf {|">|};
|
||||
Output.print_sstring conf
|
||||
(Utf8.capitalize_fst (transl conf "continue merging"));
|
||||
Output.print_sstring conf {|</a> |};
|
||||
print_someone conf base p1;
|
||||
Output.print_sstring conf " ";
|
||||
Output.print_sstring conf (transl_nth conf "and" 0);
|
||||
Output.print_sstring conf " ";
|
||||
print_someone conf base p2;
|
||||
Output.print_sstring conf "</p>"
|
||||
| _ -> (
|
||||
match p_getenv conf.env "ip" with
|
||||
| Some ip ->
|
||||
let ip = iper_of_string ip in
|
||||
let s1 =
|
||||
match p_getenv conf.env "iexcl" with
|
||||
| Some "" | None -> Adef.encoded ""
|
||||
| Some s -> "¡=" ^<^ Mutil.encode s
|
||||
in
|
||||
let s2 =
|
||||
match p_getenv conf.env "fexcl" with
|
||||
| Some "" | None -> Adef.encoded ""
|
||||
| Some s -> "&fexcl=" ^<^ Mutil.encode s
|
||||
in
|
||||
if s1 <^> Adef.encoded "" || s2 <^> Adef.encoded "" then (
|
||||
let p = poi base ip in
|
||||
let s = gen_person_text conf base p in
|
||||
Output.print_sstring conf {|<p><a href="|};
|
||||
Output.print_string conf (commd conf);
|
||||
Output.print_sstring conf {|m=MRG_DUP&ip=|};
|
||||
Output.print_string conf (string_of_iper ip |> Mutil.encode);
|
||||
Output.print_string conf s1;
|
||||
Output.print_string conf s2;
|
||||
Output.print_sstring conf {|">|};
|
||||
Output.print_sstring conf
|
||||
(Utf8.capitalize_fst (transl conf "continue merging"));
|
||||
Output.print_sstring conf {| (|};
|
||||
Output.print_sstring conf
|
||||
(transl_a_of_b conf
|
||||
(transl conf "possible duplications")
|
||||
(reference conf base p s :> string)
|
||||
(s :> string));
|
||||
Output.print_sstring conf {|)</p>|})
|
||||
| None -> ())
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user