Remove subject IDs and improve request handling

This commit is contained in:
Camden Dixie O'Brien 2025-03-01 17:22:31 +00:00
parent 9588e88b93
commit 68c55573de
9 changed files with 60 additions and 47 deletions

View File

@ -16,12 +16,12 @@ namespace StudySystemClient {
scrolled_window.add_css_class("card-container"); scrolled_window.add_css_class("card-container");
var activities = new Activity[] { var activities = new Activity[] {
{ 2, "Linguistics", ActivityType.EXERCISES }, { "Linguistics", ActivityType.EXERCISES },
{ 1, "Cybernetics", ActivityType.EXERCISES }, { "Cybernetics", ActivityType.EXERCISES },
{ 2, "Linguistics", ActivityType.READING }, { "Linguistics", ActivityType.READING },
{ 0, "Physics", ActivityType.READING }, { "Physics", ActivityType.READING },
{ 1, "Cybernetics", ActivityType.READING }, { "Cybernetics", ActivityType.READING },
{ 0, "Physics", ActivityType.EXERCISES }, { "Physics", ActivityType.EXERCISES },
}; };
foreach (var activity in activities) { foreach (var activity in activities) {
var card = new ActivityCard(client, activity); var card = new ActivityCard(client, activity);
@ -42,7 +42,7 @@ namespace StudySystemClient {
var text = new Gtk.Box(Gtk.Orientation.VERTICAL, 6); var text = new Gtk.Box(Gtk.Orientation.VERTICAL, 6);
text.hexpand = true; text.hexpand = true;
var subject = new Gtk.Label(activity.subject_name); var subject = new Gtk.Label(activity.subject);
subject.halign = Gtk.Align.START; subject.halign = Gtk.Align.START;
subject.add_css_class("activity-subject"); subject.add_css_class("activity-subject");
text.append(subject); text.append(subject);

View File

@ -1,14 +1,13 @@
namespace StudySystemClient { namespace StudySystemClient {
public struct Activity { public struct Activity {
public int subject_id; public string subject;
public string subject_name;
public ActivityType type; public ActivityType type;
public double priority; public double priority;
} }
public enum ActivityType { public enum ActivityType {
EXERCISES, READING = 0,
READING; EXERCISES = 1;
public string to_string() { public string to_string() {
switch (this) { switch (this) {

View File

@ -41,11 +41,11 @@ namespace StudySystemClient {
} }
} }
public async void log_session(int subject_id, ActivityType type, public async void log_session(string subject, ActivityType type,
int minutes) throws ClientError { int minutes) throws ClientError {
var timestamp = new DateTime.now_utc().to_unix(); var timestamp = new DateTime.now_utc().to_unix();
var request = new Request.LogSession(subject_id, type, var request
timestamp, minutes); = new Request.LogSession(subject, type, timestamp, minutes);
var response = yield connection.send(request); var response = yield connection.send(request);
if (response is Response.Ack) { if (response is Response.Ack) {
return; return;

View File

@ -39,11 +39,11 @@ namespace StudySystemClient.Request {
} }
public class LogSession : Body { public class LogSession : Body {
public LogSession(int subject_id, ActivityType type, public LogSession(string subject, ActivityType type,
int64 timestamp, int minutes) int64 timestamp, int minutes)
{ {
var fields = new Der.Datum[] { var fields = new Der.Datum[] {
new Der.Integer(subject_id), new Der.Utf8String(subject),
new Der.Enumerated((int)type), new Der.Enumerated((int)type),
new Der.Integer(timestamp), new Der.Integer(timestamp),
new Der.Integer(minutes), new Der.Integer(minutes),

View File

@ -127,20 +127,17 @@ namespace StudySystemClient.Response {
throws DecodeError { throws DecodeError {
if (datum is Der.Sequence) { if (datum is Der.Sequence) {
var fields = datum.value; var fields = datum.value;
if (fields.length < 4) { if (fields.length < 3) {
throw new DecodeError.INVALID_BODY( throw new DecodeError.INVALID_BODY(
"Too few fields in Activity: %u (expected 4)", "Too few fields in Activity: %u (expected 3)",
fields.length); fields.length);
} }
var subject_id = get_int("Activity.subjectId", fields[0]); var subject = get_string("Activity.subject", fields[0]);
var subject_name
= get_string("Activity.subjectName", fields[1]);
var activity_type var activity_type
= get_activity_type("Activity.type", fields[2]); = get_activity_type("Activity.type", fields[1]);
var int_priority = get_int("Activity.priority", fields[3]); var priority_int = get_int("Activity.priority", fields[2]);
var priority = (double)int_priority / 100.0; var priority = (double)priority_int / 100.0;
return { subject_id, subject_name, activity_type, priority }; return { subject, activity_type, priority };
} else { } else {
throw new DecodeError.INVALID_BODY( throw new DecodeError.INVALID_BODY(
"Activity was not a SEQUENCE"); "Activity was not a SEQUENCE");

View File

@ -65,7 +65,7 @@ namespace StudySystemClient {
public class SessionFactory { public class SessionFactory {
private const string CA_FILENAME = "/ca.pem"; private const string CA_FILENAME = "/ca.pem";
private const string CERT_FILENAME = "/client.pem"; private const string CERT_FILENAME = "/client.pem";
private const uint TIMEOUT_S = 1; private const uint TIMEOUT_S = 5;
private InetSocketAddress host; private InetSocketAddress host;
private TlsCertificate cert; private TlsCertificate cert;

View File

@ -6,7 +6,7 @@ ActivityType ::= ENUMERATED {
} }
Session ::= SEQUENCE { Session ::= SEQUENCE {
subjectId INTEGER, subject UTF8String,
type ActivityType, type ActivityType,
timestamp INTEGER, timestamp INTEGER,
minutes INTEGER minutes INTEGER
@ -24,8 +24,7 @@ Request ::= SEQUENCE {
} }
Activity ::= SEQUENCE { Activity ::= SEQUENCE {
subjectId INTEGER, subject UTF8String,
subjectName UTF8String,
type ActivityType, type ActivityType,
priority INTEGER priority INTEGER
} }

View File

@ -13,7 +13,8 @@ start_link(Socket) ->
init(Socket) -> init(Socket) ->
ok = ssl:setopts(Socket, [{active, true}]), ok = ssl:setopts(Socket, [{active, true}]),
{ok, #{socket => Socket}}. process_flag(trap_exit, true),
{ok, #{socket => Socket, transactions => #{}}}.
handle_call(_Request, _From, State) -> handle_call(_Request, _From, State) ->
{reply, ok, State}. {reply, ok, State}.
@ -21,21 +22,28 @@ handle_call(_Request, _From, State) ->
handle_cast(_Msg, State) -> handle_cast(_Msg, State) ->
{noreply, State}. {noreply, State}.
handle_info({ssl, Socket, Data}, State) -> handle_info({ssl, Socket, Data}, State = #{transactions := Transactions}) ->
case 'StudySystemProtocol':decode('Request', Data) of case 'StudySystemProtocol':decode('Request', Data) of
{ok, {'Request', TransactionId, RequestBody}} -> {ok, {'Request', TransactionId, RequestBody}} ->
ResponseBody = map_request(RequestBody), Pid = spawn_link(fun() -> handle_request(RequestBody) end),
{ok, Encoded} = 'StudySystemProtocol':encode( NewTransactions = maps:put(Pid, TransactionId, Transactions),
'Response', {noreply, State#{transactions := NewTransactions}};
{'Response', TransactionId, ResponseBody}),
ok = ssl:send(Socket, Encoded);
{error, {asn1, _Reason}} -> {error, {asn1, _Reason}} ->
{ok, Encoded} = 'StudySystemProtocol':encode( send(Socket, -1, {error, invalidRequest}),
'Response', {noreply, State}
{'Response', -1, {error, invalidRequest}}), end;
ok = ssl:send(Socket, Encoded) handle_info({'EXIT', Pid, Reason},
State = #{socket := Socket, transactions := Transactions})
when is_map_key(Pid, Transactions) ->
TransactionId = maps:get(Pid, Transactions),
Response = case Reason of
{response, Value} -> Value;
_ ->
io:format("Request handling error: ~p~n", [Reason]),
{error, serverError}
end, end,
{noreply, State}; send(Socket, TransactionId, Response),
{noreply, State#{transactions := maps:remove(Pid, Transactions)}};
handle_info({ssl_closed, _Socket}, State) -> handle_info({ssl_closed, _Socket}, State) ->
{stop, normal, State}; {stop, normal, State};
handle_info({ssl_error, _Socket, _Reason}, State) -> handle_info({ssl_error, _Socket, _Reason}, State) ->
@ -49,7 +57,17 @@ terminate(_Reason, #{socket := Socket}) ->
code_change(_OldVsn, State, _Extra) -> code_change(_OldVsn, State, _Extra) ->
{ok, State}. {ok, State}.
handle_request(Request) ->
timer:kill_after(500),
exit(map_request(Request)).
map_request({ping, 'NULL'}) -> map_request({ping, 'NULL'}) ->
{ack, 'NULL'}; {response, {ack, 'NULL'}};
map_request(_) -> map_request(_) ->
{error, invalidRequest}. {response, {error, invalidArguments}}.
send(Socket, TransactionId, Response) ->
{ok, Encoded} = 'StudySystemProtocol':encode(
'Response',
{'Response', TransactionId, Response}),
ok = ssl:send(Socket, Encoded).

View File

@ -11,8 +11,8 @@ start_link() ->
init([]) -> init([]) ->
SupFlags = #{strategy => simple_one_for_one, SupFlags = #{strategy => simple_one_for_one,
intensity => 10, intensity => 5,
period => 1}, period => 10},
ChildSpec = #{id => session_server, ChildSpec = #{id => session_server,
start => {session_server, start_link, []}, start => {session_server, start_link, []},
restart => temporary, restart => temporary,