Continuing work on the API.
The id_counter module now includes the record directly in the source. Fixed many typos and small syntax errors. Added ts_api:dispatch_user/2 to handle different HTTP methods. Added method placeholder stubs to ts_api. Implemented ts_api:list_entries/3. Added ts_user record and ts_user module. Implemented ts_entry:list/4, the more generic guts of the other list functions. Created ts_entry:list_asc/3 and ts_entry:list_desc/3. Fixed ts_json:encode_datetime/1.
This commit is contained in:
parent
098cd4cbb9
commit
6fe9184c8e
BIN
db/test/DECISION_TAB.LOG
Normal file
BIN
db/test/DECISION_TAB.LOG
Normal file
Binary file not shown.
BIN
db/test/LATEST.LOG
Normal file
BIN
db/test/LATEST.LOG
Normal file
Binary file not shown.
1
db/test/id_counter.DCD
Normal file
1
db/test/id_counter.DCD
Normal file
@ -0,0 +1 @@
|
|||||||
|
cXM
|
BIN
db/test/schema.DAT
Normal file
BIN
db/test/schema.DAT
Normal file
Binary file not shown.
1
db/test/ts_entry.DCD
Normal file
1
db/test/ts_entry.DCD
Normal file
@ -0,0 +1 @@
|
|||||||
|
cXM
|
BIN
db/test/ts_timeline.DCD
Normal file
BIN
db/test/ts_timeline.DCD
Normal file
Binary file not shown.
@ -1,7 +1,7 @@
|
|||||||
-module(id_counter).
|
-module(id_counter).
|
||||||
-export([create_table/1, next_counter/1, dirty_next_counter/1]).
|
-export([create_table/1, next_counter/1, dirty_next_counter/1]).
|
||||||
|
|
||||||
-include("vbs_db_records.hrl").
|
-record(id_counter, {name, next_value = 0}).
|
||||||
|
|
||||||
%% Create the table structure for Mnesia
|
%% Create the table structure for Mnesia
|
||||||
create_table(Opts) ->
|
create_table(Opts) ->
|
||||||
|
@ -1,17 +1,19 @@
|
|||||||
-module(ts_api).
|
-module(ts_api).
|
||||||
-compile(export_all).
|
-compile(export_all).
|
||||||
|
|
||||||
-include("ts_db_records/hrl").
|
-include("ts_db_records.hrl").
|
||||||
-include("yaws_api.hrl").
|
-include("yaws_api.hrl").
|
||||||
|
|
||||||
out(YArg) ->
|
out(YArg) ->
|
||||||
%get the app mod data
|
%get the app mod data
|
||||||
PathString = YArg#arg.apmoddata,
|
PathString = YArg#arg.appmoddata,
|
||||||
|
|
||||||
% split the path
|
% split the path
|
||||||
PathElements = cast PathString of
|
PathElements = case PathString of
|
||||||
undefined -> []; %handle no end slash: /ts_api
|
undefined -> []; %handle no end slash: /ts_api
|
||||||
_Any -> re:split(PathString, "/", [{return, list}])
|
_Any -> re:split(PathString, "/", [{return, list}])
|
||||||
end,
|
end,
|
||||||
|
|
||||||
% process request
|
% process request
|
||||||
dispatch_request(YArg, PathElements).
|
dispatch_request(YArg, PathElements).
|
||||||
|
|
||||||
@ -28,15 +30,26 @@ dispatch_request(YArg, [H|T]) ->
|
|||||||
Username = path_element_to_atom(H),
|
Username = path_element_to_atom(H),
|
||||||
case T of
|
case T of
|
||||||
% no additional parameters, show user info
|
% no additional parameters, show user info
|
||||||
[] -> get_user_info(YArg, Username);
|
[] -> dispatch_user(YArg, Username);
|
||||||
[Param|Params] ->
|
[Param|Params] ->
|
||||||
Timeline = path_element_to_atom(Param)
|
Timeline = path_element_to_atom(Param),
|
||||||
dispatch_timeline(YArg, Username, Timeline, Params)
|
dispatch_timeline(YArg, Username, Timeline, Params)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
|
dispatch_user(YArg, Username) ->
|
||||||
|
Req = YArg#arg.req,
|
||||||
|
HTTPMethod = Req#http_request.method,
|
||||||
|
|
||||||
|
case HTTPMethod of
|
||||||
|
'GET' -> get_user(YArg, Username);
|
||||||
|
'PUT' -> put_user(YArg, Username);
|
||||||
|
'POST' -> put_user(YArg, Username);
|
||||||
|
'DELETE' -> delete_user(YArg, Username)
|
||||||
|
end.
|
||||||
|
|
||||||
% no entries, show timeline pages
|
% no entries, show timeline pages
|
||||||
dispatch_timeline(YArg, Username, Timeline, []) ->
|
dispatch_timeline(YArg, Username, Timeline, []) ->
|
||||||
Req = YArg#arg.arg,
|
Req = YArg#arg.req,
|
||||||
HTTPMethod = Req#http_request.method,
|
HTTPMethod = Req#http_request.method,
|
||||||
|
|
||||||
case HTTPMethod of
|
case HTTPMethod of
|
||||||
@ -51,17 +64,17 @@ dispatch_timeline(YArg, Username, Timeline, [H|T]) ->
|
|||||||
|
|
||||||
Param = path_element_to_atom(H),
|
Param = path_element_to_atom(H),
|
||||||
|
|
||||||
case Param of ->
|
case Param of
|
||||||
list -> list_entries(YArg, Username, Timeline);
|
list -> list_entries(YArg, Username, Timeline);
|
||||||
by_id -> dispatch_entry_by_id(YArg, Username, Timeline, T);
|
by_id -> dispatch_entry_by_id(YArg, Username, Timeline, T);
|
||||||
by_date -> dispatch_entry_by_date(YArg, Username, Timeline, T);
|
by_date -> dispatch_entry_by_date(YArg, Username, Timeline, T);
|
||||||
_Other -> make_json_404(YArg)
|
_Other -> make_json_404(YArg)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
dispatch_entry_by_id(YArg, Username, Timeline, []) ->
|
dispatch_entry_by_id(YArg, _Username, _Timeline, []) ->
|
||||||
make_json_404(YArg, [{note, "An entry id is expected in the URL."}];
|
make_json_404(YArg, [{note, "An entry id is expected in the URL."}]);
|
||||||
|
|
||||||
dispatch_entry_by_id(YArg, Username, Timeline, [H|T]) ->
|
dispatch_entry_by_id(YArg, Username, Timeline, [H|_T]) ->
|
||||||
|
|
||||||
EventId = list_to_integer(H), % TODO: guard against bad input
|
EventId = list_to_integer(H), % TODO: guard against bad input
|
||||||
get_entry_by_id(YArg, Username, Timeline, EventId).
|
get_entry_by_id(YArg, Username, Timeline, EventId).
|
||||||
@ -72,6 +85,14 @@ dispatch_entry_by_date(YArg, Username, Timeline, Params) -> todo.
|
|||||||
% ======== IMPLEMENTATION ====== %
|
% ======== IMPLEMENTATION ====== %
|
||||||
% ============================== %
|
% ============================== %
|
||||||
|
|
||||||
|
get_user(YArg, Username) -> todo.
|
||||||
|
|
||||||
|
put_user(YArg, Username) -> todo.
|
||||||
|
|
||||||
|
post_user(YArg, Username) -> todo.
|
||||||
|
|
||||||
|
delete_user(YArg, Username) -> todo.
|
||||||
|
|
||||||
get_timeline(YArg, Username, TimelineId) ->
|
get_timeline(YArg, Username, TimelineId) ->
|
||||||
case ts_timeline:lookup(Username, TimelineId) of
|
case ts_timeline:lookup(Username, TimelineId) of
|
||||||
|
|
||||||
@ -100,7 +121,7 @@ put_timeline(YArg, Username, TimelineId) ->
|
|||||||
% record created
|
% record created
|
||||||
ok ->
|
ok ->
|
||||||
|
|
||||||
EJSONRec = ts_json:record_to_ejson(NewRecord)
|
EJSONRec = ts_json:record_to_ejson(NewRecord),
|
||||||
JSONReturn = json:encode({struct, [
|
JSONReturn = json:encode({struct, [
|
||||||
{status, "ok"},
|
{status, "ok"},
|
||||||
{timeline, EJSONRec}
|
{timeline, EJSONRec}
|
||||||
@ -157,12 +178,25 @@ list_entries(YArg, Username, Timeline) ->
|
|||||||
|
|
||||||
% read or default the Start
|
% read or default the Start
|
||||||
Start = case lists:keyfind(start, 1, PostData) of
|
Start = case lists:keyfind(start, 1, PostData) of
|
||||||
{start, StartVal} -> StartVal; false -> 0 end.
|
{start, StartVal} -> StartVal; false -> 0 end,
|
||||||
|
|
||||||
% read or default the Length
|
% read or default the Length
|
||||||
Length = case lists:keyfind(length, 1, PostData)
|
Length = case lists:keyfind(length, 1, PostData) of
|
||||||
|
{length, LengthVal} -> LengthVal; false -> 50 end,
|
||||||
|
|
||||||
% read or default the sort order
|
% read or default the sort order
|
||||||
|
SortOrder = case lists:keyfind(order, 1, PostData) of
|
||||||
|
{order, "asc"} -> asc;
|
||||||
|
{order, "desc"} -> desc;
|
||||||
|
_Other -> asc
|
||||||
|
end,
|
||||||
|
|
||||||
|
Entries = case SortOrder of
|
||||||
|
asc -> ts_entry:list_asc({Username, Timeline}, Start, Length);
|
||||||
|
desc -> ts_entry:list_desc({Username, Timeline}, Start, Length)
|
||||||
|
end
|
||||||
|
|
||||||
|
.
|
||||||
|
|
||||||
get_entry_by_id(YArg, Username, Timeline, EventId) ->
|
get_entry_by_id(YArg, Username, Timeline, EventId) ->
|
||||||
case ts_entry:lookup(Username, Timeline, EventId) of
|
case ts_entry:lookup(Username, Timeline, EventId) of
|
||||||
@ -197,14 +231,14 @@ make_json_404(YArg) -> make_json_404(YArg, []).
|
|||||||
make_json_404(YArg, Fields) ->
|
make_json_404(YArg, Fields) ->
|
||||||
% add default status if not provided
|
% add default status if not provided
|
||||||
F1 = case lists:keyfind(status, 1, Fields) of
|
F1 = case lists:keyfind(status, 1, Fields) of
|
||||||
false -> Fields ++ [{status, not_found}];
|
false -> Fields ++ [{status, "not_found"}];
|
||||||
_Else -> Fields
|
_Else -> Fields
|
||||||
end,
|
end,
|
||||||
|
|
||||||
% add the path they requested
|
% add the path they requested
|
||||||
F2 = F1 ++ [{path, (YArg#arg.req)#http_request.path}],
|
F2 = F1 ++ [{path, element(2, (YArg#arg.req)#http_request.path)}],
|
||||||
|
|
||||||
[{status, 404}, {content, "application/json", json:encode({struct, Fields})}].
|
[{status, 404}, {content, "application/json", json:encode({struct, F2})}].
|
||||||
|
|
||||||
make_json_405(YArg) -> make_json_405(YArg, []).
|
make_json_405(YArg) -> make_json_405(YArg, []).
|
||||||
make_json_405(YArg, Fields) ->
|
make_json_405(YArg, Fields) ->
|
||||||
@ -217,7 +251,7 @@ make_json_405(YArg, Fields) ->
|
|||||||
% add the path they requested
|
% add the path they requested
|
||||||
F2 = F1 ++ [{path, (YArg#arg.req)#http_request.path}],
|
F2 = F1 ++ [{path, (YArg#arg.req)#http_request.path}],
|
||||||
|
|
||||||
[{status, 405}, {content, "application/json", json:encode({struct, Fields})}].
|
[{status, 405}, {content, "application/json", json:encode({struct, F2})}].
|
||||||
|
|
||||||
make_json_500(_YArg) ->
|
make_json_500(_YArg) ->
|
||||||
EJSON = {struct, [
|
EJSON = {struct, [
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
-module(ts_common).
|
-module(ts_common).
|
||||||
-export([list/3, compare_dates/2]).
|
-export([list/3, order_datetimes/2]).
|
||||||
|
|
||||||
-include_lib("stdlib/include/qlc.hrl").
|
-include_lib("stdlib/include/qlc.hrl").
|
||||||
|
|
||||||
|
@ -1,3 +1,12 @@
|
|||||||
|
-record(ts_user, {
|
||||||
|
username,
|
||||||
|
pwd,
|
||||||
|
pwd_salt,
|
||||||
|
name,
|
||||||
|
email,
|
||||||
|
join_date
|
||||||
|
}).
|
||||||
|
|
||||||
-record(ts_timeline, {
|
-record(ts_timeline, {
|
||||||
ref, % {username, timelineid}
|
ref, % {username, timelineid}
|
||||||
created,% {{year, month, day}, {hour, minute, second}}
|
created,% {{year, month, day}, {hour, minute, second}}
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
-module(ts_entry).
|
-module(ts_entry).
|
||||||
-export([create_table/1, new/1, update/1, lookup/3, list/3]).
|
-export([create_table/1, new/1, update/1, lookup/3, list_asc/3, list_desc/3]).
|
||||||
|
|
||||||
-include("ts_db_records.hrl").
|
-include("ts_db_records.hrl").
|
||||||
-include_lib("stdlib/include/qlc.hrl").
|
-include_lib("stdlib/include/qlc.hrl").
|
||||||
@ -7,10 +7,10 @@
|
|||||||
create_table(TableOpts) ->
|
create_table(TableOpts) ->
|
||||||
mnesia:create_table(ts_entry,
|
mnesia:create_table(ts_entry,
|
||||||
TableOpts ++ [{attributes, record_info(fields, ts_entry)},
|
TableOpts ++ [{attributes, record_info(fields, ts_entry)},
|
||||||
{type, ordered_set}, {index, timestamp}]).
|
{type, ordered_set}, {index, [timestamp]}]).
|
||||||
|
|
||||||
new(ER = #ts_entry()) ->
|
new(ER = #ts_entry{}) ->
|
||||||
{atmoic, NewRow) = mnesia:transaction(fun() ->
|
{atmoic, NewRow} = mnesia:transaction(fun() ->
|
||||||
{Username, TimelineId, _} = ER#ts_entry.ref,
|
{Username, TimelineId, _} = ER#ts_entry.ref,
|
||||||
NextId = id_counter:next_counter(ts_entry_id),
|
NextId = id_counter:next_counter(ts_entry_id),
|
||||||
NewRow = ER#ts_entry{ref = {Username, TimelineId, NextId}},
|
NewRow = ER#ts_entry{ref = {Username, TimelineId, NextId}},
|
||||||
@ -18,7 +18,7 @@ new(ER = #ts_entry()) ->
|
|||||||
NewRow end),
|
NewRow end),
|
||||||
{ok, NewRow}.
|
{ok, NewRow}.
|
||||||
|
|
||||||
update(ER = #ts_entry()) ->
|
update(ER = #ts_entry{}) ->
|
||||||
|
|
||||||
% look for existing record
|
% look for existing record
|
||||||
case mnesia:dirty_read(ts_entry, ER#ts_entry.ref) of
|
case mnesia:dirty_read(ts_entry, ER#ts_entry.ref) of
|
||||||
@ -34,30 +34,60 @@ lookup(Username, TimelineId, EntryId) ->
|
|||||||
[Entry] -> Entry
|
[Entry] -> Entry
|
||||||
end.
|
end.
|
||||||
|
|
||||||
list({Username, Timeline}, Start, Length)
|
list({Username, Timeline}, Start, Length, OrderFun)
|
||||||
when is_integer(Start) and is_integer(Length) ->
|
when is_integer(Start) and is_integer(Length) ->
|
||||||
ts_common:list(
|
|
||||||
qlc:q([E || E <- mnesia:table(ts_entry),
|
|
||||||
E#ts_entry.ref =:= {Username, Timeline, _}]_,
|
|
||||||
Start, Length).
|
|
||||||
|
|
||||||
list({Username, Timeline}, StartDateTime, EndDateTime) ->
|
|
||||||
|
|
||||||
{atomic, Entries} = mnesia:transaction(fun() ->
|
{atomic, Entries} = mnesia:transaction(fun() ->
|
||||||
|
% match the username and timeline
|
||||||
|
MatchHead = #ts_entry{ref = {Username, Timeline, '_'}, _='_'},
|
||||||
|
|
||||||
StartSeconds = calendar:datetime_to_gregorian_seconds(StartDateTime),
|
% select all records that match
|
||||||
EndSeconds = calendar:datetime_to_gregorian_seconds(EndDateTime),
|
mnesia:select(ts_entry, [{MatchHead, [], ['$_']}]
|
||||||
|
|
||||||
% create the query that will select these records
|
|
||||||
Q = qlc:q([E || E <- mnesia:table(ts_entry),
|
|
||||||
E#ts_entry.timestamp >= StartSeconds,
|
|
||||||
E#ts_entry.timestamp < EndSeconds]),
|
|
||||||
|
|
||||||
% sort by timestamp
|
|
||||||
SortedQ = qlc:sort(Q, {order, fun(A, B) ->
|
|
||||||
A#ts_entry.timestamp > B#ts_entry.timestamp end}),
|
|
||||||
|
|
||||||
% return
|
|
||||||
qlc:e(SortedQ)
|
|
||||||
end),
|
end),
|
||||||
Entries.
|
|
||||||
|
% sort
|
||||||
|
SortedEntries = lists:sort(OrderFun, Entries),
|
||||||
|
|
||||||
|
% return only the range selected.
|
||||||
|
% TODO: can we do this without selecting all entries?
|
||||||
|
lists:sublist(SortedEntries, Start, Length);
|
||||||
|
|
||||||
|
list({Username, Timeline}, StartDateTime, EndDateTime, OrderFun) ->
|
||||||
|
|
||||||
|
% compute the seconds from datetimes
|
||||||
|
StartSeconds = calendar:datetime_to_gregorian_seconds(StartDateTime),
|
||||||
|
EndSeconds = calendar:datetime_to_gregorian_seconds(EndDateTime),
|
||||||
|
|
||||||
|
% select all entries from the timeline that are within the time range
|
||||||
|
{atomic, Entries} = mnesia:transaction(fun() ->
|
||||||
|
|
||||||
|
% match the username and timeline id
|
||||||
|
MatchHead = #ts_entry{ref = {Username, Timeline, '_'}, timestamp='$1', _='_'},
|
||||||
|
|
||||||
|
% guards for the time range
|
||||||
|
StartGuard = {'>=', '$1', StartSeconds},
|
||||||
|
EndGuard = {'<', '$1', EndSeconds},
|
||||||
|
|
||||||
|
mnesia:select(ts_entry, [{MatchHead, [StartGuard, EndGuard], ['$_']}])
|
||||||
|
end,
|
||||||
|
|
||||||
|
% sort
|
||||||
|
lists:sort(OrderFun, Entries).
|
||||||
|
|
||||||
|
list_asc(TimelineRef, Start, Length)
|
||||||
|
when is_integer(Start) and is_integer(Length) ->
|
||||||
|
list(TimelineRef, Start, Length, fun timestamp_asc/2);
|
||||||
|
|
||||||
|
list_asc(TimelineRef, StartDateTime, EndDateTime) ->
|
||||||
|
list(TimelineRef, StartDateTime, EndDateTime, fun timestamp_asc/2).
|
||||||
|
|
||||||
|
list_desc(TimelineRef, Start, Length)
|
||||||
|
when is_integer(Start) and is_integer(Length) ->
|
||||||
|
list(TimelineRef, Start, Length, fun timestamp_desc/2);
|
||||||
|
|
||||||
|
list_desc(TimelineRef, StartDateTime, EndDateTime) ->
|
||||||
|
list(TimelineRef, StartDateTime, EndDateTime, fun timestamp_desc/2).
|
||||||
|
|
||||||
|
timestamp_asc(E1, E2) -> E1#ts_entry.timestamp > E2#ts_entry.timestamp.
|
||||||
|
|
||||||
|
timestamp_desc(E1, E2) -> E1#ts_entry.timestamp < E2#ts_entry.timestamp.
|
||||||
|
@ -13,7 +13,7 @@ record_to_ejson(Record=#ts_timeline{}) ->
|
|||||||
{struct, [
|
{struct, [
|
||||||
{id, atom_to_list(TimelineId)},
|
{id, atom_to_list(TimelineId)},
|
||||||
{created, encode_datetime(Record#ts_timeline.created)},
|
{created, encode_datetime(Record#ts_timeline.created)},
|
||||||
{description, Record#ts_timeline.desc}]}.
|
{description, Record#ts_timeline.desc}]};
|
||||||
|
|
||||||
record_to_ejson(Record=#ts_entry{}) ->
|
record_to_ejson(Record=#ts_entry{}) ->
|
||||||
% pull out the entry id
|
% pull out the entry id
|
||||||
@ -30,28 +30,28 @@ record_to_ejson(Record=#ts_entry{}) ->
|
|||||||
{notes, Record#ts_entry.notes}]}.
|
{notes, Record#ts_entry.notes}]}.
|
||||||
|
|
||||||
encode_datetime({{Year, Month, Day}, {Hour, Minute, Second}}) ->
|
encode_datetime({{Year, Month, Day}, {Hour, Minute, Second}}) ->
|
||||||
io_lib:format("~2B-~2B-~2BT~2.10.0B:~2.10.0B:~2.10.0BZ",
|
lists:flatten(io_lib:format("~4.10.0B-~2.10.0B-~2.10.0BT~2.10.0B:~2.10.0B:~2.10.0BZ",
|
||||||
[Year, Month, Day, Hour, Minute, Second]).
|
[Year, Month, Day, Hour, Minute, Second])).
|
||||||
|
|
||||||
ejson_to_record(Empty=#ts_timeline{}, EJSON) ->
|
ejson_to_record(Empty=#ts_timeline{}, EJSON) ->
|
||||||
{struct, Fields} = EJSON,
|
{struct, Fields} = EJSON,
|
||||||
{Username, _} = Empty#ts_timeline.ref,
|
{Username, _} = Empty#ts_timeline.ref,
|
||||||
|
|
||||||
#ts_timeline{
|
#ts_timeline{
|
||||||
ref = {Username, element(2, lists:keyfind(id, 1, EJSON))},
|
ref = {Username, element(2, lists:keyfind(id, 1, Fields))},
|
||||||
created = decode_datetime(element(2, lists:keyfind(created, 1, EJSON))),
|
created = decode_datetime(element(2, lists:keyfind(created, 1, Fields))),
|
||||||
desc = element(2, lists:keyfind(description, 1, EJSON))}.
|
desc = element(2, lists:keyfind(description, 1, Fields))};
|
||||||
|
|
||||||
ejson_to_record(Empty=#ts_entry{}, EJSON) ->
|
ejson_to_record(Empty=#ts_entry{}, EJSON) ->
|
||||||
{struct, Fields} = EJSON,
|
{struct, Fields} = EJSON,
|
||||||
{Username, TimelineId, _} = Empty#ts_entry.ref,
|
{Username, TimelineId, _} = Empty#ts_entry.ref,
|
||||||
|
|
||||||
#ts_entry{
|
#ts_entry{
|
||||||
ref = {Username, TimelineId, element(2, lists:keyfind(id, 1, EJSON))},
|
ref = {Username, TimelineId, element(2, lists:keyfind(id, 1, Fields))},
|
||||||
timestamp = calendar:datetime_to_gregorian_seconds(decode_datetime(
|
timestamp = calendar:datetime_to_gregorian_seconds(decode_datetime(
|
||||||
element(2, lists:keyfind(timestamp, 1, EJSON)))),
|
element(2, lists:keyfind(timestamp, 1, Fields)))),
|
||||||
mark = element(2, lists:keyfind(mark, 1, EJSON)),
|
mark = element(2, lists:keyfind(mark, 1, Fields)),
|
||||||
notes = element(2, lists:keyfind(notes, 1, EJSON))}.
|
notes = element(2, lists:keyfind(notes, 1, Fields))}.
|
||||||
|
|
||||||
decode_datetime(DateTimeString) ->
|
decode_datetime(DateTimeString) ->
|
||||||
% TODO: catch badmatch and badarg on whole function
|
% TODO: catch badmatch and badarg on whole function
|
||||||
@ -66,9 +66,9 @@ decode_datetime(DateTimeString) ->
|
|||||||
re:split(TimeString, ":", [{return, list}]),
|
re:split(TimeString, ":", [{return, list}]),
|
||||||
|
|
||||||
Date = {list_to_integer(YearString), list_to_integer(MonthString),
|
Date = {list_to_integer(YearString), list_to_integer(MonthString),
|
||||||
list_to_integer(DayString)}
|
list_to_integer(DayString)},
|
||||||
|
|
||||||
Time = {list_to_integer(HourString), list_to_integer(MinuteString),
|
Time = {list_to_integer(HourString), list_to_integer(MinuteString),
|
||||||
list_to_integer(SecondString)}
|
list_to_integer(SecondString)},
|
||||||
|
|
||||||
{Date, Time}.
|
{Date, Time}.
|
||||||
|
@ -25,15 +25,15 @@ update(TR = #ts_timeline{}) ->
|
|||||||
[_Record] -> mnesia:dirty_write(TR)
|
[_Record] -> mnesia:dirty_write(TR)
|
||||||
end.
|
end.
|
||||||
|
|
||||||
lookup(Username, TimlineId) ->
|
lookup(Username, TimelineId) ->
|
||||||
case mnesia:dirty_read(ts_timeline, {Username, TimelineId}) of
|
case mnesia:dirty_read(ts_timeline, {Username, TimelineId}) of
|
||||||
[] -> no_record;
|
[] -> no_record;
|
||||||
[Timeline] -> Timeline
|
[Timeline] -> Timeline
|
||||||
end.
|
end.
|
||||||
|
|
||||||
list(Username, Start, Length) ->
|
list(Username, Start, Length) ->
|
||||||
ts_common:list(
|
{atomic, Timelines} = mnesia:transaction(fun() ->
|
||||||
qlc:q([T || T <- mnesia:table(ts_timeline),
|
MatchHead = #ts_timeline{ref = {Username, '_'}, _='_'},
|
||||||
T#ts_timeline.ref =:= {Username, _}]),
|
mnesia:select(ts_timeline, [{MatchHead, [], ['$_']}])
|
||||||
Start, Length).
|
end),
|
||||||
|
lists:sublist(Timelines, Start, Length).
|
||||||
|
12
src/ts_user.erl
Normal file
12
src/ts_user.erl
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
-module(ts_user).
|
||||||
|
-export([]).
|
||||||
|
|
||||||
|
-include("ts_db_records.hrl").
|
||||||
|
-include_lib("stdlib/include/qlc.hrl").
|
||||||
|
|
||||||
|
create_table(TableOpts) ->
|
||||||
|
mnesia:create_table(ts_user,
|
||||||
|
TableOpts ++ [{attributes, record_info(fields, ts_user)},
|
||||||
|
{type, ordered_set}]).
|
||||||
|
|
||||||
|
%new(TR = #ts_user
|
112
src/yaws_api.hrl
Normal file
112
src/yaws_api.hrl
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
%%% File : yaws_api.hrl
|
||||||
|
%%% Author : Claes Wikstrom <klacke@hyber.org>
|
||||||
|
%%% Purpose :
|
||||||
|
%%% Created : 24 Jan 2002 by Claes Wikstrom <klacke@hyber.org>
|
||||||
|
%%%----------------------------------------------------------------------
|
||||||
|
|
||||||
|
-author('klacke@hyber.org').
|
||||||
|
|
||||||
|
-record(arg, {
|
||||||
|
clisock, %% the socket leading to the peer client
|
||||||
|
client_ip_port, %% {ClientIp, ClientPort} tuple
|
||||||
|
headers, %% headers
|
||||||
|
req, %% request
|
||||||
|
clidata, %% The client data (as a binary in POST requests)
|
||||||
|
server_path, %% The normalized server path
|
||||||
|
%% (pre-querystring part of URI)
|
||||||
|
querydata, %% For URIs of the form ...?querydata
|
||||||
|
%% equiv of cgi QUERY_STRING
|
||||||
|
appmoddata, %% (deprecated - use pathinfo instead) the remainder
|
||||||
|
%% of the path leading up to the query
|
||||||
|
docroot, %% Physical base location of data for this request
|
||||||
|
docroot_mount, %% virtual directory e.g /myapp/ that the docroot
|
||||||
|
%% refers to.
|
||||||
|
fullpath, %% full deep path to yaws file
|
||||||
|
cont, %% Continuation for chunked multipart uploads
|
||||||
|
state, %% State for use by users of the out/1 callback
|
||||||
|
pid, %% pid of the yaws worker process
|
||||||
|
opaque, %% useful to pass static data
|
||||||
|
appmod_prepath, %% (deprecated - use prepath instead) path in front
|
||||||
|
%%of: <appmod><appmoddata>
|
||||||
|
prepath, %% Path prior to 'dynamic' segment of URI.
|
||||||
|
%% ie http://some.host/<prepath>/<script-point>/d/e
|
||||||
|
%% where <script-point> is an appmod mount point,
|
||||||
|
%% or .yaws,.php,.cgi,.fcgi etc script file.
|
||||||
|
pathinfo %% Set to '/d/e' when calling c.yaws for the request
|
||||||
|
%% http://some.host/a/b/c.yaws/d/e
|
||||||
|
%% equiv of cgi PATH_INFO
|
||||||
|
}).
|
||||||
|
|
||||||
|
|
||||||
|
-record(http_request, {method,
|
||||||
|
path,
|
||||||
|
version}).
|
||||||
|
|
||||||
|
-record(http_response, {version,
|
||||||
|
status,
|
||||||
|
phrase}).
|
||||||
|
|
||||||
|
-record(headers, {
|
||||||
|
connection,
|
||||||
|
accept,
|
||||||
|
host,
|
||||||
|
if_modified_since,
|
||||||
|
if_match,
|
||||||
|
if_none_match,
|
||||||
|
if_range,
|
||||||
|
if_unmodified_since,
|
||||||
|
range,
|
||||||
|
referer,
|
||||||
|
user_agent,
|
||||||
|
accept_ranges,
|
||||||
|
cookie = [],
|
||||||
|
keep_alive,
|
||||||
|
location,
|
||||||
|
content_length,
|
||||||
|
content_type,
|
||||||
|
content_encoding,
|
||||||
|
authorization,
|
||||||
|
transfer_encoding,
|
||||||
|
other = [] %% misc other headers
|
||||||
|
}).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
-record(url,
|
||||||
|
{scheme, %% undefined means not set
|
||||||
|
host, %% undefined means not set
|
||||||
|
port, %% undefined means not set
|
||||||
|
path = [],
|
||||||
|
querypart = []}).
|
||||||
|
|
||||||
|
|
||||||
|
-record(setcookie,{
|
||||||
|
key,
|
||||||
|
value,
|
||||||
|
quoted,
|
||||||
|
comment,
|
||||||
|
comment_url,
|
||||||
|
discard,
|
||||||
|
domain,
|
||||||
|
max_age,
|
||||||
|
expires,
|
||||||
|
path,
|
||||||
|
port,
|
||||||
|
secure,
|
||||||
|
version}).
|
||||||
|
|
||||||
|
|
||||||
|
-record(redir_self, {
|
||||||
|
host, %% string() - our own host
|
||||||
|
scheme, %% http | https
|
||||||
|
scheme_str, %% "https://" | "http://"
|
||||||
|
port, %% integer() - our own port
|
||||||
|
port_str %% "" | ":<int>" - the optional port part
|
||||||
|
%% to append to the url
|
||||||
|
}).
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user