Skip to content

Commit 432f58e

Browse files
committed
readme and REST interface
1 parent 2ff1182 commit 432f58e

File tree

8 files changed

+161
-74
lines changed

8 files changed

+161
-74
lines changed

.applist

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
[kernel,stdlib,fs,crypto,compiler,syntax_tools,mnesia,ranch,gproc,cowlib,erlydtl,active,cowboy,n2o,markdown,kvs,mad,sh,uu]
1+
[kernel,stdlib,fs,crypto,compiler,syntax_tools,mnesia,ranch,gproc,cowlib,erlydtl,active,cowboy,n2o,markdown,kvs,mad,rest,sh,uu]

README.md

Lines changed: 66 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,14 @@
11
Ukraine United
22
==============
33

4-
Media in Ukraine and especially in Russia is often purposefully
5-
inflammatory and divisive. Public does not trust it, yet readily
6-
consumes the programming where people with opposing political
7-
opinion get demonized and stigmatized. In-depth analysis in
8-
English is rare and often incomplete. Overall, as the conflict
9-
is escalating and the emotions are running high, people are
10-
rapidly losing the ability to listen to each other.
11-
12-
Ukraine United has no precedent in the mainstream media,
13-
as it shows contemporary Ukraine from a unique perspective.
14-
It consists of interviews of people from every region of Ukraine,
15-
and from different social, religious, and political statuses.
16-
They speak about the Maidan revolution, the annexation of Crimea,
17-
the war in the Eastern Ukraine, and the possible resolution of this crisis.
18-
19-
Ukraine United is a platform where people can read each other's
20-
opinion and realize that they can disagree but still find common
21-
ground. We want everyone to use their own judgement about the
22-
situation in Ukraine instead of consuming unprofessional reporting
23-
and blunt propaganda.
4+
Features
5+
--------
6+
7+
* N2O DTL/SPA Application
8+
* Static HTML Generation
9+
* Static JSON Generation with REST transformation
10+
* Active Reloading
11+
* Built-in Web Server
2412

2513
Prerequisites
2614
-------------
@@ -37,11 +25,67 @@ Start
3725

3826
And open it in browser [http://localhost:8820](http://localhost:8820)
3927

28+
Usage
29+
-----
30+
31+
From Erlang:
32+
33+
```erlang
34+
> interview:get("2014-12.oleg-gubar.en").
35+
#interview{id = "2014-12.oleg-gubar.en",date = "2014-12",
36+
title = undefined,
37+
text = <<"Oleg Gubar\nOdessa\nHistorian of the City of Odessa\n\nIn general, I don’t concern myself with polit"/utf8...>>,
38+
author = "oleg-gubar"}
39+
40+
> uu:ls().
41+
[{"2014-07","andrey-volokita","en"},
42+
{"2014-07","arina-koltsova","en"},
43+
{"2014-12","oleg-gubar","en"}]
44+
45+
> uu:time().
46+
[{"2013-11-21",<<"Mustafa Nayem issued a call to">>},
47+
{"2013-11-22",<<"The number of people increases">>},
48+
{"2013-11-23",<<"To install the city New Year t">>},
49+
{"2013-11-24",<<"The tent camp is installed at ">>},
50+
{"2013-11-25",<<"The tent camp continues to exi">>},
51+
{"2013-11-26",<<"The association of two “maid"/utf8>>},
52+
{"2014-1-19",<<"About 100 – 500 of demon"/utf8...>>},
53+
{"2014-1-20",<<"The opposition on Hr"...>>},
54+
{"2014-1-22",<<"The activists Se"...>>},
55+
{"2014-1-10",<<"Prime Minist"...>>},
56+
{"2014-2-12",<<"Yanukovy"...>>},
57+
{"2014-2-17",<<"“Rig"/utf8...>>},
58+
{"2014-2-18",<<...>>},
59+
{[...],...},
60+
{...}|...]
61+
62+
> interview:get().
63+
[#interview{id = "2014-07.andrey-volokita.en",
64+
date = "2014-07",title = undefined,
65+
text = <<"Andrey Volokita\nKharkiv\nEnterpreneur\n\nThe views of Kharkovites [citizens of Kharkov, a city "...>>,
66+
author = "andrey-volokita"},
67+
#interview{id = "2014-07.arina-koltsova.en",
68+
date = "2014-07",title = undefined,
69+
text = <<"Arina Koltsova\nKiev\nHead of the “Samooborona” of Solomyansky district of Kiev\n\nI arrived"/utf8...>>,
70+
author = "arina-koltsova"},
71+
#interview{id = "2014-12.oleg-gubar.en",date = "2014-12",
72+
title = undefined,
73+
text = <<"Oleg Gubar\nOdessa\nHistorian of the City of Odessa\n\nIn general, I don’t concern mysel"/utf8...>>,
74+
author = "oleg-gubar"}]
75+
```
76+
77+
From REST clients:
78+
79+
```sh
80+
$ curl -I -X GET http://localhost:8820/json/en/2014-07.andrey-volokita.json
81+
$ curl -I -X GET http://localhost:8820/rest/interview/2014-12.oleg-gubar.en
82+
$ curl -I -X GET http://localhost:8820/static/interviews/2014-12.oleg-gubar.en.txt
83+
$ curl -I -X GET http://localhost:8820/static/timeline/2013-11/21-en.txt
84+
```
85+
4086
Credits
4187
-------
4288

4389
* Dima Gavrysh
4490
* Yurii Artyukh
4591
* Maxim Sokhatsky
46-
47-

priv/templates/article.dtl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
<div class=app>
1515
<div align=right>FROM: {{from}}<br>
1616
PROFESSION: {{to}}<br>
17+
JSON: {{json}}<br>
1718
DATE: {{date}}</div>
1819
{{image}}
1920
{{body}}

rebar.config

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
{deps_dir, "deps"}.
22
{deps, [
33
{active, ".*", {git, "git://github.com/synrc/active", "HEAD"}},
4+
{rest, ".*", {git, "git://github.com/synrc/rest", "HEAD"}},
45
{kvs, ".*", {git, "git://github.com/synrc/kvs", "HEAD"}},
56
{erlmarkdown,".*",{git,"git://github.com/erlware/erlmarkdown", "HEAD"}},
67
{n2o, ".*", {git, "git://github.com/5HT/n2o", "HEAD"}}

src/pages/article.erl

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -4,26 +4,33 @@
44
-include("uu.hrl").
55

66
main() ->
7-
Author = wf:to_list(wf:q(author)),
8-
Date = wf:to_list(wf:q(date)),
7+
Author = wf:to_list(wf:q(author)),
8+
Date = wf:to_list(wf:q(date)),
99
Language = wf:to_list(wf:q(locale)),
1010
generate(Author,Date,wf:atom([Language])).
1111

1212
generate(Author,Date,Language) ->
13-
%io:format("Article Generate: ~p~n",[{Author,Date,Language}]),
14-
[Name,Surname] = string:tokens(Author,"-"),
15-
{_,#user{name=FullName,city=City,profession=Profession,photo=Photo}} = uu_people:lookup({Author,Language}),
16-
{ok,Body} = file:read_file(lists:concat(["priv/static/interviews/",Date,".",Author,".",Language,".txt"])),
17-
#dtl{file=article,app=uu,bindings=[{body,markdown:conv_utf8(binary_to_list(Body))},
18-
{date,Date},
19-
{from,FullName},
20-
{to,Profession},
21-
{image,image(Author,Date)}]}.
13+
%io:format("Article Generate: ~p~n",[{Author,Date,Language}]),
14+
[Name,Surname] = string:tokens(Author,"-"),
15+
{_,#user{name=FullName,city=City,profession=Profession,photo=Photo}}
16+
= uu_people:lookup({Author,Language}),
2217

23-
image(Author,Date) -> [#br{},#image{src=lists:concat(["/static/people/",Author,"/image.jpg"]),width="100%"}].
18+
{ok,Body} = file:read_file(lists:concat(["priv/static/interviews/",Date,".",Author,".",Language,".txt"])),
19+
JSON = lists:concat(["/json/",Language,"/",Date,".",Author,".json"]),
20+
#dtl{file=article,
21+
app=uu,
22+
bindings=[{body,markdown:conv_utf8(binary_to_list(Body))},
23+
{date,Date},
24+
{json,wf:render(#link{body=JSON,href=JSON})},
25+
{from,FullName},
26+
{to,Profession},
27+
{image,image(Author,Date)}]}.
28+
29+
image(Author,Date) ->
30+
[#br{},
31+
#image{src=lists:concat(["/static/people/",Author,"/image.jpg"]),width="100%"}].
2432

2533
event(init) ->
26-
wf:update(body, #label{body= <<"Україна"/utf8>>} ),
2734
wf:info(?MODULE,"Init",[]);
2835

2936
event(_) -> ok.

src/pages/index.erl

Lines changed: 35 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -4,33 +4,48 @@
44
-include("uu.hrl").
55

66
lang() -> string:to_lower(wf:to_list([wf:lang()])).
7-
main() -> #dtl{file=index,app=uu,bindings=[{news,fetch(news)},
8-
{body,body()},
9-
{interviews,fetch(interviews)}]}.
7+
main() -> #dtl{file=index,
8+
app=uu,
9+
bindings=[{news,fetch(news,lang(),fun update_file/1)},
10+
{body,body()},
11+
{interviews,fetch(interviews,lang(),fun update_file/1)}]}.
1012

11-
body() ->
12-
{_,#user{name=FullName,city=City,profession=Profession,photo=Photo}} =
13-
uu_people:lookup({"dima-gavrysh",wf:to_atom([lang()])}),
14-
[ #image{src=Photo},#h2{body=FullName},#panel{body=Profession} ].
13+
body() -> {_,#user{name=FullName,city=City,profession=Profession,photo=Photo}}
14+
= uu_people:lookup({"dima-gavrysh",wf:to_atom([lang()])}),
15+
[ #image{src=Photo},#h2{body=FullName},#panel{body=Profession} ].
1516

16-
17-
fetch(Name) ->
18-
Lang = lang(),
17+
fetch(Name, Lang, UpdateFile) ->
1918
List = lists:flatten([ begin
2019
case string:tokens(F,".") of
21-
[D,A,L,"txt"] when L == Lang -> update_file([D,A,L]);
22-
_ -> [] end
23-
end || F <- filelib:wildcard(lists:concat(["priv/static/",Name,"/*.txt"])) ]).
20+
[D,A,L,"txt"] when L == Lang -> UpdateFile([D,A,L]);
21+
_ -> [] end end
22+
|| F <- filelib:wildcard(lists:concat(["priv/static/",Name,"/*.txt"])) ]).
2423

2524
update_file([D,A,L]) ->
26-
X=string:tokens(D,"/"),
2725
Language = wf:atom([L]),
28-
File = lists:concat([string:join(tl(X),"/"),".",A,".",L,".htm"]),
29-
Article = article:generate(A,lists:last(X),Language),
26+
X=string:tokens(D,"/"),
27+
Date = lists:last(X),
28+
29+
JSON = lists:concat(["json/",L,"/",Date,".",A,".json"]),
30+
HTML = lists:concat(["static/interviews/",L,"/",Date,".",A,".htm"]),
31+
Text = lists:concat(["static/interviews/",Date,".",A,".",L,".txt"]),
32+
33+
io:format("JSON: ~p~n",[JSON]),
34+
io:format("HTML: ~p~n",[HTML]),
35+
io:format("Text: ~p~n",[Text]),
36+
37+
{ok,Bin} = file:read_file(hd(X)++"/"++Text),
38+
Article = article:generate(A,Date,Language),
3039
Render = wf:render(Article),
31-
file:write_file(lists:concat([hd(X),"/",File]),Render),
32-
io:format("HTM updated: ~p~n",[lists:concat([hd(X),"/",File])]),
33-
article_link({lists:last(X),A,Language,File}).
40+
41+
file:write_file(lists:concat([hd(X),"/",HTML]),Render),
42+
file:write_file(lists:concat([hd(X),"/",JSON]),
43+
iolist_to_binary(
44+
n2o_json:encode(
45+
interview:to_json(#interview{id=string:join([Date,A,L],"."),date=Date,text=Bin,author=A})))),
46+
47+
io:format("HTM updated: ~p~n",[lists:concat([hd(X),"/",HTML])]),
48+
article_link({lists:last(X),A,Language,HTML}).
3449

3550
article_link({Date,Author,Language,File}) ->
3651
case uu_people:lookup({Author,Language}) of
@@ -40,8 +55,5 @@ article_link({Date,Author,Language,File}) ->
4055
href=File}]);
4156
_ -> [] end.
4257

43-
event(init) ->
44-
wf:update(body, #label{body= <<"Україна"/utf8>>} ),
45-
wf:info(?MODULE,"Init",[]);
46-
58+
event(init) -> wf:info(?MODULE,"Init",[]);
4759
event(_) -> ok.

src/uu.erl

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
-module(uu).
22
-export([active/1]).
3+
-include("uu.hrl").
34
-compile(export_all).
45

6+
languages() -> [en,ua,ru].
7+
58
% Active File System Update
69

7-
active(["deps"|_]) -> ok;
10+
%active(["deps"|_]) -> ok;
811
active(File) ->
912
Last = lists:last(File),
1013
Tokens = string:tokens(Last,"."),
@@ -22,15 +25,27 @@ active(File) ->
2225
{_,_} -> wf:info(?MODULE,"Active Ignore: ~p~n",[File]),
2326
active:otp(File) end.
2427

25-
online() -> ets:select(gproc,fun2ms_all()).
26-
online_count() -> ets:select_count(gproc,fun2ms_count()).
27-
fun2ms_all() -> ets:fun2ms(fun({{{_,_,broadcast},_},D,E}) -> {D,E} end).
28-
fun2ms_count() -> ets:fun2ms(fun({{{_,_,broadcast},_},D,E}) -> true end).
28+
% ls - list interviews
29+
% timeline(ua) -- show timeline event in UA locale
30+
31+
ls() -> ls(en).
32+
time() -> time(en).
33+
ls(Lang) -> index:fetch(interviews,wf:to_list(Lang),fun([A,B,C]) -> {lists:last(string:tokens(A,"/")),B,C} end).
34+
time(Lang) ->
35+
[{ lists:concat([Y,"-",M,"-",D]),
36+
case unicode:characters_to_binary(
37+
binary:part(Text,{0,erlang:min(size(Text),
38+
case Lang of en -> 30; _ -> 55 end)})) of
39+
{incomplete,B,_} -> B;
40+
E -> E end }
41+
|| {{{Y,M,D},L},Text} <- uu_timeline:all(), L == Lang ].
2942

30-
log_modules() -> [uu,uu_timeline,index,article,n2o_websocket].
43+
log_modules() -> [uu,uu_timeline,index,article,interview,n2o_websocket].
3144
main(A) -> mad_repl:main(A,[]).
3245
main() -> [].
3346
event(_) -> ok.
3447

35-
timeline(Lang) -> [ X || {{_,L},_}=X <- uu_timeline:all(), L == Lang ].
36-
48+
online() -> ets:select(gproc,fun2ms_all()).
49+
online_count() -> ets:select_count(gproc,fun2ms_count()).
50+
fun2ms_all() -> ets:fun2ms(fun({{{_,_,broadcast},_},D,E}) -> {D,E} end).
51+
fun2ms_count() -> ets:fun2ms(fun({{{_,_,broadcast},_},D,E}) -> true end).

src/uu_sup.erl

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,31 @@ start_link() -> supervisor:start_link({local, ?MODULE}, ?MODULE, []).
77
cron(Key) -> {Key,{cron,start_link,[Key]},permanent,2000,worker,[cron]}.
88

99
init([]) ->
10-
11-
wf:info(?MODULE,"~nUnitedUkraine.org WebSocket Server~n",[]),
10+
wf:info(?MODULE,"~nN2O WebSocket Server~n",[]),
1211

1312
{ok, _} = cowboy:start_http(http, 100, [{port, wf:config(n2o,port)}],
1413
[{env, [{dispatch, dispatch_rules()}]}]),
1514

1615
kvs:join(),
16+
17+
[ begin filelib:ensure_dir(lists:concat(["priv/static/interviews/",Lang,"/"])),
18+
filelib:ensure_dir(lists:concat(["priv/json/",Lang,"/"]))
19+
end || Lang <- uu:languages() ],
20+
1721
[ ets:insert(globals,X) || X <- uu_people:all() ],
1822
[ ets:insert(globals,X) || X <- uu_timeline:all() ],
23+
1924
{ok, {{one_for_one, 5, 10}, []}}.
2025

2126
mime() -> [{mimetypes,cow_mimetypes,all}].
2227

2328
dispatch_rules() ->
2429
cowboy_router:compile(
2530
[{'_', [
26-
{"/static/[...]", n2o_dynalo, {dir, "priv/static", mime()}},
27-
{"/n2o/[...]", n2o_dynalo, {dir, "deps/n2o/priv", mime()}},
28-
{"/ws/[...]", bullet_handler, [{handler, n2o_bullet}]},
29-
{'_', n2o_cowboy, []}
30-
]}]).
31+
{"/static/[...]", n2o_dynalo, {dir, "priv/static", mime()}},
32+
{"/json/[...]", n2o_dynalo, {dir, "priv/json", mime()}},
33+
{"/n2o/[...]", n2o_dynalo, {dir, "deps/n2o/priv", mime()}},
34+
{"/rest/:resource", rest_cowboy, []},
35+
{"/rest/:resource/:id", rest_cowboy, []},
36+
{"/ws/[...]", bullet_handler, [{handler, n2o_bullet}]},
37+
{'_', n2o_cowboy, []}]}]).

0 commit comments

Comments
 (0)