<commit_before>(*
Copyright (c) 2012 <NAME>

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*)


open Batteries
open Eliom_lib.Lwt_ops

type feed =
  { author : int32
  ; id : int32
  ; date : CalendarLib.Calendar.t
  ; description : string
  ; url : string option
  ; parent: int32 option
  ; root : int32 option
  ; tags : string list
  ; score : int
  ; user : < email_digest : string; name: string >
  ; fav : bool
  ; vote : int
  ; count : int
  }

type feeds = feed list

type feed_generator =
  starting:int32 ->
  number:int32 ->
  user:int32 option ->
  unit ->
  feeds Lwt.t

let get_feeds_aux ?range
  ~feeds_filter
  ~users_filter
  ~user
  () =
  begin match range with
  | Some (limit, offset) ->
      Db.view
        (<:view< group {
          email_digest = md5[u.email];
        }
        by {
          f.id;
          f.url;
          f.description;
          f.timedate;
          f.author;
          f.parent;
          f.root;
          u.name;
          u.email;
        } order by f.id desc limit $int32:limit$ offset $int32:offset$
        | f in $Db_table.feeds$; $feeds_filter$ f;
          u in $Db_table.users$; $users_filter$ f u;
          f.author = u.id;
        >>)
  | None ->
      Db.view
        (<:view< group {
          email_digest = md5[u.email];
        }
        by {
          f.id;
          f.url;
          f.description;
          f.timedate;
          f.author;
          f.parent;
          f.root;
          u.name;
          u.email;
        } order by f.id desc
        | f in $Db_table.feeds$; $feeds_filter$ f;
          u in $Db_table.users$; $users_filter$ f u;
          f.author = u.id;
        >>)
  end
  >>= fun feeds ->
  let ids = List.map (fun x -> x#id) feeds in
  Db.view
    (<:view<
      group {
        tags = match array_agg[t.tag] with null -> $string_array:[]$ | x -> x;
      }
      by { t.id_feed }
      | t in $Db_table.feeds_tags$;
      in' t.id_feed $ids$
    >>)
  >>= fun tags ->
  Db.view
    (<:view<
      group { c = count[f.root] } by { f.root }
      | f in $Db_table.feeds$;
      (match f.root with
       | null -> false
       | root -> in' root $ids$
      )
    >>)
  >>= fun count ->
  Db.view
    (<:view< {
            f.id_feed;
            f.id_user;
            f.score;
            } | f in $Db_table.votes$;
            in' f.id_feed $ids$
     >>)
  >>= fun votes ->
  begin match user with
    | Some user_id ->
        Db.view
          (<:view< {
                  f.id_feed;
                  } | f in $Db_table.favs$;
                  f.id_user = $int32:user_id$; in' f.id_feed $ids$
           >>)
        >|= fun favs ->
        (favs, List.filter (fun x -> Int32.equal x#!id_user user_id) votes)
    | None ->
      Lwt.return ([], [])
  end
  >>= fun (favs, user_votes) ->
  let favs = List.map (fun x -> x#!id_feed) favs in
  let find_and_map find map default x =
    Option.map_default map default (List.Exceptionless.find find x)
  in
  let new_object o =
    let id = o#!id in
    let map map default x =
      find_and_map (fun x -> Int32.equal x#!id_feed id) map default x
    in
    { author = o#!author
    ; id
    ; date = o#!timedate
    ; description = o#!description
    ; url = o#?url
    ; parent = o#?parent
    ; root = o#?root
    ; tags = map (fun x -> List.filter_map identity x#!tags) [] tags
    ; score = List.fold_left (fun acc x -> if Int32.equal x#!id_feed id then acc + Int32.to_int x#!score else acc) 0 votes
    ; user = object method name = o#!name method email_digest = o#!email_digest end
    ; fav = List.exists (Int32.equal id) favs
    ; vote = map (fun x -> Int32.to_int x#!score) 0 user_votes
    ; count = find_and_map (fun e -> match e#?root with Some x -> Int32.equal x id | None -> assert false) (fun x -> Int64.to_int x#!c) 0 count
    }
  in
  Lwt.return (List.map new_object feeds)

let get_tree_feeds feed_id ~starting ~number ~user () =
  let feeds_filter f = (<:value< f.root = $int32:feed_id$ >>) in
  let users_filter _ _ = (<:value< true >>) in
  get_feeds_aux ~range:(number, starting) ~feeds_filter ~users_filter ~user ()

let get_links_feeds ~starting ~number ~user () =
  let feeds_filter f = (<:value< is_not_null f.url >>) in<commit_msg>Count comments for all feeds (root ands feeds) but this bug is not fix !<commit_after>(*
Copyright (c) 2012 <NAME>

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*)


open Batteries
open Eliom_lib.Lwt_ops

type feed =
  { author : int32
  ; id : int32
  ; date : CalendarLib.Calendar.t
  ; description : string
  ; url : string option
  ; parent: int32 option
  ; root : int32 option
  ; tags : string list
  ; score : int
  ; user : < email_digest : string; name: string >
  ; fav : bool
  ; vote : int
  ; count : int
  }

type feeds = feed list

type feed_generator =
  starting:int32 ->
  number:int32 ->
  user:int32 option ->
  unit ->
  feeds Lwt.t

let get_feeds_aux ?range
  ~feeds_filter
  ~users_filter
  ~user
  () =
  begin match range with
  | Some (limit, offset) ->
      Db.view
        (<:view< group {
          email_digest = md5[u.email];
        }
        by {
          f.id;
          f.url;
          f.description;
          f.timedate;
          f.author;
          f.parent;
          f.root;
          u.name;
          u.email;
        } order by f.id desc limit $int32:limit$ offset $int32:offset$
        | f in $Db_table.feeds$; $feeds_filter$ f;
          u in $Db_table.users$; $users_filter$ f u;
          f.author = u.id;
        >>)
  | None ->
      Db.view
        (<:view< group {
          email_digest = md5[u.email];
        }
        by {
          f.id;
          f.url;
          f.description;
          f.timedate;
          f.author;
          f.parent;
          f.root;
          u.name;
          u.email;
        } order by f.id desc
        | f in $Db_table.feeds$; $feeds_filter$ f;
          u in $Db_table.users$; $users_filter$ f u;
          f.author = u.id;
        >>)
  end
  >>= fun feeds ->
  let ids = List.map (fun x -> x#id) feeds in
  Db.view
    (<:view<
      group {
        tags = match array_agg[t.tag] with null -> $string_array:[]$ | x -> x;
      }
      by { t.id_feed }
      | t in $Db_table.feeds_tags$;
      in' t.id_feed $ids$
    >>)
  >>= fun tags ->
  Db.view
    (<:view<
      group { c = count[f.parent] } by { f.parent }
      | f in $Db_table.feeds$;
      (match f.parent with
       | null -> false
       | parent -> in' parent $ids$
      )
    >>)
  >>= fun count ->
  Db.view
    (<:view< {
            f.id_feed;
            f.id_user;
            f.score;
            } | f in $Db_table.votes$;
            in' f.id_feed $ids$
     >>)
  >>= fun votes ->
  begin match user with
    | Some user_id ->
        Db.view
          (<:view< {
                  f.id_feed;
                  } | f in $Db_table.favs$;
                  f.id_user = $int32:user_id$; in' f.id_feed $ids$
           >>)
        >|= fun favs ->
        (favs, List.filter (fun x -> Int32.equal x#!id_user user_id) votes)
    | None ->
      Lwt.return ([], [])
  end
  >>= fun (favs, user_votes) ->
  let favs = List.map (fun x -> x#!id_feed) favs in
  let find_and_map find map default x =
    Option.map_default map default (List.Exceptionless.find find x)
  in
  let new_object o =
    let id = o#!id in
    let map map default x =
      find_and_map (fun x -> Int32.equal x#!id_feed id) map default x
    in
    { author = o#!author
    ; id
    ; date = o#!timedate
    ; description = o#!description
    ; url = o#?url
    ; parent = o#?parent
    ; root = o#?root
    ; tags = map (fun x -> List.filter_map identity x#!tags) [] tags
    ; score = List.fold_left (fun acc x -> if Int32.equal x#!id_feed id then acc + Int32.to_int x#!score else acc) 0 votes
    ; user = object method name = o#!name method email_digest = o#!email_digest end
    ; fav = List.exists (Int32.equal id) favs
    ; vote = map (fun x -> Int32.to_int x#!score) 0 user_votes
    ; count = find_and_map (fun e -> match e#?parent with Some x -> Int32.equal x id | None -> assert false) (fun x -> Int64.to_int x#!c) 0 count
      (*
       * On ne comptais (avec l'ancienne version) que le nombre de commentaires
       * pour les roots.
       *
       * Maintenant, nous comptons le nombre de commentaires pour tout les feeds
       * mais nous prennons en compte que les commentaires de premier niveau.
       *
       * TODO: compter la totalité des commentaires
       *)
    }
  in
  Lwt.return (List.map new_object feeds)

let get_tree_feeds feed_id ~starting ~number ~user () =
  let feeds_filter f = (<:value< f.root = $int32:feed_id$ >>) in
  let users_filter _ _ = (<:value< true >>) in
  get_feeds_aux ~range:(number, starting) ~feeds_filter ~users_filter ~user ()

let get_links_feeds ~starting ~number ~user () =
  let feeds_filter f = (<:value< is_not_null f.url >>) in
{'fraction_non_alphanumeric': 0.11138888888888888, 'fraction_numerical': 0.008703703703703703, 'mean_word_length': 2.1434807916181606, 'pattern_counts': {'":': 0, '<': 43, '<?xml version=': 0, '>': 123, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3588637', 'n_tokens_mistral': 3713, 'n_tokens_neox': 3554, 'n_words': 1185}