Merge branch 'master' of git://github.com/dpavlin/angular-mojolicious
authorDobrica Pavlinusic <dpavlin@rot13.org>
Mon, 3 Oct 2011 17:16:25 +0000 (19:16 +0200)
committerDobrica Pavlinusic <dpavlin@rot13.org>
Mon, 3 Oct 2011 17:16:25 +0000 (19:16 +0200)
README
angular-replicate.pl
angular-server.pl
common/mojo
couchdb-changes.pl
couchdb-trigger.pl
public/app/reservations/calendar.html [new file with mode: 0644]
run.sh

diff --git a/README b/README
index 4dd9f76..c0fd38f 100644 (file)
--- a/README
+++ b/README
@@ -77,7 +77,7 @@ Angular examples available:
 
 CouchDB examples:
 
-       couchdb-changes.pl - simple _changes feed watcher using Mojo::Client documented at
+       couchdb-changes.pl - simple _changes feed watcher using Mojo::UserAgent documented at
 
                http://wiki.apache.org/couchdb/HTTP_database_API#Changes
 
index ad8c807..2010754 100755 (executable)
@@ -2,7 +2,7 @@
 use warnings;
 use strict;
 
-use Mojo::Client;
+use Mojo::UserAgent;
 use Data::Dump qw(dump);
 
 use lib 'common/mojo/lib';
@@ -12,7 +12,7 @@ my ( $from, $to ) = @ARGV;
 die "usage: $0 http://from/data/database/ http://to/data/database/\n"
 unless $from && $to;
 
-my $client = Mojo::Client->new;
+my $client = Mojo::UserAgent->new;
 
 my $got = $client->get( $from )->res->json;
 warn "# from $from ",dump($got);
index 57e40a4..1ef3778 100755 (executable)
@@ -6,6 +6,7 @@ use Mojolicious::Lite;
 use Data::Dump qw(dump);
 use Time::HiRes;
 use Clone qw(clone);
+use Mojo::UserAgent;
 
 sub new_uuid { Time::HiRes::time * 100000 }
 
@@ -13,8 +14,8 @@ sub new_uuid { Time::HiRes::time * 100000 }
 # http://docs.getangular.com/REST.Basic
 # http://angular.getangular.com/data
 
-my $couchdb = 'http://localhost:5984';
-my $client = Mojo::Client->new;
+my $couchdb = $ENV{COUCHDB} || 'http://localhost:5984';
+my $client = Mojo::UserAgent->new;
 
 sub _couchdb_put {
        my ( $url, $data ) = @_;
@@ -26,20 +27,7 @@ sub _couchdb_put {
        my $rev;
 
        warn "# _couchdb_put $url = $json";
-       $client->put( "$couchdb/$url" => $json => sub {
-               my ($client,$tx) = @_;
-               my ($message, $code) = $tx->error;
-               my $response = $tx->res->json;
-               warn "## response $code ",dump($response);
-               if ($tx->error) {
-                       warn "ERROR $code $message";
-               }
-               return
-               $rev = $response->{rev};
-       })->process;
-
-       warn "## rev = $rev";
-       return $rev;
+       return $client->put( "$couchdb/$url" => $json)->res->json;
 }
 
 sub _couchdb_get {
@@ -142,8 +130,14 @@ any [ 'post' ] => '/data/:database/:entity' => sub {
 
        $json->{'$id'} ||= $id; # make sure $id is in there
 
-       my $rev = _couchdb_put "/$database/$entity.$id" => $json;
-       $json->{_rev} = $rev;
+       my $new = _couchdb_put "/$database/$entity.$id" => $json;
+       warn "new: ",dump($new);
+       if ( $new->{ok} ) {
+               $json->{'_'.$_} = $new->{$_} foreach ( 'rev','id' );
+       } else {
+               warn "ERROR: ",dump($new);
+               $json->{error} = $new;
+       }
 
        _render_jsonp( $self,  $json );
 };
@@ -230,6 +224,95 @@ get '/json/:database/:entity' => sub {
        _render_jsonp( $self, $docs )
 };
 
+# app/resevations
+use Encode;
+use iCal::Parser;
+
+plugin 'proxy';
+
+get '/reservations/get/(*url)' => sub {
+       my $self = shift;
+
+       my $text = $client->get( 'http://' . $self->param('url') )->res->body;
+       warn "# get ", $self->param('url'), dump($text);
+
+       $text = decode( 'utf-8', $text );
+       $text =~ s{\\,}{,}gs;
+       $text =~ s{\\n}{ }gs;
+
+       my $c = iCal::Parser->new->parse_strings( $text );
+
+#      warn "# iCal::Parser = ",dump($c);
+
+       my $ical = {
+               cal => $c->{cals}->[0], # FIXME assume single calendar
+       };
+
+       my $e = $c->{events};
+       my @events;
+
+       foreach my $yyyy ( sort keys %$e ) {
+               foreach my $mm ( sort keys %{ $e->{$yyyy} } ) {
+                       foreach my $dd ( sort keys %{ $e->{$yyyy}->{$mm} } ) {
+                               push @events, values %{ $e->{$yyyy}->{$mm}->{$dd} };
+                       }
+               }
+       }
+
+       @events = map {
+               foreach my $check_slot ( qw(
+                       DESCRIPTION
+                       LOCATION
+                       STATUS
+                       SUMMARY
+               )) {
+                       next unless exists $_->{$check_slot};
+                       $_->{slots} = $1 if $_->{$check_slot} =~ m/(\d+)\s*mjesta/s;
+               }
+               $_;
+       } @events;
+
+       $ical->{events} = [ sort {
+                                       $a->{DTSTART} cmp $b->{DTSTART}
+       } @events ];
+
+       _render_jsonp( $self, $ical );
+};
+
+get '/reservations/events/:view_name' => sub {
+       my $self = shift;
+
+       my $view = _couchdb_get('/reservations/_design/events/_view/' . $self->param('view_name') . '?group=true');
+       my $hash;
+
+       if ( exists $view->{error} ) {
+               _couchdb_put "/reservations/_design/events", {
+                       _id => '_design/events',
+                       language => 'javascript',
+                       views => {
+                               submited => {
+                                       map    => q|
+                                               function(doc) {
+                                                       if ( doc.event && doc.event.UID ) emit(doc.event.UID, 1)
+                                               }
+                                       |,
+                                       reduce => q|_sum|,
+                               }
+                       }
+               };
+       }
+
+       _render_jsonp( $self, {} ) unless ref $view->{rows} eq 'ARRAY';
+
+       foreach my $row ( @{ $view->{rows} } ) {
+               $hash->{ $row->{key} } = $row->{value};
+       }
+
+       _render_jsonp( $self, $hash );
+};
+
+get '/_utils/script/(*url)' => sub { $_[0]->proxy_to( "$couchdb/_utils/script/" . $_[0]->param('url') , with_query_params => 1 ) };
+
 app->start;
 __DATA__
 
index c6b96c8..a777d8d 160000 (submodule)
@@ -1 +1 @@
-Subproject commit c6b96c83cb1904ce1894e18d3bf553df651817bd
+Subproject commit a777d8d1952922f4f0289c892bbeb3808d000958
index 41fcc8b..3cb1637 100755 (executable)
@@ -7,13 +7,13 @@ use strict;
 
 use lib 'common/mojo/lib';
 
-use Mojo::Client;
+use Mojo::UserAgent;
 use Mojo::JSON;
 
 my $url = 'http://localhost:5984/monitor/_changes?feed=continuous;include_docs=true;since=';
 my $seq = 0;
 
-my $client = Mojo::Client->new;
+my $client = Mojo::UserAgent->new;
 my $json   = Mojo::JSON->new;
 my $error;
 
index 1724b67..2ec5705 100755 (executable)
@@ -15,7 +15,7 @@ use strict;
 
 use lib 'common/mojo/lib';
 
-use Mojo::Client;
+use Mojo::UserAgent;
 use Mojo::JSON;
 use Time::HiRes qw(time);
 use Data::Dump qw(dump);
@@ -32,7 +32,7 @@ require $trigger_path if -e $trigger_path;
 
 my $seq = 0;
 
-my $client = Mojo::Client->new;
+my $client = Mojo::UserAgent->new;
 our $json   = Mojo::JSON->new;
 sub info { warn $_[0], " ",$json->encode($_[1]),$/ }
 sub debug { info "# $_[0]", $_[1] }
diff --git a/public/app/reservations/calendar.html b/public/app/reservations/calendar.html
new file mode 100644 (file)
index 0000000..f947f21
--- /dev/null
@@ -0,0 +1,142 @@
+<!DOCTYPE HTML>
+<html xmlns:ng="http://angularjs.org">
+<head>
+<meta charset="utf-8">
+<!--
+<script src="angular.js" ng:autobind></script>
+-->
+<script src="http://code.angularjs.org/0.9.15/angular-0.9.15.min.js"
+   ng:autobind></script>
+
+<script>
+Calendar.$inject = ['$xhr', '$resource']; 
+
+function Calendar($xhr,$resource){ 
+       console.info('Calendar');
+       var self = this;
+       $xhr("GET"
+               , "/reservations/get/www.google.com/calendar/ical/8tg8ecg285qshtp75813jktqa0%40group.calendar.google.com/private-b9d68b530fde2c6060979f8a05aa0865/basic.ics"
+               , function(code, response){ 
+                       console.log('xhr calendar', code, response);
+                       self.data = response;
+               }
+       );
+
+       var Reservation = $resource('/data/reservations/prijava/:id');
+       this.reservation = new Reservation();
+
+       this.load_submited = function() {
+               $xhr("GET"
+                       , "/reservations/events/submited"
+                       , function(code, response){ 
+                               console.log('xhr submited', code, response);
+                               self.submited = response;
+                       }
+               );
+       }
+       this.load_submited();
+
+       self.reservation.status = 'event';
+
+       this.select_event = function(c) {
+               console.info('select_event', c);
+               self.reservation.event = c;
+               self.reservation.status = 'changed';
+               if (self.submited) self.reservation.slot_nr = ++self.submited[c.UID];
+       }
+
+       this.change_event = function() {
+               self.reservation.event = null;
+               self.reservation.status = 'event';
+               self.load_submited();
+       }
+
+       this.save_submission = function() {
+               if ( self.reservation && self.reservation.event ) {
+                       console.info('submit save');
+                       self.reservation.status = 'saved';
+                       self.reservation.$save();
+                       self.load_submited();
+               } else {
+                       console.info('submit ignored');
+               }
+       }
+
+} 
+
+// http://groups.google.com/group/angular/browse_thread/thread/af68afb22fd2d2ab/18fc5e3216a77e53?show_docid=18fc5e3216a77e53
+</script>
+
+<title>Reservations</title>
+
+<style>
+</style>
+
+</head>
+<body>
+
+<div ng:controller="Calendar"> 
+
+<h1>{{data.cal['X-WR-CALNAME']}}</h1>
+
+<div>{{data.cal['X-WR-CALDESC'] | html}}</div>
+
+<ul>
+<li ng:repeat="c in data.events" ng:show="reservation.status == 'event'">
+{{c.DTSTART}} - {{c.DTEND}}
+<a href="" ng:click="select_event(c)" ng:show="! reservation.event && c.UID != reservation.event.UID">{{c.SUMMARY}}</a>
+<b ng:show="reservation.event && reservation.event.UID == c.UID">{{c.SUMMARY}}</b>
+<em ng:show="submited[c.UID]">
+broj prijava: {{submited[c.UID]}}
+</em>
+<em ng:show="c.slots">/{{c.slots}}</em>
+<div>{{c.LOCATION}}</div>
+<div ng:show="c.DESCRIPTION">
+{{c.DESCRIPTION}}
+</div>
+</li>
+</ul>
+
+<div ng:show="reservation.error" style="background:#f88">{{reservation.error}}</div>
+
+<form ng:show="reservation.status != 'event'">
+
+<div ng:show="reservation.event" style="background: #ff8">
+Vaša <b>{{reservation.status}}</b> prijava za
+<b>{{reservation.event.SUMMARY}}</b>
+u terminu {{reservation.event.DTSTART}} u trajanju od {{reservation.event.hours}} sata
+<span ng:show="reservation.slot_nr">broj {{reservation.slot_nr}}</span>
+<span ng:show="reservation.saved">je spremljena.</span>
+<input type=button value="Potvrdi promjenu" ng:show="reservation.changed" ng:click="save_submission()">
+
+<a href="" ng:click="change_event()">Promjeni termin</a>
+</div>
+
+
+ime: <input name="reservation.name" ng:required>
+<br>
+prezime: <input name="reservation.surname" ng:required>
+<br>
+e-mail: <input name="reservation.email" ng:required>
+<br>
+odsjek: <input name="reservation.odsjek" ng:required>
+<br>
+zvanje: <input name="reservation.zvanje">
+<br>
+područke/tema zaninimanja: <input name="reservation.porducje">
+<br>
+<input type=button ng:click="save_submission()" value="Prijavi me" ng:disable="$invalidWidgets.visible()">
+</form>
+
+<input name=debug type=checkbox>
+<pre ng:show="debug">
+reservation={{reservation}}
+submited={{submited}}
+data={{data}}
+</pre>
+
+</div>
+
+
+</body>
+</html>
diff --git a/run.sh b/run.sh
index f7e54c2..2800ea5 100755 (executable)
--- a/run.sh
+++ b/run.sh
@@ -7,5 +7,5 @@ if [ ! -z "$1" ] ; then
        test -f public/angular/build/angular.js || ( cd public/angular && rake compile )
 fi
 
-./angular-server.pl daemon --reload --listen 'http://*:3001'
+morbo ./angular-server.pl --listen 'http://*:3001'