-module(ts_json). -export([encode_record/1, record_to_ejson/1, ejson_to_record/2, ejson_to_record_strict/2]). -include("ts_db_records.hrl"). encode_record(Record) -> lists:flatten(json:encode(record_to_ejson(Record))). % User JSON record structure: % {"id": "john_doe", % "name": "John Doe", % "email": "john.doe@example.com", % "join_date": "2011-01-01T12:00.000Z", % "ext_data": {"last_timeline", "personal"}} record_to_ejson(Record=#ts_user{}) -> {struct, [ {id, Record#ts_user.username}, {name, Record#ts_user.name}, {email, Record#ts_user.email}, {join_date, encode_datetime(Record#ts_user.join_date)}, {ext_data, Record#ts_user.ext_data}]}; % Timeline JSON record stucture: % {"user_id": "john_doe", % "id": "personal", % "created": "2011-01-01T14:00.000Z", % "description:"Personal time-tracking."} record_to_ejson(Record=#ts_timeline{}) -> % pull out the username and timeline id {Username, TimelineId} = Record#ts_timeline.ref, % create the EJSON struct {struct, [ {user_id, Username}, {id, TimelineId}, {created, encode_datetime(Record#ts_timeline.created)}, {description, Record#ts_timeline.desc}]}; % Entry JSON record structure: % {"user_id": "john_doe", % "timeline_id": "personal", % "id": "1", % "timestamp": "2011-01-01T14:01.000Z", % "mark": "Workout.", % "notes": "First workout after a long break."} record_to_ejson(Record=#ts_entry{}) -> % pull out the username, timeline id, and entry id {Username, TimelineId, EntryId} = Record#ts_entry.ref, % convert the timestamp to a date-time DateTime = calendar:gregorian_seconds_to_datetime(Record#ts_entry.timestamp), % create the EJSON struct {struct, [ {user_id, Username}, {timeline_id, TimelineId}, {id, EntryId}, {timestamp, encode_datetime(DateTime)}, {mark, Record#ts_entry.mark}, {notes, Record#ts_entry.notes}]}. encode_datetime({{Year, Month, Day}, {Hour, Minute, Second}}) -> lists:flatten(io_lib:format("~4.10.0B-~2.10.0B-~2.10.0BT~2.10.0B:~2.10.0B:~2.10.0B.~3.10.0BZ", [Year, Month, Day, Hour, Minute, Second, 000])). ejson_to_record(Rec=#ts_timeline{}, EJSON) -> {struct, Fields} = EJSON, Created = case lists:keyfind(created, 1, Fields) of false -> undefined; {created, CreatedVal} -> decode_datetime(CreatedVal) end, Desc = case lists:keyfind(description, 1, Fields) of false -> undefined; {description, DescVal} -> DescVal end, Rec#ts_timeline{ created = Created, desc = Desc }; ejson_to_record(Rec=#ts_entry{}, EJSON) -> {struct, Fields} = EJSON, Timestamp = case lists:keyfind(timestamp, 1, Fields) of false -> undefined; {timestamp, TSVal} -> calendar:datetime_to_gregorian_seconds( decode_datetime(TSVal)) end, Mark = case lists:keyfind(mark, 1, Fields) of false -> undefined; {mark, MarkVal} -> MarkVal end, Notes = case lists:keyfind(notes, 1, Fields) of false -> undefined; {notes, NotesVal} -> NotesVal end, Rec#ts_entry{ timestamp = Timestamp, mark = Mark, notes = Notes}. ejson_to_record_strict(Rec=#ts_timeline{}, EJSON) -> {struct, Fields} = EJSON, Rec#ts_timeline{ created = decode_datetime(element(2, lists:keyfind(created, 1, Fields))), desc = element(2, lists:keyfind(description, 1, Fields))}; ejson_to_record_strict(Rec=#ts_entry{}, EJSON) -> {struct, Fields} = EJSON, Rec#ts_entry{ timestamp = calendar:datetime_to_gregorian_seconds(decode_datetime( element(2, lists:keyfind(timestamp, 1, Fields)))), mark = element(2, lists:keyfind(mark, 1, Fields)), notes = element(2, lists:keyfind(notes, 1, Fields))}. decode_datetime(DateTimeString) -> % TODO: catch badmatch and badarg on whole function [DateString, TimeString] = re:split(DateTimeString, "[TZ]", [{return, list}, trim]), [YearString, MonthString, DayString] = re:split(DateString, "-", [{return, list}]), [HourString, MinuteString, SecondString] = case re:split(TimeString, "[:\\.]", [{return, list}]) of [HS, MS, SS, _MSS] -> [HS, MS, SS]; [HS, MS, SS] -> [HS, MS, SS] end, Date = {list_to_integer(YearString), list_to_integer(MonthString), list_to_integer(DayString)}, Time = {list_to_integer(HourString), list_to_integer(MinuteString), list_to_integer(SecondString)}, {Date, Time}.