Skip to content

Commit

Permalink
Merge branch 'release/1.0.0'
Browse files Browse the repository at this point in the history
  • Loading branch information
amalloy committed May 20, 2011
2 parents 1ca94b5 + cec6723 commit e0a84a5
Show file tree
Hide file tree
Showing 13 changed files with 174 additions and 90 deletions.
5 changes: 3 additions & 2 deletions config.clj
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
:db-pwd nil
:host "smtp.googlemail.com"
:user "team@4clojure.com"
:problem-submission false
:problem-submission true
:advanced-user-count 50
:pass ""}
:pass ""
:golfing-active true}
2 changes: 1 addition & 1 deletion load-data.bat
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
@echo off
for /f %%a in ('lein classpath') do @set project_classpath=%%a
java -cp %project_classpath% clojure.main .\src\foreclojure\data_set.clj
java -cp %project_classpath% clojure.main -i .\src\foreclojure\mongo.clj -e "(use 'foreclojure.mongo) (prepare-mongo) (shutdown-agents)"
2 changes: 1 addition & 1 deletion load-data.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
project_classpath=`lein classpath`
echo $project_classpath
java -cp $project_classpath clojure.main ./src/foreclojure/data_set.clj
java -cp $project_classpath clojure.main -i ./src/foreclojure/mongo.clj -e "(use 'foreclojure.mongo) (prepare-mongo) (shutdown-agents)"
6 changes: 3 additions & 3 deletions project.clj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
(defproject foreclojure "0.3.1"
(defproject foreclojure "1.0.0"
:description "4clojure - a website for lisp beginners"
:dependencies [[org.clojure/clojure "1.2.1"]
[org.clojure/clojure-contrib "1.2.0"]
:dependencies [[clojure "1.2.1"]
[clojure-contrib "1.2.0"]
[compojure "0.6.2"]
[hiccup "0.2.4"]
[clojail "0.4.0-SNAPSHOT"]
Expand Down
17 changes: 2 additions & 15 deletions src/foreclojure/core.clj
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
(ns foreclojure.core
(:use compojure.core
[foreclojure static problems login register golf
users config social version graphs db-utils utils]
users config social version graphs mongo utils]
ring.adapter.jetty
somnium.congomongo
(ring.middleware (reload :only [wrap-reload])
Expand All @@ -10,20 +10,7 @@
[sandbar.stateful-session :as session]
[ring.util.response :as response]))

(mongo!
:host (if-let [host (:db-host config)]
host
"localhost")
:db "mydb")

(if-let [db-user (:db-user config)]
(if-let [db-pwd (:db-pwd config)]
(authenticate db-user db-pwd)))

(add-index! :users [:user] :unique true)
(add-index! :users [[:solved -1]])

(reconcile-solved-count)
(prepare-mongo)

(defroutes main-routes
(GET "/" [] (welcome-page))
Expand Down
5 changes: 0 additions & 5 deletions src/foreclojure/data_set.clj
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,9 @@

(defn load-problems []
(do
(mongo! :db :mydb)

(insert! :seqs
{:_id "problems"
:seq 87})

(insert! :problems
{:_id 1
:title "Nothing but the Truth"
Expand Down Expand Up @@ -1014,5 +1011,3 @@ number of prime numbers."
"(= (__ 1 2 10 100 1300) '(= (* (+ 1 2 10) 100) 1300)"]})

))

(load-problems)
23 changes: 0 additions & 23 deletions src/foreclojure/db_utils.clj

This file was deleted.

69 changes: 69 additions & 0 deletions src/foreclojure/mongo.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
(ns foreclojure.mongo
(:use somnium.congomongo
[foreclojure.data-set :only [load-problems]]
[foreclojure.config :only [config]]
[foreclojure.problems :only [total-solved get-problem-list]]
[foreclojure.users :only [get-users]]))

(defn connect-to-db []
(let [{:keys [db-user db-pwd db-host]} config]
(mongo!
:host (or db-host "localhost")
:db "mydb")
(when (and db-user db-pwd)
(authenticate db-user db-pwd))))

(defn prepare-problems []
(when-not (fetch-one :problems)
(load-problems))
(add-index! :problems [:solved]))

(defn prepare-seqs []
(update! :seqs
{:_id "problems"}
{:$set {:seq (apply max
(map :_id
(fetch :problems
:only [:_id])))}}))

;; make it easier to get off the ground by marking contributors automatically
;; useful since some "in-development" features aren't enabled for all users
(defn prepare-users []
(add-index! :users [:user] :unique true)
(add-index! :users [[:solved -1]])
(update! :users
{:user {:$in ["amalloy" "dbyrne" "raynes" "cmeier" "devn"
"citizen428" "daviddavis" "clinteger"]}}
{:$set {:contributor true}}
:upsert false
:multiple true))

(defn prepare-solutions []
(add-index! :solutions [:user :problem]))

(defn reconcile-solved-count
"Overwrites the times-solved field in the problems collection based on data from the users collection. Should only be called on server startup since it isn't a safe operation. Also updates the total-solved agent."
[]
(send
total-solved +
(let [problems (get-problem-list)]
(reduce
#(do
(update! :problems
{:_id (first %2)}
{:$set {:times-solved (last %2)}})
(+ %1 (last %2)))
0
(reduce #(update-in %1 [%2] inc)
(reduce #(conj %1 [%2 0])
{}
(map :_id problems))
(mapcat :solved (get-users)))))))

(defn prepare-mongo []
(connect-to-db)
(prepare-problems)
(prepare-seqs)
(prepare-users)
(prepare-solutions)
(reconcile-solved-count))
53 changes: 33 additions & 20 deletions src/foreclojure/problems.clj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
(:use (foreclojure utils config
[social :only [tweet-link gist!]]
[feeds :only [create-feed]]
[users :only [golfer?]])
[users :only [golfer? get-user-id]]
[solutions :only [save-solution get-solution]])
(clojail [core :exclude [safe-read]] testers)
somnium.congomongo
(hiccup form-helpers page-helpers core)
Expand Down Expand Up @@ -62,15 +63,15 @@
(count (remove #(Character/isWhitespace %)
code)))

(defn record-golf-score! [user-name problem-id score]
(defn record-golf-score! [user-id problem-id score]
(let [user-score-key (keyword (str "scores." problem-id))
problem-score-key (keyword (str "scores." score))
[problem-scores-key user-subkey] (map mongo-key-from-number
[score problem-id])]
(when-let [{:keys [_id scores] :as user}
(from-mongo
(fetch-one :users
:where {:user user-name}))]
:where {:_id user-id}))]
(let [old-score-real (get scores user-subkey)
old-score-test (or old-score-real 1e6)
old-score-key (keyword (str "scores." old-score-real))]
Expand All @@ -91,6 +92,17 @@
{:_id _id}
{:$set {user-score-key score}}))))))

(defn store-completed-state! [username problem-id code]
(let [{user-id :_id} (fetch-one :users
:where {:user username}
:only [:_id])]
(when (not-any? #{problem-id} (get-solved username))
(update! :users {:_id user-id} {:$addToSet {:solved problem-id}})
(update! :problems {:_id problem-id} {:$inc {:times-solved 1}})
(send total-solved inc))
(record-golf-score! user-id problem-id (code-length code))
(save-solution (? user-id) (? problem-id) (? code))))

(defn mark-completed [problem code & [user]]
(let [user (or user (session/session-get :user))
{:keys [_id approved]} problem
Expand All @@ -101,11 +113,7 @@
(cond
(not approved) (str "You've solved the unapproved problem. Now you can approve it!")
user (do
(when (not-any? #{_id} (get-solved user))
(update! :users {:user user} {:$addToSet {:solved _id}})
(update! :problems {:_id _id} {:$inc {:times-solved 1}})
(send total-solved inc))
(record-golf-score! user _id (code-length code))
(store-completed-state! user _id code)
(str "Congratulations, you've solved the problem!"
"<br />" (next-problem-link _id)))
:else (str "You've solved the problem! If you "
Expand Down Expand Up @@ -182,7 +190,7 @@
[:span#graph-link "View Chart"]]])))

(def-page code-box [id]
(let [{:keys [title tags description restricted tests approved user]}
(let [{:keys [_id title tags description restricted tests approved user]}
(get-problem (Integer. id))]
[:div
[:span#prob-title
Expand Down Expand Up @@ -213,7 +221,10 @@
[:p#instruct "Code which fills in the blank: "]
(text-area {:id "code-box"
:spellcheck "false"}
:code (session/flash-get :code))
:code (or (session/flash-get :code)
(-> (session/session-get :user)
(get-user-id)
(get-solution ,,, _id))))
[:div#golfgraph
(render-golf-chart)]
(hidden-field :id id)
Expand Down Expand Up @@ -330,16 +341,18 @@
(flash-error "You are not authorized to submit a problem." "/problems"))))

(defn edit-problem [id]
(let [{:keys [title user tags restricted description tests]} (get-problem id)]
(doseq [[k v] {:prob-id id
:author user
:title title
:tags (s/join " " tags)
:restricted (s/join " " restricted)
:description description
:tests (s/join "\r\n\r\n" tests)}]
(session/flash-put! k v))
(response/redirect "/problems/submit")))
(if (approver? (session/session-get :user))
(let [{:keys [title user tags restricted description tests]} (get-problem id)]
(doseq [[k v] {:prob-id id
:author user
:title title
:tags (s/join " " tags)
:restricted (s/join " " restricted)
:description description
:tests (s/join "\r\n\r\n" tests)}]
(session/flash-put! k v))
(response/redirect "/problems/submit"))
(flash-error "You don't have access to this page" "/problems")))

(defn approve-problem [id]
"take a user submitted problem and approve it"
Expand Down
39 changes: 27 additions & 12 deletions src/foreclojure/social.clj
Original file line number Diff line number Diff line change
Expand Up @@ -23,37 +23,52 @@
:last-sent (if ok now last-sent)}))))
(apply f args)))))

(def clojure-hashtag (throttled (constantly " #clojure")
(def clojure-hashtag (throttled (constantly "#clojure ")
(* 1000 60 60))) ; hourly

(defn tweet-link [status & [anchor-text]]
(str "<a href=\"http://twitter.com/home?status="
(URLEncoder/encode status) "\">"
(defn tweet-link [id status & [anchor-text]]
(str "<a href=\"http://twitter.com/share?"
"text=" (URLEncoder/encode status)
"&url=" (URLEncoder/encode
(str "https://4clojure.com/problem/" id))
"&related=4clojure"
"\">"
(or anchor-text "Twitter")
"</a>"))

(defn get-problem-title [id]
(:title
(fetch-one :problems
:only [:title]
:where {:_id id})))

(defn gist!
"Create a new gist containing a user's solution to a problem and
return its url."
[user-name problem-num solution]
(let [user-name (or user-name "anonymous")
{name :title} (fetch-one :problems
:where {:_id problem-num})
(let [[user-name possessive] (if user-name
[user-name "'s"]
["anonymous" nil])
name (get-problem-title problem-num)
filename (str user-name "-4clojure-solution" problem-num ".clj")
text (str ";; " user-name "'s solution to " name "\n"
text (str ";; " user-name possessive " solution to " name "\n"
";; https://4clojure.com/problem/" problem-num
"\n\n"
solution)]
(try
(->> (gist/new-gist {} filename text)
:repo
(str "https://gist.github.com/"))
(catch Throwable _ nil))))
(catch Throwable _))))

(defn tweet-solution [id gist-url & [link-text]]
(let [status-msg (str "Check out how I solved https://4clojure.com/problem/"
id " - " gist-url " #4clojure" (clojure-hashtag))]
(tweet-link status-msg link-text)))
(let [status-msg (str "Check out how I solved "
(let [title (get-problem-title id)]
(if (> (count title) 35)
(str "problem " id)
(str "\"" title "\"")))
" on #4clojure " (clojure-hashtag) gist-url)]
(tweet-link id status-msg link-text)))

(def-page share-page []
(if-let [[id code] (session/session-get :code)]
Expand Down
17 changes: 17 additions & 0 deletions src/foreclojure/solutions.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
(ns foreclojure.solutions
(:use foreclojure.utils
somnium.congomongo
(amalloy.utils [debug :only [?]]))
(:require [clojure.string :as s]))

(defn get-solution [user-id problem-id]
(:code (fetch-one :solutions
:where {:user user-id
:problem problem-id})))

(defn save-solution [user-id problem-id code]
(update! :solutions
{:user user-id
:problem problem-id}
{:$set {:code code}}
:upsert true))
6 changes: 6 additions & 0 deletions src/foreclojure/users.clj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@
(when (:golfing-active config)
[:golfer])))

(defn get-user-id [name]
(:_id
(fetch-one :users
:where {:user name}
:only [:_id])))

(defn get-users []
(let [users (from-mongo
(fetch :users
Expand Down
Loading

0 comments on commit e0a84a5

Please sign in to comment.