Permalink
Browse files

add in a /q/contact/date/ interface to query contacts

  • Loading branch information...
1 parent 5e158e6 commit a1fd176d21d025bd0d5c69d6287df5b97ba2203d @avsm committed Aug 17, 2009
Showing with 157 additions and 1 deletion.
  1. +11 −0 client/ocaml/lifedb.ml
  2. +6 −1 lifedb_dispatch.ml
  3. +34 −0 lifedb_query.ml
  4. +96 −0 lifedb_schema.ml
  5. +4 −0 lifedb_schema.mli
  6. +1 −0 lifedb_schema_generator.ml
  7. +5 −0 utils.ml
View
@@ -156,5 +156,16 @@ module Rpc = struct
month: int;
days: int array
>
+
+ type json contact = <
+ first_name: string option;
+ last_name: string option;
+ uid: string
+ >
+ and contacts = <
+ results: int;
+ rows: contact list
+ >
+
end
end
View
@@ -126,7 +126,12 @@ let dispatch (lifedb : Lifedb_schema.Init.t) (syncdb : Sync_schema.Init.t) env (
|(`GET|`HEAD), "pltype" ->
mark_get_rpc cgi;
Lifedb_query.dispatch lifedb syncdb env cgi (`Mtype (List.tl url_list))
-
+ |(`GET|`HEAD), "q" -> begin
+ mark_get_rpc cgi;
+ match url_list with
+ |_ :: "contact" :: mode :: tl -> Lifedb_query.dispatch lifedb syncdb env cgi (`Contact_query (mode,tl))
+ |_ -> raise (Lifedb_rpc.Invalid_rpc "only mode required with /q/")
+ end
|_ -> raise (Invalid_rpc "Unknown request")
end
View
@@ -136,3 +136,37 @@ let dispatch lifedb syncdb env (cgi:Netcgi.cgi_activation) = function
|[e] -> Lifedb_rpc.return_file cgi e#file_name e#mime_type
|_ -> raise (Lifedb_rpc.Resource_conflict "multiple ids")
end
+ |`Contact_query (mode,tl) -> begin
+ match mode with
+ |"date" -> begin
+ let btm = Unix.gmtime 0. in
+ let fulldate = List.map (fun n -> try Some (int_of_string n) with _ -> None) tl in
+ match fulldate with
+ |[(Some year); (Some month); (Some day)] ->
+ let datefrom = {btm with Unix.tm_year=(year-1900); tm_mon=month-1; tm_mday=day} in
+ let dateto = {datefrom with Unix.tm_hour=23; tm_min=59; tm_sec=59} in
+ let sqldate x = Sqlite3.Data.INT (Int64.of_float (fst (Unix.handle_unix_error Unix.mktime x))) in
+ let sqlfrom = sqldate datefrom in
+ let sqlto = sqldate dateto in
+ let custom_where = "entry.created >= ? AND entry.created < ?", [sqlfrom; sqlto] in
+ let qs = LS.Entry.get_from_recipients ~custom_where lifedb in
+ let contact_of_res svc = match svc#contact with
+ |Some c -> Some (object
+ method first_name=c#first_name
+ method last_name=c#last_name
+ method uid=c#uid
+ end )
+ |None -> None
+ in
+ let contacts = List.fold_left (fun a (frm,recip) ->
+ let r = (contact_of_res frm) :: (List.map contact_of_res recip) in
+ let r = List.fold_left (fun a -> function None -> a |Some b -> b :: a) [] r in
+ r @ a
+ ) [] qs in
+ let contacts = results_of_search (unique (fun x y -> x#uid <> y#uid) contacts) in
+ Lifedb_rpc.return_json cgi (Query.json_of_contacts contacts)
+
+ |_ -> Lifedb_rpc.return_error cgi `Not_found "bad query date" "need yr/<month>/<day>"
+ end
+ |_ -> Lifedb_rpc.return_error cgi `Not_found "bad mode" "bad mode"
+ end
View
@@ -1479,6 +1479,102 @@ module Entry = struct
(* execute the SQL query *)
step_fold db stmt of_stmt
+ let get_from_recipients ?(custom_where=("",[])) db =
+ let q = "" in
+ let q = match custom_where with |"",_ -> q |w,_ -> q ^ "WHERE (" ^ w ^ ")" in
+ let sql="SELECT entry_from.id, entry_from.name, entry_from.uid, entry_from.contact_id, entry.id, entry.from_id, entry_from_contact.id, entry_from_contact.file_name, entry_from_contact.uid, entry_from_contact.first_name, entry_from_contact.last_name, entry_from_contact.mtime FROM entry LEFT JOIN service AS entry_from ON (entry_from.id = entry.from_id) LEFT JOIN contact AS entry_from_contact ON (entry_from_contact.id = entry_from.contact_id) " ^ q in
+ let stmt=Sqlite3.prepare db.db sql in
+ ignore(match custom_where with |_,[] -> () |_,eb ->
+ let pos = ref 1 in
+ List.iter (fun b ->
+ db_must_ok db (fun () -> Sqlite3.bind stmt !pos b);
+ incr pos;
+ ) eb);
+ let t ~id ~from ~recipients db = (from,recipients) in
+ (* convert statement into an ocaml object *)
+ let of_stmt stmt =
+ t
+ (* native fields *)
+ ~id:(
+ (match Sqlite3.column stmt 4 with
+ |Sqlite3.Data.NULL -> None
+ |x -> Some (match x with |Sqlite3.Data.INT i -> i |x -> (try Int64.of_string (Sqlite3.Data.to_string x) with _ -> failwith "error: entry id")))
+ )
+ (* foreign fields *)
+ ~from:(
+ Service.t
+ (* native fields *)
+ ~id:(
+ (match Sqlite3.column stmt 0 with
+ |Sqlite3.Data.NULL -> None
+ |x -> Some (match x with |Sqlite3.Data.INT i -> i |x -> (try Int64.of_string (Sqlite3.Data.to_string x) with _ -> failwith "error: entry_from id")))
+ )
+ ~name:(
+ (match Sqlite3.column stmt 1 with
+ |Sqlite3.Data.NULL -> failwith "null of_stmt"
+ |x -> Sqlite3.Data.to_string x)
+ )
+ ~uid:(
+ (match Sqlite3.column stmt 2 with
+ |Sqlite3.Data.NULL -> failwith "null of_stmt"
+ |x -> Sqlite3.Data.to_string x)
+ )
+ (* foreign fields *)
+ ~contact:(
+ (try
+ Some (
+ Contact.t
+ (* native fields *)
+ ~id:(
+ (match Sqlite3.column stmt 6 with
+ |Sqlite3.Data.NULL -> None
+ |x -> Some (match x with |Sqlite3.Data.INT i -> i |x -> (try Int64.of_string (Sqlite3.Data.to_string x) with _ -> failwith "error: entry_from_contact id")))
+ )
+ ~file_name:(
+ (match Sqlite3.column stmt 7 with
+ |Sqlite3.Data.NULL -> failwith "null of_stmt"
+ |x -> Sqlite3.Data.to_string x)
+ )
+ ~uid:(
+ (match Sqlite3.column stmt 8 with
+ |Sqlite3.Data.NULL -> failwith "null of_stmt"
+ |x -> Sqlite3.Data.to_string x)
+ )
+ ~first_name:(
+ (match Sqlite3.column stmt 9 with
+ |Sqlite3.Data.NULL -> None
+ |x -> Some (Sqlite3.Data.to_string x))
+ )
+ ~last_name:(
+ (match Sqlite3.column stmt 10 with
+ |Sqlite3.Data.NULL -> None
+ |x -> Some (Sqlite3.Data.to_string x))
+ )
+ ~mtime:(
+ (match Sqlite3.column stmt 11 with
+ |Sqlite3.Data.NULL -> failwith "null of_stmt"
+ |x -> match x with |Sqlite3.Data.FLOAT i -> i|x -> (try float_of_string (Sqlite3.Data.to_string x) with _ -> failwith "error: entry_from_contact mtime"))
+ )
+ (* foreign fields *)
+ db
+ ) with _ -> None))
+ db
+ )
+ ~recipients:(
+ (* foreign many-many mapping field *)
+ let sql' = "select service_id from map_recipients_entry_service where entry_id=?" in
+ let stmt' = Sqlite3.prepare db.db sql' in
+ let entry__id = Sqlite3.column stmt 4 in
+ db_must_ok db (fun () -> Sqlite3.bind stmt' 1 entry__id);
+ List.flatten (step_fold db stmt' (fun s ->
+ let i = match Sqlite3.column s 0 with |Sqlite3.Data.INT i -> i |_ -> assert false in
+ Service.get ~id:(Some i) db)
+ ))
+ db
+ in
+ (* execute the SQL query *)
+ step_fold db stmt of_stmt
+
let get_by_uid ~uid ?(custom_where=("",[])) db =
let q = "WHERE entry.uid=?" in
let q = match custom_where with |"",_ -> q |w,_ -> q ^ " AND (" ^ w ^ ")" in
View
@@ -286,6 +286,10 @@ module Entry : sig
?custom_where:string * Sqlite3.Data.t list -> Init.t ->
float list
+ val get_from_recipients :
+ ?custom_where:string * Sqlite3.Data.t list -> Init.t ->
+ (Service.t * Service.t list) list
+
val get_by_uid :
uid:string ->
?custom_where:string * Sqlite3.Data.t list -> Init.t ->
@@ -63,6 +63,7 @@ let lifedb = make [
["uid"],[];
["file_name"],[];
["created"],[];
+ ["from";"recipients"],[];
[],["uid"];
[],["file_name"]
], default_opts;
View
@@ -95,3 +95,8 @@ let results_of_search l =
method results = List.length l
method rows = l
end
+
+let rec unique fn = function
+| [] -> []
+| x :: xs ->
+ [x] @ (unique fn (List.filter (fn x) xs))

0 comments on commit a1fd176

Please sign in to comment.