so mamy things...

This commit is contained in:
didier amyot committed 2015-07-05 17:17:01 -04:00
1 parent f7a8d416c1
commit 78d50d9a6d
23 files changed
+441 -98

No files matched your search

+2 -2
View File
@@ -16,8 +16,8 @@ func init() {
func TestGetAllDoormen(t *testing.T) { func TestGetAllDoormen(t *testing.T) {
conn = inmemoryconnector.NewInMemoryDBConnector() conn = inmemoryconnector.NewInMemoryDBConnector()
conn.Save(doormen.QuickNewDoormanDefinition("foo", 0.25, 0.75)) conn.Save(doormen.QuickNewDoormanDefinition("foo", "0.25", "0.75"))
conn.Save(doormen.QuickNewDoormanDefinition("bar", 0.5, 0.5)) conn.Save(doormen.QuickNewDoormanDefinition("bar", "0.5", "0.5"))
req, err := http.NewRequest("GET", "http://bigtits.com/api/doormen", nil) req, err := http.NewRequest("GET", "http://bigtits.com/api/doormen", nil)
if err != nil { if err != nil {
+8
View File
@@ -44,11 +44,19 @@ func Create(root string) http.Handler {
UpdateDoorman, UpdateDoorman,
} }
getDoormanStatus := &simpleapi.Endpoint{
"/doormen/{id}/status",
`Returns the status of the doorman as a doorman client expect it.`,
"GET",
GetDoormanStatus,
}
api := simpleapi.New( api := simpleapi.New(
root, root,
serverSpecification, serverSpecification,
createDoorman, createDoorman,
allDoorman, allDoorman,
getDoormanStatus,
doorman, doorman,
updateDoorman, updateDoorman,
) )
+2 -2
View File
@@ -18,7 +18,7 @@ func TestCreateDoorman(t *testing.T) {
conn = inmemoryconnector.NewInMemoryDBConnector() conn = inmemoryconnector.NewInMemoryDBConnector()
payload := ` payload := `
{"name":"name", {"name":"name",
"values":[{"name":"red","probability":0.2},{"name":"blue","probability":0.8}], "values":[{"name":"red","probability":"1/5"},{"name":"blue","probability": "4/5"}],
"emails": ["alice"] "emails": ["alice"]
}` }`
req, err := http.NewRequest( req, err := http.NewRequest(
@@ -34,7 +34,7 @@ func TestCreateDoorman(t *testing.T) {
w := httptest.NewRecorder() w := httptest.NewRecorder()
CreateDoorman(w, req) CreateDoorman(w, req)
if w.Code != 201 { if w.Code != 201 {
t.Error("bad status code", w.Code) t.Error("bad status code", w.Code, "\n", w.Body)
} else if location := w.Header().Get("location"); location == "" { } else if location := w.Header().Get("location"); location == "" {
t.Error("missing location header", location) t.Error("missing location header", location)
} else if dor, _ := conn.GetDoorman(GetIdFromLocation(location)); dor == nil { } else if dor, _ := conn.GetDoorman(GetIdFromLocation(location)); dor == nil {
+21
View File
@@ -21,3 +21,24 @@ func GetDoorman(w http.ResponseWriter, r *http.Request) {
encoder.Encode(doorman) encoder.Encode(doorman)
} }
} }
func GetDoormanStatus(w http.ResponseWriter, r *http.Request) {
id := getDoormanIdFromRequest(w, r)
if id == "" {
Write404Error(w)
return
}
doorman, err := conn.GetDoorman(id)
if err != nil {
Write500Error(w, err)
} else if doorman == nil {
Write404Error(w)
}
if payload, err := doorman.AsDoormanUpdatePayload(); err != nil {
Write500Error(w, err)
} else {
w.Write(payload)
}
}
+23 -13
View File
@@ -4,18 +4,22 @@ import (
"encoding/json" "encoding/json"
"errors" "errors"
"fmt" "fmt"
"gopkg.in/mgo.v2/bson"
"math/big"
"strconv" "strconv"
"time" "time"
"github.com/didiercrunch/doorman/shared" "github.com/didiercrunch/doorman/shared"
"gopkg.in/mgo.v2/bson"
) )
const epsilon float64 = 0.0001
type DoormanValue struct { type DoormanValue struct {
Name string `json:"name" bson:"name"` Name string `json:"name"`
Probability float64 `json:"probability" bson:"probability"` Probability *big.Rat `json:"probability"`
}
type doormanValue struct {
Name string
Probability string
} }
type DoormanId struct { type DoormanId struct {
@@ -43,10 +47,15 @@ func NewDoormanDefinition(name string, ownerEmails ...string) *DoormanDefinition
// a way to quickly create doormen in tests. do not use in real code. will // a way to quickly create doormen in tests. do not use in real code. will
// panic if a doorman is wrongly specify // panic if a doorman is wrongly specify
func QuickNewDoormanDefinition(name string, probs ...float64) *DoormanDefinition { func QuickNewDoormanDefinition(name string, probs ...string) *DoormanDefinition {
ret := NewDoormanDefinition(name, "natasha@bigtits.com") ret := NewDoormanDefinition(name, "natasha@bigtits.com")
for i, prob := range probs { for i, prob := range probs {
ret.Values = append(ret.Values, &DoormanValue{"T" + strconv.Itoa(i), prob}) r := new(big.Rat)
_, err := fmt.Sscan(prob, r)
if err != nil {
panic(err)
}
ret.Values = append(ret.Values, &DoormanValue{"T" + strconv.Itoa(i), r})
} }
if err := ret.Validate("natasha@bigtits.com"); err != nil { if err := ret.Validate("natasha@bigtits.com"); err != nil {
panic(err) panic(err)
@@ -79,18 +88,19 @@ func (dmd *DoormanDefinition) AsWriteAccess(email string) bool {
} }
func (dmd *DoormanDefinition) ValidateDoormanValueProbabilities() error { func (dmd *DoormanDefinition) ValidateDoormanValueProbabilities() error {
var sum float64 = 0 sum, zero, one := big.NewRat(0, 1), big.NewRat(0, 1), big.NewRat(1, 1)
for _, w := range dmd.Values { for _, w := range dmd.Values {
if p := w.Probability; p < 0-epsilon || p > 1+epsilon { if p := w.Probability; p.Cmp(zero) < 0 || p.Cmp(one) > 0 {
return errors.New(fmt.Sprintf("doorman value %v is out of range", p)) return errors.New(fmt.Sprintf("doorman value %v is out of range", p))
} else { } else {
sum += p sum = new(big.Rat).Add(sum, p)
} }
} }
if sum < 1-epsilon || sum > 1+epsilon { if sum.Cmp(one) != 0 {
return errors.New("the sum of the probability must be 1.0") return errors.New("the sum of the probability must be 1")
} }
return nil return nil
} }
@@ -125,7 +135,7 @@ func (dmd *DoormanDefinition) CanBeUpdatedBy(dmd2 *DoormanDefinition) error {
func (dmd *DoormanDefinition) AsDoormanUpdatePayload() ([]byte, error) { func (dmd *DoormanDefinition) AsDoormanUpdatePayload() ([]byte, error) {
wu := &shared.DoormanUpdater{Id: dmd.Id.Hex()} wu := &shared.DoormanUpdater{Id: dmd.Id.Hex()}
wu.Timestamp = time.Now().Unix() wu.Timestamp = time.Now().Unix()
wu.Probabilities = make([]float64, len(dmd.Values)) wu.Probabilities = make([]*big.Rat, len(dmd.Values))
for i, value := range dmd.Values { for i, value := range dmd.Values {
wu.Probabilities[i] = value.Probability wu.Probabilities[i] = value.Probability
} }
+23 -19
View File
@@ -2,13 +2,17 @@ package doormen
import ( import (
"encoding/json" "encoding/json"
"math" "math/big"
"testing" "testing"
"github.com/didiercrunch/doorman/shared" "github.com/didiercrunch/doorman/shared"
"gopkg.in/mgo.v2/bson" "gopkg.in/mgo.v2/bson"
) )
var zero = big.NewRat(0, 1)
const epsilon float64 = 0.0001
func TestNewDoormanDefinition(t *testing.T) { func TestNewDoormanDefinition(t *testing.T) {
dmd := NewDoormanDefinition("some_name") dmd := NewDoormanDefinition("some_name")
if dmd.Id == "" { if dmd.Id == "" {
@@ -33,40 +37,40 @@ func TestValidate(t *testing.T) {
t.Error(err) t.Error(err)
} }
dmd = &DoormanDefinition{Name: "foo", Values: []*DoormanValue{&DoormanValue{"foo", 0}, &DoormanValue{"foo", 0}}} dmd = &DoormanDefinition{Name: "foo", Values: []*DoormanValue{&DoormanValue{"foo", zero}, &DoormanValue{"foo", zero}}}
if err := dmd.Validate("bob"); err.Error() != "doorman value names must be unique within a doorman" { if err := dmd.Validate("bob"); err.Error() != "doorman value names must be unique within a doorman" {
t.Error(err) t.Error(err)
} }
dmd = &DoormanDefinition{Name: "foo", Values: []*DoormanValue{&DoormanValue{"foo", 0}, &DoormanValue{"", 0}}} dmd = &DoormanDefinition{Name: "foo", Values: []*DoormanValue{&DoormanValue{"foo", zero}, &DoormanValue{"", zero}}}
if err := dmd.Validate("bob"); err.Error() != "doorman value cannot be empty" { if err := dmd.Validate("bob"); err.Error() != "doorman value cannot be empty" {
t.Error(err) t.Error(err)
} }
dmd = &DoormanDefinition{Name: "foo", Values: []*DoormanValue{&DoormanValue{"foo", -0.25}, &DoormanValue{"bar", 0.5}}} dmd = &DoormanDefinition{Name: "foo", Values: []*DoormanValue{&DoormanValue{"foo", big.NewRat(-1, 4)}, &DoormanValue{"bar", big.NewRat(1, 2)}}}
if err := dmd.Validate("bob"); err.Error() != "doorman value -0.25 is out of range" { if err := dmd.Validate("bob"); err.Error() != "doorman value -1/4 is out of range" {
t.Error(err) t.Error(err)
} }
dmd = &DoormanDefinition{Name: "foo", Values: []*DoormanValue{&DoormanValue{"foo", 2.5}, &DoormanValue{"bar", 0.5}}} dmd = &DoormanDefinition{Name: "foo", Values: []*DoormanValue{&DoormanValue{"foo", big.NewRat(5, 2)}, &DoormanValue{"bar", big.NewRat(1, 2)}}}
if err := dmd.Validate("bob"); err.Error() != "doorman value 2.5 is out of range" { if err := dmd.Validate("bob"); err.Error() != "doorman value 5/2 is out of range" {
t.Error(err) t.Error(err)
} }
dmd = &DoormanDefinition{Name: "foo", Values: []*DoormanValue{&DoormanValue{"foo", 0.75}, &DoormanValue{"bar", 0.5}}} dmd = &DoormanDefinition{Name: "foo", Values: []*DoormanValue{&DoormanValue{"foo", big.NewRat(3, 4)}, &DoormanValue{"bar", big.NewRat(1, 2)}}}
if err := dmd.Validate("bob"); err.Error() != "the sum of the probability must be 1.0" { if err := dmd.Validate("bob"); err.Error() != "the sum of the probability must be 1" {
t.Error(err) t.Error(err)
} }
dmd = &DoormanDefinition{Name: "foo", Values: []*DoormanValue{&DoormanValue{"foo", 0.2}, &DoormanValue{"bar", 0.2}}} dmd = &DoormanDefinition{Name: "foo", Values: []*DoormanValue{&DoormanValue{"foo", big.NewRat(1, 5)}, &DoormanValue{"bar", big.NewRat(1, 5)}}}
if err := dmd.Validate("bob"); err.Error() != "the sum of the probability must be 1.0" { if err := dmd.Validate("bob"); err.Error() != "the sum of the probability must be 1" {
t.Error(err) t.Error(err)
} }
dmd = &DoormanDefinition{Name: "foo", Values: []*DoormanValue{&DoormanValue{"foo", 0.8}, &DoormanValue{"bar", 0.2}}} dmd = &DoormanDefinition{Name: "foo", Values: []*DoormanValue{&DoormanValue{"foo", big.NewRat(4, 5)}, &DoormanValue{"bar", big.NewRat(1, 5)}}}
if err := dmd.Validate("bob"); err.Error() != "A doorman needs at least one owner specify as 'email'." { if err := dmd.Validate("bob"); err.Error() != "A doorman needs at least one owner specify as 'email'." {
t.Error(err) t.Error(err)
} }
dmd = &DoormanDefinition{Name: "foo", Values: []*DoormanValue{&DoormanValue{"foo", 0.8}, &DoormanValue{"bar", 0.2}}, OwnerEmails: []string{"alice"}} dmd = &DoormanDefinition{Name: "foo", Values: []*DoormanValue{&DoormanValue{"foo", big.NewRat(4, 5)}, &DoormanValue{"bar", big.NewRat(1, 5)}}, OwnerEmails: []string{"alice"}}
if err := dmd.Validate("bob"); err.Error() != "'bob' is not allow to edit this doorman." { if err := dmd.Validate("bob"); err.Error() != "'bob' is not allow to edit this doorman." {
t.Error(err) t.Error(err)
} }
@@ -91,7 +95,7 @@ func TestAsJson(t *testing.T) {
func TestAsDoormanUpdatePayload(t *testing.T) { func TestAsDoormanUpdatePayload(t *testing.T) {
wd := &DoormanDefinition{} wd := &DoormanDefinition{}
wd.Id = bson.NewObjectId() wd.Id = bson.NewObjectId()
wd.Values = []*DoormanValue{&DoormanValue{"C", 0.25}, &DoormanValue{"T1", 0.75}} wd.Values = []*DoormanValue{&DoormanValue{"C", big.NewRat(1, 4)}, &DoormanValue{"T1", big.NewRat(3, 4)}}
data, err := wd.AsDoormanUpdatePayload() data, err := wd.AsDoormanUpdatePayload()
if err != nil { if err != nil {
t.Error(err) t.Error(err)
@@ -106,9 +110,9 @@ func TestAsDoormanUpdatePayload(t *testing.T) {
} }
if len(wu.Probabilities) != 2 { if len(wu.Probabilities) != 2 {
t.Error("bad probability length") t.Error("bad probability length")
} else if math.Abs(wu.Probabilities[0]-0.25) > epsilon { } else if wu.Probabilities[0].Cmp(big.NewRat(1, 4)) != 0 {
t.Error("bad first probability") t.Error("bad first probability")
} else if math.Abs(wu.Probabilities[1]-0.75) > epsilon { } else if wu.Probabilities[1].Cmp(big.NewRat(3, 4)) != 0 {
t.Error("bad second probability") t.Error("bad second probability")
} else if wu.Timestamp == 0 { } else if wu.Timestamp == 0 {
t.Error("no time specify") t.Error("no time specify")
@@ -118,7 +122,7 @@ func TestAsDoormanUpdatePayload(t *testing.T) {
} }
func TestQuickNewDoormanDefinition(t *testing.T) { func TestQuickNewDoormanDefinition(t *testing.T) {
dmd := QuickNewDoormanDefinition("foo", 0.75, 0.25) dmd := QuickNewDoormanDefinition("foo", "0.75", "0.25")
if dmd.Name != "foo" { if dmd.Name != "foo" {
t.Error() t.Error()
} }
@@ -127,11 +131,11 @@ func TestQuickNewDoormanDefinition(t *testing.T) {
t.Error() t.Error()
} }
if dmd.Values[0].Name != "T0" || dmd.Values[0].Probability != 0.75 { if dmd.Values[0].Name != "T0" || dmd.Values[0].Probability.Cmp(big.NewRat(3, 4)) != 0 {
t.Error() t.Error()
} }
if dmd.Values[1].Name != "T1" || dmd.Values[1].Probability != 0.25 { if dmd.Values[1].Name != "T1" || dmd.Values[1].Probability.Cmp(big.NewRat(1, 4)) != 0 {
t.Error() t.Error()
} }
if len(dmd.OwnerEmails) != 1 && dmd.OwnerEmails[0] != "natasha@bigtits.com" { if len(dmd.OwnerEmails) != 1 && dmd.OwnerEmails[0] != "natasha@bigtits.com" {
+26
View File
@@ -0,0 +1,26 @@
package doormen
import (
"gopkg.in/mgo.v2/bson"
"math/big"
)
func (val *DoormanValue) GetBSON() (interface{}, error) {
ret := &doormanValue{val.Name, ""}
txt, err := val.Probability.MarshalText()
if err != nil {
return nil, err
}
ret.Probability = string(txt)
return ret, nil
}
func (val *DoormanValue) SetBSON(raw bson.Raw) error {
v := new(doormanValue)
if err := raw.Unmarshal(v); err != nil {
return err
}
val.Name = v.Name
val.Probability = new(big.Rat)
return val.Probability.UnmarshalText([]byte(v.Probability))
}
+38
View File
@@ -0,0 +1,38 @@
package doormen
import (
"encoding/json"
"gopkg.in/mgo.v2/bson"
"reflect"
"testing"
)
func TestJsonEncoding(t *testing.T) {
dmd := QuickNewDoormanDefinition("test json", "1/4", "3/4")
data, err := json.Marshal(dmd)
if err != nil {
t.Error(err)
}
dmd2 := new(DoormanDefinition)
if err = json.Unmarshal(data, dmd2); err != nil {
t.Error(err)
}
if !reflect.DeepEqual(dmd2, dmd) {
t.Error()
}
}
func TestBsonEncoding(t *testing.T) {
dmd := QuickNewDoormanDefinition("test bson", "1/4", "3/4")
data, err := bson.Marshal(dmd)
if err != nil {
t.Error(err)
}
dmd2 := new(DoormanDefinition)
if err = bson.Unmarshal(data, dmd2); err != nil {
t.Error(err)
}
if !reflect.DeepEqual(dmd2, dmd) {
t.Error()
}
}
@@ -8,7 +8,7 @@ import (
) )
func createMockDoorman(name string) *doormen.DoormanDefinition { func createMockDoorman(name string) *doormen.DoormanDefinition {
return doormen.QuickNewDoormanDefinition(name, 0.5, 0.5) return doormen.QuickNewDoormanDefinition(name, "0.5", "0.5")
} }
func TestNewInMemoryDBConnector(t *testing.T) { func TestNewInMemoryDBConnector(t *testing.T) {
+3
View File
@@ -0,0 +1,3 @@
{
"directory": "app/bower_components"
}
+2 -1
View File
@@ -22,6 +22,7 @@
"angularAMD": "~0.2.1", "angularAMD": "~0.2.1",
"angular-foundation": "~0.5.1", "angular-foundation": "~0.5.1",
"lodash": "~3.9.3", "lodash": "~3.9.3",
"font-awesome": "~4.3.0" "font-awesome": "~4.3.0",
"bourbon": "~4.2.3"
} }
} }
+21 -1
View File
@@ -1,9 +1,11 @@
define [ define [
"angularAMD" "angularAMD"
"lodash"
"angular-route" "angular-route"
"foundation" "foundation"
], (angularAMD) ->
], (angularAMD, _) ->
app = angular.module("webapp", ["ngRoute", "mm.foundation"]) app = angular.module("webapp", ["ngRoute", "mm.foundation"])
app.config ($routeProvider) -> app.config ($routeProvider) ->
$routeProvider.when("/", angularAMD.route( $routeProvider.when("/", angularAMD.route(
@@ -39,6 +41,24 @@ define [
document.getElementsByTagName("body")[0].style.visibility = "" document.getElementsByTagName("body")[0].style.visibility = ""
return return
app.controller "topbarCtrl", ["$scope", "$rootScope", "$location", ($scope, $rootScope, $location) ->
$scope.init = () ->
$scope.email = localStorage.getItem("email")
$scope.loggedIn = !!$scope.email
if !$scope.loggedIn
$location.path('/').replace()
$scope.logout = () ->
localStorage.setItem("email", "")
$scope.email = ""
$scope.loggedIn = false
$scope.$emit("logout")
$scope.init()
$rootScope.$on('login', $scope.init)
return
]
angularAMD.bootstrap(app) angularAMD.bootstrap(app)
+12 -2
View File
@@ -1,6 +1,8 @@
define ["app", "lodash"], (app, _) -> define ["app", "lodash", "directives/ratinput"], (app, _, ratinput) ->
app.controller "DoormanCtrl", ["$scope", "$http","$routeParams","$location", ($scope, $http, $routeParams, $location) -> app.controller "DoormanCtrl", ["$scope", "$http","$routeParams","$location", ($scope, $http, $routeParams, $location) ->
$scope.doorman = {values: []} $scope.doorman = {values: []}
$scope.somevalue = "2/6"
$scope.apiurl = "/api/doormen/#{$routeParams.id}" $scope.apiurl = "/api/doormen/#{$routeParams.id}"
$scope.user = localStorage.getItem("email") $scope.user = localStorage.getItem("email")
@@ -20,10 +22,18 @@ define ["app", "lodash"], (app, _) ->
$scope.initEmails = () -> $scope.initEmails = () ->
[$scope.leftEmailColumns, $scope.rightEmailColumns] = _.chunk($scope.doorman.emails, 2) [$scope.leftEmailColumns, $scope.rightEmailColumns] = _.chunk($scope.doorman.emails, 2)
$scope.rationalToFloat = (rat) ->
if _.contains(rat, "/")
num = Number(rat.substr(0, rat.indexOf("/")))
denum = Number(rat.substr(rat.indexOf("/") + 1))
return num / denum
Number(rat)
$scope.validate = ()-> $scope.validate = ()->
ret = 0 ret = 0
for val in $scope.doorman.values for val in $scope.doorman.values
ret += val.probability ret += $scope.rationalToFloat(val.probability)
return Math.abs(ret - 1.0) < 0.000001 return Math.abs(ret - 1.0) < 0.000001
$scope.addNewAuthorEmail = () -> $scope.addNewAuthorEmail = () ->
+7 -3
View File
@@ -1,11 +1,15 @@
define ["app"], (app) -> define ["app"], (app) ->
app.controller "HomeCtrl", ($scope) -> app.controller "HomeCtrl", ['$scope', '$rootScope', ($scope, $rootScope) ->
$scope.message = "message from home ctrl" $scope.init = () ->
$scope.loggedEmail = localStorage.getItem("email") $scope.loggedEmail = localStorage.getItem("email")
$scope.login = () -> $scope.login = () ->
if $scope.email if $scope.email
localStorage.setItem("email", $scope.email) localStorage.setItem("email", $scope.email)
$scope.loggedEmail = $scope.email $scope.loggedEmail = $scope.email
$scope.email = "" $scope.email = ""
$scope.$emit("login")
return $scope.init()
$rootScope.$on('logout', $scope.init)
]
+22 -12
View File
@@ -1,10 +1,10 @@
epsilon = 0.0001 epsilon = 0.0001
define ["app", "../filters"], (app, filters) -> define ["app", "lodash", "../filters", "directives/ratinput"], (app, _, filters, ratinput) ->
app.controller "NewDoormanCtrl", ["$scope", "$http", "$location", ($scope, $http, $location) -> app.controller "NewDoormanCtrl", ["$scope", "$http", "$location", "rationals", ($scope, $http, $location, rationals) ->
$scope.error = "" $scope.error = ""
$scope.user = localStorage.getItem("email") $scope.user = localStorage.getItem("email")
$scope.value = {probability: 0, name: ""} $scope.value = {probability: "1", name: ""}
$scope.payload = $scope.payload =
name: "" name: ""
values: [] values: []
@@ -17,27 +17,34 @@ define ["app", "../filters"], (app, filters) ->
if !$scope.isValidValue() if !$scope.isValidValue()
return return
$scope.payload.values.push($scope.value) $scope.payload.values.push($scope.value)
$scope.value = {name: "", probability: 0} $scope.value =
name: ""
probability: $scope.getProbabilityRemaining()
$scope.removeValue = (idx) -> $scope.removeValue = (idx) ->
$scope.payload.values.splice(idx, 1); $scope.payload.values.splice(idx, 1);
$scope.getProbabilityRemaining = () -> $scope.getProbabilityRemaining = () ->
ret = 0 sum = rationals.sum(_.pluck($scope.payload.values, "probability")...)
for v in $scope.payload.values return rationals.minus("1", sum)
ret += v.probability
return 1 - ret
$scope.redirectToDoorman = (doormanLocation) -> $scope.redirectToDoorman = (doormanLocation) ->
$http.get(doormanLocation).then (res) -> $http.get(doormanLocation).then (res) ->
id = res.data.id id = res.data.id
return $location.url("/doormen/#{id}") return $location.url("/doormen/#{id}")
$scope.getPayload = () ->
payload = _.cloneDeep($scope.payload)
for value in payload.values
value.probability = value.probability
return payload
$scope.createDoorman = () -> $scope.createDoorman = () ->
if not $scope.isDoormanValid() if not $scope.isDoormanValid()
return return
params = {params: {user: $scope.user}} params = {params: {user: $scope.user}}
$http.post("/api/doormen", $scope.payload, params). $http.post("/api/doormen", $scope.getPayload(), params).
then((res, s) -> then((res, s) ->
$scope.redirectToDoorman(res.headers("location")) $scope.redirectToDoorman(res.headers("location"))
).catch((res) -> ).catch((res) ->
@@ -49,9 +56,8 @@ define ["app", "../filters"], (app, filters) ->
return false return false
if $scope.payload.values.length < 2 if $scope.payload.values.length < 2
return false return false
if Math.abs($scope.getProbabilityRemaining()) > epsilon rationals.equal($scope.getProbabilityRemaining(), "0" )
return false
return true
$scope.addEmail = () -> $scope.addEmail = () ->
if not $scope.newEmail if not $scope.newEmail
@@ -64,6 +70,10 @@ define ["app", "../filters"], (app, filters) ->
return return
$scope.payload.emails.splice(idx, 1); $scope.payload.emails.splice(idx, 1);
$scope.getSum = () ->
probs = _.pluck($scope.payload.values, "probability")
return rationals.pp(rationals.sum(probs...))
return return
+64
View File
@@ -0,0 +1,64 @@
template = """
<div class="row rat-input">
<div class="row" ng-show="!edit" >
<div class="columns large-12" style="text-align: center;">
<span class="fraction-box" ng-click="enableEdit()">
<span ng-bind="nominator"></span> &frasl; <span ng-bind="denominator" ></span>
<i class="fa fa-pencil-square-o" ng-show="!ngDisabled"></i>
</span>
</div>
</div>
<div ng-show="edit" class="edit">
<form ng-submit="edit=false">
<div class="large-12 columns" style="text-align: center;">
<input ng-model="nominator" type="text" ng-change="changeModel()" ng-disabled="ngDisabled">
<span style="text-align: center;">&frasl;<span>
<input ng-model="denominator" type="text" ng-change="changeModel()" ng-disabled="ngDisabled">
<i class="fa fa-check" ng-click="edit=false"></i>
<input type="submit" style="display:none;">
</div>
</form>
</div>
</div>
"""
define ["app", "angularAMD", "lodash", "services/rationals"], (app, angularAMD, _, rationals) ->
angularAMD.directive "ratInput", () ->
directive =
restrict: 'E',
template: template
scope:
ngModel: "="
ngDisabled: "="
controller: [ '$scope', "rationals", ($scope, rationals) ->
$scope.getNominator = ()->
if _.contains($scope.ngModel, "/")
return Number($scope.ngModel.substr(0, $scope.ngModel.indexOf("/")))
Number($scope.ngModel)
$scope.getDenominator = () ->
if _.contains($scope.ngModel, "/")
return Number($scope.ngModel.substr($scope.ngModel.indexOf("/") + 1))
1
$scope.changeModel = ()->
$scope.ngModel = rationals.newRat($scope.nominator, $scope.denominator)
$scope.enableEdit = () ->
if not $scope.ngDisabled
$scope.edit = true
$scope.init = () ->
$scope.nominator = rationals.getNominator($scope.ngModel)
$scope.denominator = $scope.getDenominator()
$scope.$watch 'ngModel', () ->
$scope.init()
$scope.init()
]
return directive
+80
View File
@@ -0,0 +1,80 @@
define ["app", "angularAMD", "lodash"], (app, angularAMD, _) ->
app.service "rationals", () ->
service =
getNominator: (rat) ->
if _.contains(rat, "/")
return Number(rat.substr(0, rat.indexOf("/")))
Number(rat)
getDenominator: (rat) ->
if _.contains(rat, "/")
return Number(rat.substr(rat.indexOf("/") + 1))
1
getNomDenom: (rat) ->
return [service.getNominator(rat), service.getDenominator(rat)]
gcd: (a, b) ->
if b == 0
return a
return service.gcd(b, a % b)
lcm: (a, b) ->
return a * b / service.gcd(a, b)
changeDenominator: (rat, newDenom) ->
[nom, denom] = service.getNomDenom(rat)
return service.newRat( nom * newDenom / denom, newDenom)
add: (rat1, rat2) ->
[nom, denom] = service.getNomDenom(rat2)
[nNom, nDenom] = service.getNomDenom(rat1)
gcd = service.gcd(nDenom, denom)
rNom = nom*nDenom + nNom * denom
rDenom = denom * nDenom
return service.newRat(rNom/gcd , rDenom/gcd)
minus: (rat1, rat2) ->
return service.add(rat1, service.opposite(rat2))
reduce: (rat) ->
[nom, denom] = service.getNomDenom(rat)
gcd = service.gcd(nom, denom)
return service.newRat(nom/gcd, denom/gcd)
opposite: (rat) ->
[nom, denom] = service.getNomDenom(rat)
return service.newRat(-nom, denom)
sum: (rats...) ->
ret = "0/1"
for rat in rats
ret = service.add(ret, rat)
return service.reduce(ret)
equal: (rat1, rat2) ->
[nom1, denom1] = service.getNomDenom(rat1)
[nom2, denom2] = service.getNomDenom(rat2)
return nom1 * denom2 == nom2 * denom1
pp: (x) ->
[num, denum] = service.getNomDenom(service.reduce(x))
if denum == 1
return num
return "#{num}/#{denum}"
newRat: (nom , denom) ->
if nom * denom >= 0
return "#{Math.abs(nom)}/#{Math.abs(denom)}"
return "#{-Math.abs(nom)}/#{Math.abs(denom)}"
return service
app.filter "ratpp", ["rationals", (rationals) ->
filter = (n) ->
return rationals.pp(n)
]
+1 -1
View File
@@ -17,7 +17,7 @@ gulp.task('bower', function() {
gulp.task('sass', ['bower'], function () { gulp.task('sass', ['bower'], function () {
return gulp.src('./scss/*.scss') return gulp.src('./scss/*.scss')
.pipe(sass({includePaths: ['bower_components/foundation/scss']})) .pipe(sass({includePaths: ['bower_components/foundation/scss', 'bower_components/bourbon/app/assets/stylesheets']}))
.pipe(gulp.dest('./css')); .pipe(gulp.dest('./css'));
}); });
+13 -9
View File
@@ -9,35 +9,39 @@
</head> </head>
<body style="visibility:hidden;"> <body style="visibility:hidden;">
<div ng-controller="topbarCtrl">
<top-bar> <top-bar>
<ul class="title-area"> <ul class="title-area">
<li class="name"> <li class="name">
<h1><a href="#">Doorman</a></h1> <h1><a href="#/">Doorman</a></h1>
</li> </li>
<li toggle-top-bar class="menu-icon"><a href="#">Menu</a></li> <li toggle-top-bar class="menu-icon"><a >Menu</a></li>
</ul> </ul>
<top-bar-section> <top-bar-section>
<!-- Right Nav Section --> <ul class="left show-for-large-up">
<li>
<a ng-bind="email" href="#"></a>
</li>
</ul>
<ul class="right"> <ul class="right">
<li has-dropdown> <li has-dropdown>
<a href="#">Dropdown</a> <a >Dropdown</a>
<ul top-bar-dropdown> <ul top-bar-dropdown ng-show="loggedIn">
<li><a href="#/doormen">doormen</a></li> <li><a href="#/doormen">doormen</a></li>
<li><a href="#/newdoorman">new doorman</a></li> <li><a href="#/newdoorman">new doorman</a></li>
<li><a href="#/apispec">api specification</a></li> <li><a href="#/apispec">api specification</a></li>
<li><a href="#/serverspec">server specification</a></li> <li><a href="#/serverspec">server specification</a></li>
<li ng-click="logout()"><a href="#/">logout</a></li>
</ul> </ul>
</li> </li>
</ul> </ul>
</top-bar-section> </top-bar-section>
</top-bar> </top-bar>
</div>
<div class="large-12 small-12 columns" ng-view></div> <div class="large-12 small-12 columns" ng-view></div>
</body> </body>
</html> </html>
+4 -10
View File
@@ -12,19 +12,13 @@
</div> </div>
<div class="row" ng-repeat="value in doorman.values"> <div class="row" ng-repeat="value in doorman.values">
<div class="columns large-8 small-10"> <div class="columns large-7 small-10">
<label >name<input type="text" placeholder="value name" ng-value="value.name" disabled></label> <label >name<input type="text" placeholder="value name" ng-value="value.name" disabled></label>
</div> </div>
<div class="columns large-4 small-2"> <div class="columns large-4 small-2">
<label > <label >
probability probability
<input type="number" <rat-input ng-model="value.probability" ng-disabled="!isAdmin"></rat-input>
placeholder="value probability"
ng-model="value.probability"
ng-disabled="!isAdmin"
min="0"
max="1"
step="0.01"></label>
</div> </div>
</div> </div>
@@ -55,11 +49,11 @@
<label> <label>
new admin new admin
<div class="row collapse"> <div class="row collapse">
<div class="columns large-10"> <div class="columns large-10 small-10">
<input type="text" ng-model="newEmail"/> <input type="text" ng-model="newEmail"/>
</div> </div>
<div class="columns large-2"> <div class="columns large-2 small-2">
<a class="button postfix" ng-click="addNewAuthorEmail()" ng-class=" canAddNewEmail() ? '' : 'disabled' " >+</a> <a class="button postfix" ng-click="addNewAuthorEmail()" ng-class=" canAddNewEmail() ? '' : 'disabled' " >+</a>
</div> </div>
+3 -1
View File
@@ -7,6 +7,7 @@
<div class="row"> <div class="row">
<form ng-submit="login()" >
<fieldset> <fieldset>
<legend>Login</legend> <legend>Login</legend>
<div class="large-12 columns"> <div class="large-12 columns">
@@ -15,11 +16,12 @@
<input type="text" placeholder="email" ng-model="email"> <input type="text" placeholder="email" ng-model="email">
</div> </div>
<div class="small-2 columns"> <div class="small-2 columns">
<a class="button postfix" ng-click="login()">Login!</a> <input type="submit" class="button postfix" >Login!</input>
</div> </div>
</div> </div>
</div> </div>
</fieldset> </fieldset>
<form>
</div> </div>
<div class="row" ng-show="loggedEmail"> <div class="row" ng-show="loggedEmail">
+30 -21
View File
@@ -4,14 +4,13 @@
</div> </div>
</div> </div>
<form> <div class="row">
<div class="row">
<div class="columns large-12"> <div class="columns large-12">
<label >name<input type="text" placeholder="unique doorman name" ng-model="payload.name"></label> <label >name<input type="text" placeholder="unique doorman name" ng-model="payload.name"></label>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="columns large-12"> <div class="columns large-12">
<fieldset> <fieldset>
<legend>doorman state</legend> <legend>doorman state</legend>
@@ -21,12 +20,16 @@
<label >name<input type="text" placeholder="value name" ng-model="value.name"></label> <label >name<input type="text" placeholder="value name" ng-model="value.name"></label>
</div> </div>
<div class="columns large-3"> <div class="columns large-3">
<label >probability<input type="number" placeholder="value probability" ng-model="value.probability" min="0" max="1" step="0.01"></label> <label >probability
<rat-input ng-model="value.probability"></rat-input>
</label>
</div> </div>
<div class="columns large-1"> <div class="columns large-1">
<br> <br>
<a class="button round tiny" ng-class="{disabled: !isValidValue()}" ng-click="addNewValue()">+</a> <a class="button round tiny" ng-class="{disabled: !isValidValue()}" ng-click="addNewValue()">
<i class="fa fa-plus"></i>
</a>
</div> </div>
</div> </div>
@@ -35,28 +38,34 @@
<label >value<input type="text" placeholder="value name" ng-model="value.name"></label> <label >value<input type="text" placeholder="value name" ng-model="value.name"></label>
</div> </div>
<div class="columns large-3"> <div class="columns large-3">
<label >probability<input type="number" placeholder="value probability" ng-model="value.probability" min="0" max="1" step="0.01"></label> <label >probability
<rat-input ng-model="value.probability"></rat-input>
</label>
</div> </div>
<div class="columns large-1"> <div class="columns large-1">
<br> <br>
<a class="button round tiny" ng-click="removeValue( payload.values.length - $index - 1 )">-</a> <a class="button round tiny" ng-click="removeValue( payload.values.length - $index - 1 )">
<i class="fa fa-times"></i>
</a>
</div> </div>
</div> </div>
</fieldset> </fieldset>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="columns large-12"> <div class="columns large-12">
<fieldset> <fieldset>
<legend>administrator</legend> <legend>administrator</legend>
<div class="row"> <div class="row">
<div class="columns large-11"> <div class="columns large-11">
<label>admin email <input type="text" placeholder="wild cards are allow here" ng-model="newEmail"></label> <label>admin email <input type="text" placeholder="admin email" ng-model="newEmail"></label>
</div> </div>
<div class="columns large-1"> <div class="columns large-1">
<br><a class="button round tiny" ng-click="addEmail()" ng-class="{disabled: !newEmail}">+</a> <br><a class="button round tiny" ng-click="addEmail()" ng-class="{disabled: !newEmail}">
<i class="fa fa-plus"></i>
</a>
</div> </div>
</div> </div>
@@ -65,28 +74,28 @@
<label>admin email <input type="text" ng-disabled="email==user" placeholder="wild cards are allow here" ng-model="email"></label> <label>admin email <input type="text" ng-disabled="email==user" placeholder="wild cards are allow here" ng-model="email"></label>
</div> </div>
<div class="columns large-1"> <div class="columns large-1">
<br><a class="button round tiny" ng-click="removeEmail(payload.emails.length - $index -1)" ng-disabled="email==user? 'disabled' : ''">-</a> <br><a class="button round tiny" ng-click="removeEmail(payload.emails.length - $index -1)" ng-disabled="email==user? 'disabled' : ''">
<i class="fa fa-times"></i>
</a>
</div> </div>
</div> </div>
</fieldset> </fieldset>
</div> </div>
</div> </div>
<div class="row" ng-show="error"> <div class="row" ng-show="error">
<div class="columns large-12"> <div class="columns large-12">
<code style="width:100%; height:100%; display:block; margin-bottom: 1em;">{{ error }}</code> <code style="width:100%; height:100%; display:block; margin-bottom: 1em;">{{ error }}</code>
</div> </div>
</div> </div>
<div class="row"> <div class="row">
<div class="large-11 columns"> <div class="large-11 columns">
<button ng-click="createDoorman()" ng-class="{disabled: !isDoormanValid()}">create</button> <button ng-click="createDoorman()" ng-class="{disabled: !isDoormanValid()}">create</button>
</div> </div>
<div class="large-1 columns"> <div class="large-1 columns">
<span >propability remaning: {{ getProbabilityRemaining() }}</span> <span >propability remaning: {{ getProbabilityRemaining() | ratpp}}</span>
</div> </div>
</div> </div>
</form>
+35
View File
@@ -1,4 +1,6 @@
@import "settings"; @import "settings";
@import "bourbon";
// @import url(http://fonts.googleapis.com/css?family=Orbitron:400,900,500,700); // @import url(http://fonts.googleapis.com/css?family=Orbitron:400,900,500,700);
@font-face { @font-face {
font-family: 'Orbitron'; font-family: 'Orbitron';
@@ -26,3 +28,36 @@ h1, h2{
text-align: center; text-align: center;
} }
} }
.rat-input{
.fraction-box{
font-family: monospace;
font-size: 1.3em;
border: 2px;
padding: 0.3em;
box-shadow: 2px 2px 1px #888;
@include border-top-radius(5px);
@include border-bottom-radius(5px);
i{
font-size: 1em;
margin-left: 0.2em;
}
}
.fraction-box:hover{
border: 2px;
padding: 0.3em;
box-shadow: 2px 2px 1px red;
}
.edit{
input{
width: 4em;
height: 2em;
display: inline;
padding: 0px;
padding-left: 0.5em;
}
}
}