+(* Poll Bugzilla, find new tasks, and add them to the database.
+ * This runs from a cron job.
+ *)
+
+open CalendarLib
+open Unix
+open Printf
+
+let quote = Filename.quote
+
+let products = [
+ "Red Hat Enterprise Linux 6",
+ Printer.Date.from_string "2016-11-14";
+ "Red Hat Enterprise Linux 7",
+ Printer.Date.from_string "2017-04-03";
+]
+
+let assigned_to = "rjones@redhat.com"
+let states = ["NEW";"ASSIGNED";"ON_DEV";"POST";"MODIFIED"]
+
+let () =
+ let rex = Pcre.regexp "^(\\d+) (\\S+) (.*)$" in
+ let errors = ref 0 in
+
+ let dbh = PGOCaml.connect ~database:"todo" () in
+
+ List.iter (
+ fun (product, my_deadline) ->
+ (* Query bugzilla. *)
+ let lines =
+ let cmd =
+ sprintf "bugzilla query -p %s -a %s -t %s --outputformat='%%{bug_id} %%{component} %%{short_desc}'"
+ (quote product) (quote assigned_to)
+ (quote (String.concat "," states)) in
+ let chan = open_process_in cmd in
+ let lines = ref [] in
+ (try while true do lines := input_line chan :: !lines done
+ with End_of_file -> ());
+ let stat = close_process_in chan in
+ (match stat with
+ | WEXITED 0 -> ()
+ | WEXITED i ->
+ eprintf "error: bugzilla command: exited with error %d\n%!" i;
+ incr errors
+ | WSIGNALED i ->
+ eprintf "error: bugzilla command: killed by signal %d\n%!" i;
+ incr errors
+ | WSTOPPED i ->
+ eprintf "error: bugzilla command: stopped by signal %d\n%!" i;
+ incr errors
+ );
+ List.rev !lines in
+
+ if lines <> [] then (
+ (* The output is <BUG> <COMPONENT> <DESCRIPTION>.
+ * The component is turned into a tag.
+ *)
+ List.iter (
+ fun line ->
+ try
+ let subs = Pcre.exec ~rex line in
+ let bugid = Int32.of_string (Pcre.get_substring subs 1) in
+ let component = Pcre.get_substring subs 2 in
+ let description = Pcre.get_substring subs 3 in
+
+ PGOCaml.begin_work dbh;
+
+ (* If the bug doesn't exist in the tasks table, add it. *)
+ let rows = PGSQL(dbh) "select id from tasks
+ where rhbz = $bugid" in
+ let taskid =
+ match rows with
+ | [id] -> id
+ | [] ->
+ printf "new task: RHBZ#%ld %s %s\n%!"
+ bugid component description;
+ PGSQL(dbh) "insert into tasks (description, rhbz)
+ values ($description, $bugid)";
+ PGOCaml.serial4 dbh "tasks_id_seq"
+ | _ -> assert false in
+
+ (* If the component doesn't exist as a tag, create it. *)
+ let rows =
+ PGSQL(dbh) "select id from tags where name = $component" in
+ let tagid =
+ match rows with
+ | [id] -> id
+ | [] ->
+ printf "new tag: %s\n%!" component;
+ PGSQL(dbh) "insert into tags (name, colour)
+ values ($component, 'blue')";
+ PGOCaml.serial4 dbh "tags_id_seq"
+ | _ -> assert false in
+
+ (* If the bug is not tagged with the component, tag it.
+ * If the bug changes component, this creates a second
+ * tag, which is intentional.
+ *)
+ let rows =
+ PGSQL(dbh) "select 1 from tags_tasks, tasks, tags
+ where tags_tasks.taskid = tasks.id
+ and tags_tasks.tagid = tags.id
+ and tasks.rhbz = $bugid
+ and tags.name = $component" in
+ if rows <> [Some 1_l] then
+ PGSQL(dbh) "insert into tags_tasks (tagid, taskid)
+ values ($tagid, $taskid)";
+
+ (* Add the bug to the todo table with the appropriate
+ * deadline.
+ *)
+ let rows =
+ PGSQL(dbh) "select 1 from todo where taskid = $taskid" in
+ if rows <> [Some 1_l] then
+ PGSQL(dbh) "insert into todo (taskid, deadline)
+ values ($taskid, $my_deadline::date)";
+
+ PGOCaml.commit dbh
+ with
+ Not_found ->
+ eprintf "error: line did not match regular expression: %s\n%!"
+ line;
+ incr errors
+ ) lines
+ )
+ ) products;
+
+ if !errors > 0 then
+ exit 1