WIP: Activity feed feature

* backend support for activity feed
* partial frontend support (still needs work)
* save issue modification timestamp to db
* rename commit-id -> commit-sha everywhere for consistency
* "No data" texts for UI collections when no data exists
This commit is contained in:
Teemu Patja 2017-02-26 23:40:59 +02:00
parent c33fb858f9
commit 2599b85d41
No known key found for this signature in database
GPG Key ID: F5B7035E6580FD4C
13 changed files with 226 additions and 90 deletions

View File

@ -0,0 +1,5 @@
ALTER TABLE "public"."issues" ADD COLUMN "updated" timestamp without time zone DEFAULT timezone('utc'::text, now());
ALTER TABLE "public"."issues" RENAME COLUMN "commit_id" TO "commit_sha";
ALTER TABLE "public"."pull_requests" RENAME COLUMN "commit_id" TO "commit_sha";

View File

@ -0,0 +1,87 @@
create view bounties_view as
select
i.title as issue_title,
i.issue_number,
r.repo as repo_name,
u.name as user_name,
u.avatar_url as user_avatar_url,
i.payout_receipt,
i.balance,
i.updated as updated
FROM issues i, users u, repositories r
WHERE r.repo_id = i.repo_id
AND r.user_id = u.id
and contract_address is not null
and comment_id is not null
order by updated;
create view claims_view as
select
i.title as issue_title,
i.issue_number,
r.repo as repo_name,
u.name as user_name,
u.avatar_url as user_avatar_url,
i.payout_receipt,
p.updated as updated,
i.balance,
p.state as pr_state
FROM issues i, users u, repositories r, pull_requests p
WHERE r.repo_id = i.repo_id
AND p.issue_id = i.issue_id
AND p.user_id = u.id
and i.contract_address is not null
and i.comment_id is not null
order by p.updated;
create view activity_feed_view as
select 'open-claim' as type,
issue_title,
repo_name,
issue_number,
user_name,
user_avatar_url,
balance,
updated
from claims_view
where
pr_state=0
and payout_receipt is null
union
select 'claim-payout' as type,
issue_title,
repo_name,
issue_number,
user_name,
user_avatar_url,
balance,
updated
from claims_view
where
pr_state=1
and payout_receipt is not null
union
select 'new-bounty' as type,
issue_title,
repo_name,
issue_number,
user_name,
user_avatar_url,
balance,
updated
from bounties_view
where balance=0
and payout_receipt is null
union
select 'balance-update' as type,
issue_title,
repo_name,
issue_number,
user_name,
user_avatar_url,
balance,
updated
from bounties_view
where balance>0
and payout_receipt is null
order by updated desc;

View File

@ -121,17 +121,19 @@ INSERT INTO issues (repo_id, issue_id, issue_number, title)
FROM issues
WHERE repo_id = :repo_id AND issue_id = :issue_id);
-- :name update-commit-id :<! :1
-- :doc updates issue with commit_id
-- :name update-commit-sha :<! :1
-- :doc updates issue with commit_sha
UPDATE issues
SET commit_id = :commit_id
SET commit_sha = :commit_sha,
updated = timezone('utc'::text, now())
WHERE issue_id = :issue_id
RETURNING repo_id, issue_id, issue_number, title, commit_id, contract_address;
RETURNING repo_id, issue_id, issue_number, title, commit_sha, contract_address;
-- :name update-transaction-hash :! :n
-- :doc updates transaction-hash for a given issue
UPDATE issues
SET transaction_hash = :transaction_hash
SET transaction_hash = :transaction_hash,
updated = timezone('utc'::text, now())
WHERE issue_id = :issue_id;
@ -153,7 +155,8 @@ WITH t AS (
AND i.issue_id = :issue_id
)
UPDATE issues i
SET contract_address = :contract_address
SET contract_address = :contract_address,
updated = timezone('utc'::text, now())
FROM t
WHERE i.issue_id = :issue_id
RETURNING t.issue_id, t.issue_number, t.title, t.transaction_hash, i.contract_address, t.login, t.repo, t.repo_id;
@ -161,7 +164,8 @@ RETURNING t.issue_id, t.issue_number, t.title, t.transaction_hash, i.contract_ad
-- :name update-comment-id :! :n
-- :doc updates comment-id for a given issue
UPDATE issues
SET comment_id = :comment_id
SET comment_id = :comment_id,
updated = timezone('utc'::text, now())
WHERE issue_id = :issue_id;
-- :name list-pending-deployments :? :*
@ -182,7 +186,7 @@ INSERT INTO pull_requests (pr_id,
pr_number,
issue_number,
issue_id,
commit_id,
commit_sha,
user_id,
state)
VALUES(:pr_id,
@ -190,14 +194,14 @@ VALUES(:pr_id,
:pr_number,
:issue_number,
:issue_id,
:commit_id,
:commit_sha,
:user_id,
:state)
ON CONFLICT (pr_id) DO UPDATE
SET
state = :state,
updated = timezone('utc'::text, now()),
commit_id = :commit_id;
commit_sha = :commit_sha;
-- Bounties ------------------------------------------------------------------------
@ -247,42 +251,31 @@ AND i.payout_hash IS NOT NULL;
-- :name update-confirm-hash :! :n
-- :doc updates issue with confirmation hash
UPDATE issues
SET confirm_hash = :confirm_hash
SET confirm_hash = :confirm_hash,
updated = timezone('utc'::text, now())
WHERE issue_id = :issue_id;
-- :name update-execute-hash :! :n
-- :doc updates issue with execute transaction hash
UPDATE issues
SET execute_hash = :execute_hash
SET execute_hash = :execute_hash,
updated = timezone('utc'::text, now())
WHERE issue_id = :issue_id;
-- :name update-payout-hash :! :n
-- :doc updates issue with payout transaction hash
UPDATE issues
SET payout_hash = :payout_hash
SET payout_hash = :payout_hash,
updated = timezone('utc'::text, now())
WHERE issue_id = :issue_id;
-- :name update-payout-receipt :! :n
-- :doc updates issue with payout transaction receipt
UPDATE issues
SET payout_receipt = :payout_receipt
SET payout_receipt = :payout_receipt,
updated = timezone('utc'::text, now())
WHERE issue_id = :issue_id;
-- :name all-bounties-list :? :*
-- :doc open (not merged) bounty issues
SELECT
i.contract_address AS contract_address,
i.issue_id AS issue_id,
i.issue_number AS issue_number,
i.title AS issue_title,
i.repo_id AS repo_id,
i.balance AS issue_balance,
r.login AS owner_name,
r.repo AS repo_name
FROM issues i, repositories r
WHERE
r.repo_id = i.repo_id
AND i.commit_id IS NULL;
-- :name owner-bounties-list :? :*
-- :doc all bounty issues for given owner
@ -352,7 +345,7 @@ SELECT
FROM issues i, repositories r
WHERE r.repo_id = i.repo_id
AND r.user_id = :owner_id
AND i.commit_id IS NULL
AND i.commit_sha IS NULL
AND NOT exists(SELECT 1
FROM pull_requests
WHERE issue_number = i.issue_number
@ -398,7 +391,8 @@ WHERE contract_address = :contract_address;
-- :name update-balance :! :n
-- :doc updates balance of a wallet attached to a given issue
UPDATE issues
SET balance = :balance
SET balance = :balance,
updated = timezone('utc'::text, now())
WHERE contract_address = :contract_address;
@ -419,6 +413,8 @@ WHERE issue_id = :issue_id;
-- :name top-hunters :? :*
-- :doc list of user that have reveived bounty payouts with sum of
-- earnings
SELECT
u.id AS user_id,
u.login AS login,
@ -427,8 +423,24 @@ u.avatar_url AS avatar_url,
SUM(i.balance) AS total_eth
FROM issues i, users u, pull_requests pr
WHERE
pr.commit_id = i.commit_id
pr.commit_sha = i.commit_sha
AND u.id = pr.user_id
AND i.payout_receipt IS NOT NULL
GROUP BY u.id
ORDER BY total_eth DESC;
-- :name bounties-activity :? :*
-- :doc data for bounty activity feed
SELECT
type,
issue_title,
repo_name,
issue_number,
user_name,
user_avatar_url,
balance,
updated
FROM activity_feed_view
ORDER BY updated DESC
LIMIT 100;

View File

@ -4,10 +4,6 @@
[clojure.set :refer [rename-keys]]))
(defn list-all-bounties
[]
(jdbc/with-db-connection [con-db *db*]
(db/all-bounties-list con-db)))
(defn list-owner-bounties
[owner]
@ -75,3 +71,8 @@
[]
(jdbc/with-db-connection [con-db *db*]
(db/top-hunters con-db)))
(defn bounty-activity
[]
(jdbc/with-db-connection [con-db *db*]
(db/bounties-activity con-db)))

View File

@ -12,12 +12,12 @@
:issue_number issue-number
:title issue-title})))
(defn update-commit-id
"Updates issue with commit_id"
[commit-id issue-id]
(defn update-commit-sha
"Updates issue with commit-sha"
[issue-id commit-sha]
(jdbc/with-db-connection [con-db *db*]
(db/update-commit-id con-db {:issue_id issue-id
:commit_id commit-id})))
(db/update-commit-sha con-db {:issue_id issue-id
:commit_sha commit-sha})))
(defn update-transaction-hash
"Updates issue with transaction-hash"

View File

@ -116,6 +116,11 @@
(bounties-db/top-hunters))))
(defn activity-feed []
(let [activity-items (bounties-db/bounty-activity)]
(map #(update % :balance decimal->str) activity-items)))
(defapi service-routes
{:swagger {:ui "/swagger-ui"
:spec "/swagger.json"
@ -127,10 +132,9 @@
(GET "/top-hunters" []
(log/debug "/top-hunters")
(ok (top-hunters)))
(context "/bounties" []
(GET "/all" []
(log/debug "/bounties/all")
(ok (bounties-db/list-all-bounties))))
(GET "/activity-feed" []
(log/debug "/activity-feed")
(ok (activity-feed)))
(context "/user" []
(GET "/" []
:auth-rules authenticated?

View File

@ -30,9 +30,9 @@
(= "labeled" action)
(= bounties/label-name (get-in issue [:label :name]))))
(defn find-commit-id
(defn find-commit-sha
[user repo issue-number event-types]
(log/debug "find-commit-id" user repo issue-number event-types)
(log/debug "find-commit-sha" user repo issue-number event-types)
(some identity (map #(->
(github/get-issue-events user repo issue-number)
(find-issue-event % user)
@ -55,8 +55,8 @@
[{{{owner :login} :owner repo :name} :repository
{issue-id :id issue-number :number} :issue}]
(log/debug "handle-issue-closed" owner repo issue-number issue-id)
(when-let [commit-id (find-commit-id owner repo issue-number ["referenced" "closed"])]
(log/debug (format "Issue %s/%s/%s closed with commit %s" owner repo issue-number commit-id))
(when-let [commit-sha (find-commit-sha owner repo issue-number ["referenced" "closed"])]
(log/debug (format "Issue %s/%s/%s closed with commit %s" owner repo issue-number commit-sha))
(log/info "NOT considering event as bounty winner")
;; TODO: disabled for now since the system is meant to be used
;; exclusively via pull requests. issue closed event without a PR
@ -65,7 +65,7 @@
;; maintainer (could be that the bounty hunter had write access
;; to master, but that scenario should be very rare and better
;; not to support it)
#_(issues/close commit-id issue-id)))
#_(issues/close commit-sha issue-id)))
(def ^:const keywords
[#"(?i)close:?\s+#(\d+)"
@ -148,19 +148,19 @@
(log/info "PR with reference to bounty issue"
bounty-issue-number "opened")
(pull-requests/save (merge pr-data {:state :opened
:commit_id head-sha})))
:commit-sha head-sha})))
:closed (if merged?
(do (log/info "PR with reference to bounty issue"
bounty-issue-number "merged")
(pull-requests/save
(merge pr-data {:state :merged
:commit_id head-sha}))
(issues/update-commit-id head-sha (:id issue)))
:commit-sha head-sha}))
(issues/update-commit-sha (:id issue) head-sha))
(do (log/info "PR with reference to bounty issue"
bounty-issue-number "closed with no merge")
(pull-requests/save
(merge pr-data {:state :closed
:commit_id head-sha}))))))))
:commit-sha head-sha}))))))))
(defn handle-issue

View File

@ -1,12 +1,15 @@
(ns commiteth.activity
(:require [re-frame.core :as rf]))
(:require [re-frame.core :as rf]
[reagent.core :as r]))
(defn activity-item [{{image-url :avatar-url
display-name :display-name} :user
timestamp :timestamp
description :description} item]
(defn activity-item [{image-url :user_avatar_url
display-name :user_name
timestamp :updated
balance :balance
issue-title :issue_title
item-type :type} item]
[:div.item.activity-item
[:div.ui.mini.circular.image
@ -14,13 +17,16 @@
[:div.content
[:div.header display-name]
[:div.description
[:p description]]
[:div.time timestamp]]])
[:p item-type]
[:p issue-title]]
#_[:div.time timestamp]]])
(defn activity-page []
(let [activity-items (rf/subscribe [:activity-feed])]
(fn []
[:div.ui.container
(into [:div.ui.items]
(for [item @activity-items]
[activity-item item]))])))
(if (empty? @activity-items)
[:div.ui.text "No data"]
(into [:div.ui.items]
(for [item @activity-items]
[activity-item item])))])))

View File

@ -112,16 +112,18 @@
(defn top-hunters []
(let [top-hunters (rf/subscribe [:top-hunters])]
(fn []
(into [:div.ui.items.top-hunters]
(map-indexed (fn [idx hunter]
[:div.item
[:div.leader-ordinal (str (+ 1 idx))]
[:div.ui..mini.circular.image
[:img {:src (:avatar-url hunter)}]]
[:div.content
[:div.header (:display-name hunter)]
[:div.description (str "ETH " (:total-eth hunter))]]])
@top-hunters)))))
(if (empty? @top-hunters)
[:div.ui.text "No data"]
(into [:div.ui.items.top-hunters]
(map-indexed (fn [idx hunter]
[:div.item
[:div.leader-ordinal (str (+ 1 idx))]
[:div.ui..mini.circular.image
[:img {:src (:avatar-url hunter)}]]
[:div.content
[:div.header (:display-name hunter)]
[:div.description (str "ETH " (:total-eth hunter))]]])
@top-hunters))))))
(defn page []
(fn []
@ -174,6 +176,7 @@
(reset! active-user nil)))
(defn load-data []
(rf/dispatch [:load-activity-feed])
(rf/dispatch [:load-top-hunters])
(load-user))

View File

@ -6,19 +6,23 @@
:repos-loading? false
:repos {}
:owner-bounties {}
:top-hunters [{:avatar-url "https://randomuser.me/api/portraits/men/4.jpg"
:display-name "Place Holder"
:total-eth "11 000.00"}
{:avatar-url "https://randomuser.me/api/portraits/men/6.jpg"
:display-name "Dummy User"
:total-eth "8 400.00"}]
:activity-feed [{:type :submit-claim
:user {:display-name "Dummy User"
:avatar-url "https://randomuser.me/api/portraits/men/6.jpg"}
:description "Submitted a claim for X"
:timestamp "1 day ago"}
{:type :submit-claim
:user {:display-name "Place Holder"
:avatar-url "https://randomuser.me/api/portraits/men/4.jpg"}
:description "Posted ETH 15 bounty to Y"
:timestamp "2 days ago"}]})
:top-hunters []
:activity-feed [] #_[{:type :create-bounty
:user {:display-name "Dummy User"
:avatar-url "https://randomuser.me/api/portraits/men/6.jpg"}
:issue-title "Feature X"
:issue-url "https://github.com/foo/bar/issues/2"
:timestamp "1 day ago"}
{:type :submit-claim
:user {:display-name "Place Holder"
:avatar-url "https://randomuser.me/api/portraits/men/4.jpg"}
:balance-eth "15"
:timestamp "2 days ago"}
{:type :balance-update
:user {:display-name "Place Holder"
:avatar-url "https://randomuser.me/api/portraits/men/4.jpg"}
:issue-title "Feature Y"
:issue-url "https://github.com/foo/bar/issues/1"
:balance-eth "15"
:timestamp "2 days ago"}]})

View File

@ -94,6 +94,20 @@
(fn [db [_ top-hunters]]
(assoc db :top-hunters top-hunters)))
(reg-event-fx
:load-activity-feed
(fn [{:keys [db]} [_]]
{:db db
:http {:method GET
:url "/api/activity-feed"
:on-success #(dispatch [:set-activity-feed %])}}))
(reg-event-db
:set-activity-feed
(fn [db [_ activity-feed]]
(assoc db :activity-feed activity-feed)))
(reg-event-fx
:load-owner-bounties