Статистика для дашборда, расширенная #7
This commit is contained in:
+19
-14
@@ -4,20 +4,25 @@
|
||||
update_role/2, block/1, unblock/1, generate_id/0]).
|
||||
|
||||
create(Email, Password, Role) ->
|
||||
Id = generate_id(),
|
||||
{ok, Hash} = argon2:hash(Password),
|
||||
Now = calendar:universal_time(),
|
||||
Admin = #admin{
|
||||
id = Id,
|
||||
email = Email,
|
||||
password_hash = Hash,
|
||||
role = Role,
|
||||
status = active,
|
||||
created_at = Now,
|
||||
updated_at = Now
|
||||
},
|
||||
mnesia:dirty_write(Admin),
|
||||
{ok, Admin}.
|
||||
case get_by_email(Email) of
|
||||
{ok, _} ->
|
||||
{error, email_exists};
|
||||
{error, not_found} ->
|
||||
Id = generate_id(),
|
||||
{ok, Hash} = argon2:hash(Password),
|
||||
Now = calendar:universal_time(),
|
||||
Admin = #admin{
|
||||
id = Id,
|
||||
email = Email,
|
||||
password_hash = Hash,
|
||||
role = Role,
|
||||
status = active,
|
||||
created_at = Now,
|
||||
updated_at = Now
|
||||
},
|
||||
mnesia:dirty_write(Admin),
|
||||
{ok, Admin}
|
||||
end.
|
||||
|
||||
get_by_email(Email) ->
|
||||
Match = #admin{email = Email, _ = '_'},
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
-module(core_admin_audit).
|
||||
-include("records.hrl").
|
||||
-export([log/7, list/0, list/1]).
|
||||
-export([count_actions_by_admin/2]).
|
||||
|
||||
log(AdminId, Email, Role, Action, EntityType, EntityId, Ip) ->
|
||||
log(AdminId, Email, Role, Action, EntityType, EntityId, Ip, undefined).
|
||||
@@ -26,15 +27,33 @@ list() ->
|
||||
|
||||
%% Фильтрация по параметрам (простая версия)
|
||||
list(Filters) ->
|
||||
All = list(),
|
||||
All = list(), % все записи
|
||||
lists:filter(fun(E) ->
|
||||
% Фильтр по admin_id
|
||||
case proplists:get_value(admin_id, Filters) of
|
||||
undefined -> true;
|
||||
Id -> E#admin_audit.admin_id =:= Id
|
||||
end andalso
|
||||
case proplists:get_value(action, Filters) of
|
||||
undefined -> true;
|
||||
Act -> E#admin_audit.action =:= Act
|
||||
end
|
||||
% можно добавить фильтр по дате и т.д.
|
||||
end, All).
|
||||
Id -> E#admin_audit.admin_id =:= Id
|
||||
end
|
||||
andalso
|
||||
% Фильтр по action
|
||||
case proplists:get_value(action, Filters) of
|
||||
undefined -> true;
|
||||
Act -> E#admin_audit.action =:= Act
|
||||
end
|
||||
andalso
|
||||
% Фильтр по дате с
|
||||
case proplists:get_value(date_from, Filters) of
|
||||
undefined -> true;
|
||||
From -> E#admin_audit.timestamp >= From
|
||||
end
|
||||
andalso
|
||||
% Фильтр по дате по
|
||||
case proplists:get_value(date_to, Filters) of
|
||||
undefined -> true;
|
||||
To -> E#admin_audit.timestamp =< To
|
||||
end
|
||||
end, All).
|
||||
|
||||
count_actions_by_admin(AdminId, Action) ->
|
||||
Match = #admin_audit{admin_id = AdminId, action = Action, _ = '_'},
|
||||
length(mnesia:dirty_match_object(Match)).
|
||||
+17
-1
@@ -4,7 +4,7 @@
|
||||
-export([create/4, create_recurring/5, get_by_id/1, list_by_calendar/1,
|
||||
update/2, delete/1, materialize_occurrence/3]).
|
||||
-export([generate_id/0]).
|
||||
-export([count_events/0]).
|
||||
-export([count_events/0, count_events_by_date/2]).
|
||||
|
||||
%% Создание одиночного события
|
||||
create(CalendarId, Title, StartTime, Duration) ->
|
||||
@@ -171,6 +171,22 @@ delete(Id) ->
|
||||
count_events() ->
|
||||
mnesia:table_info(event, size).
|
||||
|
||||
count_events_by_date(From, To) ->
|
||||
All = mnesia:dirty_match_object(#event{_ = '_'}),
|
||||
Filtered = lists:filter(fun(E) ->
|
||||
E#event.created_at >= From andalso E#event.created_at =< To
|
||||
end, All),
|
||||
Counts = lists:foldl(fun(E, Acc) ->
|
||||
Day = date_part(E#event.created_at),
|
||||
case lists:keyfind(Day, 1, Acc) of
|
||||
false -> [{Day, 1} | Acc];
|
||||
{Day, C} -> lists:keyreplace(Day, 1, Acc, {Day, C+1})
|
||||
end
|
||||
end, [], Filtered),
|
||||
lists:sort(Counts).
|
||||
|
||||
date_part({{Y,M,D}, _}) -> {Y,M,D}.
|
||||
|
||||
%% Внутренние функции
|
||||
generate_id() ->
|
||||
base64:encode(crypto:strong_rand_bytes(16), #{mode => urlsafe, padding => false}).
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
-export([update_status/3, get_count_by_target/2]).
|
||||
-export([generate_id/0]).
|
||||
-export([count_reports_by_status/1, count_reports_by_admin/2]).
|
||||
-export([count_reports_resolved_by_admin/2, avg_resolution_time/1]).
|
||||
|
||||
%% Создание жалобы
|
||||
create(ReporterId, TargetType, TargetId, Reason) ->
|
||||
@@ -92,6 +93,22 @@ count_reports_by_admin(AdminId, Status) ->
|
||||
Match = #report{resolved_by = AdminId, status = Status, _ = '_'},
|
||||
length(mnesia:dirty_match_object(Match)).
|
||||
|
||||
count_reports_resolved_by_admin(AdminId, Status) ->
|
||||
Match = #report{resolved_by = AdminId, status = Status, _ = '_'},
|
||||
length(mnesia:dirty_match_object(Match)).
|
||||
|
||||
avg_resolution_time(Status) ->
|
||||
Match = #report{status = Status, _ = '_'},
|
||||
Reports = mnesia:dirty_match_object(Match),
|
||||
Resolved = lists:filter(fun(R) -> R#report.resolved_at =/= undefined end, Reports),
|
||||
case Resolved of
|
||||
[] -> 0;
|
||||
_ ->
|
||||
TotalSeconds = lists:sum([calendar:datetime_to_gregorian_seconds(R#report.resolved_at) -
|
||||
calendar:datetime_to_gregorian_seconds(R#report.created_at) || R <- Resolved]),
|
||||
TotalSeconds / length(Resolved) / 3600.0
|
||||
end.
|
||||
|
||||
%% Внутренние функции
|
||||
generate_id() ->
|
||||
base64:encode(crypto:strong_rand_bytes(16), #{mode => urlsafe, padding => false}).
|
||||
+23
-12
@@ -8,6 +8,7 @@
|
||||
create_ticket/1,
|
||||
list_by_user/1]).
|
||||
-export([count_tickets_by_status/1, count_tickets_by_admin/2]).
|
||||
-export([avg_resolution_time/0]).
|
||||
|
||||
list_all() ->
|
||||
mnesia:dirty_match_object(#ticket{_ = '_'}).
|
||||
@@ -69,6 +70,27 @@ create_ticket(Data) ->
|
||||
list_by_user(UserId) ->
|
||||
mnesia:dirty_match_object(#ticket{reporter_id = UserId, _ = '_'}).
|
||||
|
||||
count_by_status(Status, Tickets) ->
|
||||
length([T || T <- Tickets, T#ticket.status =:= Status]).
|
||||
|
||||
count_tickets_by_status(Status) ->
|
||||
Match = #ticket{status = Status, _ = '_'},
|
||||
length(mnesia:dirty_match_object(Match)).
|
||||
|
||||
count_tickets_by_admin(AdminId, Status) ->
|
||||
Match = #ticket{assigned_to = AdminId, status = Status, _ = '_'},
|
||||
length(mnesia:dirty_match_object(Match)).
|
||||
|
||||
avg_resolution_time() ->
|
||||
Tickets = mnesia:dirty_match_object(#ticket{status = closed, _ = '_'}),
|
||||
case Tickets of
|
||||
[] -> 0;
|
||||
_ ->
|
||||
TotalSeconds = lists:sum([calendar:datetime_to_gregorian_seconds(T#ticket.last_seen) -
|
||||
calendar:datetime_to_gregorian_seconds(T#ticket.first_seen) || T <- Tickets]),
|
||||
TotalSeconds / length(Tickets) / 3600.0
|
||||
end.
|
||||
|
||||
%% ── внутренние ─────────────────────────────────────────
|
||||
apply_updates(Ticket, Updates) ->
|
||||
lists:foldl(fun({Key, Value}, Acc) ->
|
||||
@@ -81,15 +103,4 @@ apply_updates(Ticket, Updates) ->
|
||||
<<"context">> -> Acc#ticket{context = Value};
|
||||
_ -> Acc
|
||||
end
|
||||
end, Ticket, maps:to_list(Updates)).
|
||||
|
||||
count_by_status(Status, Tickets) ->
|
||||
length([T || T <- Tickets, T#ticket.status =:= Status]).
|
||||
|
||||
count_tickets_by_status(Status) ->
|
||||
Match = #ticket{status = Status, _ = '_'},
|
||||
length(mnesia:dirty_match_object(Match)).
|
||||
|
||||
count_tickets_by_admin(AdminId, Status) ->
|
||||
Match = #ticket{assigned_to = AdminId, status = Status, _ = '_'},
|
||||
length(mnesia:dirty_match_object(Match)).
|
||||
end, Ticket, maps:to_list(Updates)).
|
||||
+17
-1
@@ -6,7 +6,7 @@
|
||||
-export([generate_id/0]).
|
||||
-export([list_users/0]).
|
||||
-export([block/1, unblock/1]).
|
||||
-export([count_users/0]).
|
||||
-export([count_users/0, count_users_by_date/2]).
|
||||
|
||||
%% Создание пользователя
|
||||
create(Email, Password) ->
|
||||
@@ -126,6 +126,22 @@ unblock(Id) ->
|
||||
count_users() ->
|
||||
mnesia:table_info(user, size).
|
||||
|
||||
count_users_by_date(From, To) ->
|
||||
All = mnesia:dirty_match_object(#user{_ = '_'}),
|
||||
Filtered = lists:filter(fun(U) ->
|
||||
U#user.created_at >= From andalso U#user.created_at =< To
|
||||
end, All),
|
||||
Counts = lists:foldl(fun(U, Acc) ->
|
||||
Day = date_part(U#user.created_at),
|
||||
case lists:keyfind(Day, 1, Acc) of
|
||||
false -> [{Day, 1} | Acc];
|
||||
{Day, C} -> lists:keyreplace(Day, 1, Acc, {Day, C+1})
|
||||
end
|
||||
end, [], Filtered),
|
||||
lists:sort(Counts).
|
||||
|
||||
date_part({{Y,M,D}, _}) -> {Y,M,D}.
|
||||
|
||||
%% Внутренние функции
|
||||
generate_id() ->
|
||||
base64:encode(crypto:strong_rand_bytes(16), #{mode => urlsafe, padding => false}).
|
||||
|
||||
Reference in New Issue
Block a user