]> Devoid-pointer.net GitWeb - Nine-Q.git/commitdiff
- Store multiple problems to allow for the user to return to previous problems
authorMichal Malý <madcatxster@devoid-pointer.net>
Mon, 24 Nov 2014 23:47:41 +0000 (00:47 +0100)
committerMichal Malý <madcatxster@devoid-pointer.net>
Mon, 24 Nov 2014 23:47:41 +0000 (00:47 +0100)
18 files changed:
bin/templates/face_acidobazic.html
bin/templates/face_solubility_params.html
bin/templates/face_solubility_submit.html
src/face_generators/face_generator.adb
src/face_generators/face_generator.ads
src/formatting_helpers.adb
src/global_types.adb
src/global_types.ads
src/handlers/handler_check_answer.adb
src/handlers/handler_next_problem.adb
src/handlers/handler_start.adb
src/nine_q.adb
src/problem_generators/problem_generator-solubility_suite.adb
src/problem_generators/problem_generator.adb
src/problem_generators/problem_generator.ads
src/problem_generators/problem_generator_syswides.ads
src/problem_manager.adb
src/problem_manager.ads

index 59001ca0d5c0e188768fac6eba9c583ee187b867..e36074df43d323b6bca845d8a9764e05c69f4d75 100644 (file)
@@ -3,6 +3,7 @@
     <div class="caption_v1">Zadání:</div>
     <div class="assignment_text">Vypočítejte pH <span class="key_info">@_SUBSTANCE_@</span> jejíž <span class="key_info">@_PKX_@ = @_PKX_VALUE_INT_@,@_PKX_VALUE_DEC_@</span> a koncentrace <span class="key_info">c = @_CONCENTRATION_INT_@,@_CONCENTRATION_DEC_@ . 10<span class="exponent">@_CONCENTRATION_EXP_@</span></span> mol/L.</div>
       <form class="problem_form" method="post" action="/check_answer" enctype="multipart/form-data">
+       <input type="hidden" name="@_RESERVED__PROBLEM_ID_@" value="@_RESERVED__PROBLEM_ID_VAL_@">
        <div class="form_line">
          <label class="form_label_ac" for="@_ANSWER_PH_@">pH:</label>
          <input class="form_input_ac" type="text" name="@_ANSWER_PH_@" maxlength="128" value="" />
@@ -36,6 +37,7 @@
     <div class="backgrounded_block">
       <div class="caption_v1">Parametry příkladů:</div>
       <form class="problem_form" method="post" action="/next_problem" enctype="multipart/form-data">
+       <input type="hidden" name="@_RESERVED__PROBLEM_CATEGORY_@" value="@_RESERVED__PROBLEM_CATEGORY_VAL_@">
        <div class="form_line">
          <label class="form_label_ac_param" for="@_PARAMETER_NO_BOTH_SIMPLIFICATIONS_@">Pouze příklady, kde nelze zanedbat oba jevy:</label>
          <input type="checkbox" name="@_PARAMETER_NO_BOTH_SIMPLIFICATIONS_@" value="True" @_PARAMETER_NO_BOTH_SIMPLIFICATIONS_CHECKED_@ />
index 86852e2258f5816f3425e518d58718de81902375..6600b3415fb0a0e7686330c2ef35e1b70ae08da2 100644 (file)
@@ -1,4 +1,5 @@
       <form class="problem_form" method="post" action="/next_problem" enctype="multipart/form-data">
+       <input type="hidden" name="@_RESERVED__PROBLEM_CATEGORY_@" value="@_RESERVED__PROBLEM_CATEGORY_VAL_@">
        <div class="form_line">
          <label class="form_label_sol_param" for="@_PARAMETER_IONIC_STRENGTH_@">Uvažovat iontovou sílu:</label>
          <input type="checkbox" name="@_PARAMETER_IONIC_STRENGTH_@" value="True" @_PARAMETER_IONIC_STRENGTH_CHECKED_@ />
index c1fec58bc855091010610c588f9f1728480106bb..5b2c8e3bee99469cfd155b1a8b26ade0505c51fd 100644 (file)
@@ -1,4 +1,5 @@
       <form class="problem_form" method="post" action="/check_answer" enctype="multipart/form-data">
+       <input type="hidden" name="@_RESERVED__PROBLEM_ID_@" value="@_RESERVED__PROBLEM_ID_VAL_@">
        <div class="form_line">
          <label class="form_label_sol" for="ANSWER_NUM">Výsledek:</label>
          <input class="form_input_sol" type="text" name="ANSWER_NUM" maxlength="128" value="" />
index 0f9c75108a370c05ff0328e7cddd22bdbf64acc1..8297c821812a0e5eda73523bbd2bb1f0181d0af7 100644 (file)
@@ -22,17 +22,19 @@ package body Face_Generator is
 
   function Generate_Face(Assignment: in Problem_Generator_Syswides.Assignment_Info.Map; Parameters:
                         in Problem_Generator_Syswides.Parameters_Info.Map;
-                        HTML: out HTML_Code) return Boolean is
+                        HTML: out HTML_Code;
+                        Pr_ID: in String; Pr_Cat: in String) return Boolean is
   begin
     return Generate_Face_With_Answer(Assignment => Assignment, Parameters => Parameters, HTML => HTML,
-                                    Answer_Message => To_UB_Text(""), Answer_Code => Problem_Generator_Syswides.Invalid_Answer);
+                                    Answer_Message => To_UB_Text(""), Answer_Code => Problem_Generator_Syswides.Invalid_Answer,
+                                    Pr_ID => Pr_ID, Pr_Cat => Pr_Cat);
   end Generate_Face;
 
   function Generate_Face_With_Answer(Assignment: in Problem_Generator_Syswides.Assignment_Info.Map;
                                     Answer_Message: in UB_Text;
                                     Answer_Code: in Problem_Generator_Syswides.Answer_RetCode;
                                     Parameters: in Problem_Generator_Syswides.Parameters_Info.Map;
-                                    HTML: out HTML_Code) return Boolean is
+                                    HTML: out HTML_Code; Pr_ID: in String; Pr_Cat: in String) return Boolean is
     use Problem_Generator_Syswides;
     use Problem_Generator_Syswides.Assignment_Info;
 
@@ -45,9 +47,9 @@ package body Face_Generator is
       Problem_Type_Str: constant String := Assignment.Element(PROBLEM_TYPE_KEY);
     begin
       if Problem_Type_Str = PROBLEM_TYPE_ACIDOBAZIC then
-        return Generate_Face_Acidobazic(Assignment, Answer_Message, Answer_Code, Parameters, HTML);
+        return Generate_Face_Acidobazic(Assignment, Answer_Message, Answer_Code, Parameters, HTML, Pr_ID, Pr_Cat);
       elsif Problem_Type_Str = PROBLEM_TYPE_SOLUBILITY then
-        return Generate_Face_Solubility(Assignment, Answer_Message, Answer_Code, Parameters, HTML);
+        return Generate_Face_Solubility(Assignment, Answer_Message, Answer_Code, Parameters, HTML, Pr_ID, Pr_Cat);
       else
        return False;
       end if;
@@ -84,7 +86,7 @@ package body Face_Generator is
                                    Answer_Message: in UB_Text;
                                    Answer_Code: in Problem_Generator_Syswides.Answer_RetCode;
                                    Parameters: in Problem_Generator_Syswides.Parameters_Info.Map;
-                                   HTML: out HTML_Code) return Boolean is
+                                   HTML: out HTML_Code; Pr_ID: in String; Pr_Cat: in String) return Boolean is
     use AWS.Templates;
     use Problem_Generator_Syswides;
     use Problem_Generator_Syswides.Assignment_Info;
@@ -102,6 +104,11 @@ package body Face_Generator is
     Temp := Parse(Filename => "scripts/expand_collapse.js", Cached => True);
     Append_HTML(Source => HTML, New_Item => Temp);
 
+    -- Mandatory hidden parameters
+    Insert(Translations, Assoc(RESERVED_PROBLEM_ID_KEY, RESERVED_PROBLEM_ID_KEY));
+    Insert(Translations, Assoc(RESERVED_PROBLEM_ID_VAL_KEY, Pr_ID));
+    Insert(Translations, Assoc(RESERVED_PROBLEM_CATEGORY_KEY, RESERVED_PROBLEM_CATEGORY_KEY));
+    Insert(Translations, Assoc(RESERVED_PROBLEM_CATEGORY_VAL_KEY, Pr_Cat));
     Insert(Translations, Assoc(Acidobazic_Suite.CONCENTRATION_INT_KEY, Assignment.Element(Acidobazic_Suite.CONCENTRATION_INT_KEY)));
     Insert(Translations, Assoc(Acidobazic_Suite.CONCENTRATION_DEC_KEY, Assignment.Element(Acidobazic_Suite.CONCENTRATION_DEC_KEY)));
     Insert(Translations, Assoc(Acidobazic_Suite.CONCENTRATION_EXP_KEY, Assignment.Element(Acidobazic_Suite.CONCENTRATION_EXP_KEY)));
@@ -146,7 +153,7 @@ package body Face_Generator is
                                    Answer_Message: in UB_Text;
                                    Answer_Code: in Problem_Generator_Syswides.Answer_RetCode;
                                    Parameters: in Problem_Generator_Syswides.Parameters_Info.Map;
-                                   HTML: out HTML_Code) return Boolean is
+                                   HTML: out HTML_Code; Pr_ID: in String; Pr_Cat: in String) return Boolean is
     use Ada.Strings.Unbounded;
     use AWS.Templates;
     use Problem_Generator_Syswides;
@@ -155,6 +162,7 @@ package body Face_Generator is
 
     Translations_Hdr: Translate_Set;
     Translations_Params: Translate_Set;
+    Translations_Submit: Translate_Set;
     Translations: Translate_Set;
     Temp: HTML_Code;
     Params_Code: HTML_Code;
@@ -171,10 +179,19 @@ package body Face_Generator is
 
     Add_Answer_Section(Translations, Answer_Message, Answer_Code);
 
-    Temp := Parse(Filename => "templates/face_solubility_submit.html", Cached => True);
+    -- Add submit section
+    -- - Mandatory parameters
+    Insert(Translations_Submit, Assoc(RESERVED_PROBLEM_ID_KEY, Pr_ID));
+    Insert(Translations_Submit, Assoc(RESERVED_PROBLEM_ID_VAL_KEY, Pr_ID));
+    --
+    Temp := Parse(Filename => "templates/face_solubility_submit.html", Translations => Translations_Submit);
     Insert(Translations, Assoc("SUBMIT_FORM", HTML_To_Fixed_String(Temp)));
 
     -- Add parameters section
+    -- - Mandatory parameters
+    Insert(Translations_Params, Assoc(RESERVED_PROBLEM_CATEGORY_KEY, Pr_Cat));
+    Insert(Translations, Assoc(RESERVED_PROBLEM_CATEGORY_VAL_KEY, Pr_Cat));
+    --
     Insert(Translations_Params, Assoc(Solubility_Suite.PARAMETER_IONIC_STRENGTH_KEY, Solubility_Suite.PARAMETER_IONIC_STRENGTH_KEY));
     if Parameters.Find(Solubility_Suite.PARAMETER_IONIC_STRENGTH_KEY) /= Parameters_Info.No_Element then
       Insert(Translations_Params, Assoc("PARAMETER_IONIC_STRENGTH_CHECKED", "checked=""checked"""));
index a758854a635bb23f89a14a8cd09bb139d238de53..9db16ca1dbe799f219a9c2f9ed8e06058f91cb4b 100644 (file)
@@ -8,13 +8,15 @@ package Face_Generator is
 
   function Generate_Face(Assignment: in Problem_Generator_Syswides.Assignment_Info.Map;
                         Parameters: in Problem_Generator_Syswides.Parameters_Info.Map;
-                        HTML: out HTML_Code) return Boolean;
+                        HTML: out HTML_Code;
+                        Pr_ID: in String; Pr_Cat: in String) return Boolean;
 
   function Generate_Face_With_Answer(Assignment: in Problem_Generator_Syswides.Assignment_Info.Map;
                                     Answer_Message: in UB_Text;
                                     Answer_Code: in Problem_Generator_Syswides.Answer_RetCode;
                                     Parameters: in Problem_Generator_Syswides.Parameters_Info.Map;
-                                    HTML: out HTML_Code) return Boolean;
+                                    HTML: out HTML_Code;
+                                    Pr_ID: in String; Pr_Cat: in String) return Boolean;
 
 private
   procedure Add_Answer_Section(Translations: in out AWS.Templates.Translate_Set; Answer_Message: in UB_Text;
@@ -24,13 +26,15 @@ private
                                    Answer_Message: in UB_Text;
                                    Answer_Code: in Problem_Generator_Syswides.Answer_RetCode;
                                    Parameters: in Problem_Generator_Syswides.Parameters_Info.Map;
-                                   HTML: out HTML_Code) return Boolean;
+                                   HTML: out HTML_Code;
+                                   Pr_ID: in String; Pr_Cat: in String) return Boolean;
 
   function Generate_Face_Solubility(Assignment: in Problem_Generator_Syswides.Assignment_Info.Map;
                                    Answer_Message: in UB_Text;
                                    Answer_Code: in Problem_Generator_Syswides.Answer_RetCode;
                                    Parameters: in Problem_Generator_Syswides.Parameters_Info.Map;
-                                   HTML: out HTML_Code) return Boolean;
+                                   HTML: out HTML_Code;
+                                   Pr_ID: in String; Pr_Cat: in String) return Boolean;
 
   HEADER_CAPTION_KEY: constant String := "HEADER_CAPTION";
   HINTS_SECTION_KEY: constant String := "HINTS_SECTION";
index 8163dde5495ceb206bf6f0920d5d1240b3ab5856..24643864a9a1fb20344d1e049f64916a81b542fb 100644 (file)
@@ -29,7 +29,6 @@ package body Formatting_Helpers is
   end Get_Integer_Part_Int;
 
   function String_To_Float(S: in String) return FH_Float is
-    F: FH_Float;
     Idx: Natural;
     SS: String := S;
   begin
index 2480115d620897498b67c0e565d32fad187ae36d..e4c067c4847a89e6f775b76deea69b0d3f4daf76 100644 (file)
@@ -1,38 +1,58 @@
 package body Global_Types is
 
   procedure Append_HTML(Source: in out HTML_Code; New_Item: in HTML_Code) is
+    use Ada.Strings.Unbounded;
   begin
-    Ada.Strings.Unbounded.Append(Source => Source, New_Item => New_Item);
+    Append(Source => Source, New_Item => New_Item);
   end Append_HTML;
 
   procedure Append_UB_Text(Source: in out UB_Text; New_Item: in UB_Text) is
+    use Ada.Strings.Unbounded;
   begin
-    Ada.Strings.Unbounded.Append(Source => Source, New_Item => New_Item);
+    Append(Source => Source, New_Item => New_Item);
   end Append_UB_Text;
 
   procedure Append_UB_Text(Source: in out UB_Text; New_Item: in String) is
+    use Ada.Strings.Unbounded;
   begin
-    Ada.Strings.Unbounded.Append(Source => Source, New_Item => To_UB_Text(New_Item));
+    Append(Source => Source, New_Item => To_Unbounded_String(New_Item));
   end Append_UB_Text;
 
   function HTML_To_Fixed_String(HTML: in HTML_Code) return String is
+    use Ada.Strings.Unbounded;
   begin
-    return Ada.Strings.Unbounded.To_String(HTML);
+    return To_String(HTML);
   end HTML_To_Fixed_String;
 
   function To_HTML_Code(S: in String) return HTML_Code is
+    use Ada.Strings.Unbounded;
   begin
-    return Ada.Strings.Unbounded.To_Unbounded_String(S);
+    return To_Unbounded_String(S);
   end To_HTML_Code;
 
   function To_UB_Text(S: in String) return UB_Text is
+    use Ada.Strings.Unbounded;
   begin
-    return Ada.Strings.Unbounded.To_Unbounded_String(S);
+    return To_Unbounded_String(S);
   end To_UB_Text;
 
   function UB_Text_To_Fixed_String(Text: in UB_Text) return String is
+    use Ada.Strings.Unbounded;
   begin
-    return Ada.Strings.Unbounded.To_String(Text);
+    return To_String(Text);
   end UB_Text_To_Fixed_String;
 
+
+  protected body Simple_Mutex is
+    entry Lock when Locked = False is
+    begin
+      Locked := True;
+    end Lock; 
+
+    entry Unlock when Locked is
+    begin
+      Locked := False;
+    end Unlock;
+  end Simple_Mutex;
+
 end Global_Types;
index 7e42f3916ac13aba031da07ef3d908ed4f8e6500..adfca9cbb245195a7e31a268cb76ca8244ddf4ec 100644 (file)
@@ -3,7 +3,8 @@ with Ada.Strings.Unbounded;
 
 package Global_Types is
 
-  subtype Unique_ID is Ada.Containers.Count_Type;
+  type Problem_ID is new Ada.Containers.Count_Type;
+  type Unique_ID is new Ada.Containers.Count_Type;
   subtype HTML_Code is Ada.Strings.Unbounded.Unbounded_String;
   subtype UB_Text is Ada.Strings.Unbounded.Unbounded_String;
 
@@ -15,4 +16,12 @@ package Global_Types is
   function To_UB_Text(S: in String) return UB_Text;
   function UB_Text_To_Fixed_String(Text: in UB_Text) return String;
 
+  protected type Simple_Mutex is
+    entry Lock;
+    entry Unlock;
+  private
+    Locked: Boolean := False;
+  end Simple_Mutex;
+  type Simple_Mutex_All_Access is access all Simple_Mutex;
+
 end Global_Types;
index af41bb8a0c2c0d7405a0fcbc8b35b4c889c02516..386f1212d310c1c11767938153c9d1a3d02b4d03 100644 (file)
@@ -33,6 +33,7 @@ package body Handler_Check_Answer is
 
          POST_Data: constant AWS.Parameters.List := AWS.Status.Parameters(Request);
          Answer: Answer_Info.Map;
+         Pr_ID: Problem_ID;
          Success: Boolean;
        begin
          -- Get UID
@@ -55,7 +56,27 @@ package body Handler_Check_Answer is
            end;
          end loop;
 
-         Success := Problem_Manager.Display_Checked_Answer(UID, Answer, HTML);
+         -- Get problem ID
+         if Answer.Find(Problem_Generator_Syswides.RESERVED_PROBLEM_ID_KEY) = Answer_Info.No_Element then
+           Ada.Text_IO.Put_Line("Problem ID not found!");
+           return AWS.Response.URL(Location => "/");
+         end if;
+
+         begin
+           Pr_ID := Problem_ID'Value(Answer.Element(Problem_Generator_Syswides.RESERVED_PROBLEM_ID_KEY));
+           Ada.Text_IO.Put_Line("Got problem ID " & Problem_ID'Image(Pr_ID));
+         exception
+           when Constraint_Error =>
+             Ada.Text_IO.Put_Line("Invalid problem ID value");
+             return AWS.Response.URL(Location => "/");
+         end;
+
+         Success := Problem_Manager.Display_Checked_Answer(UID, Answer, HTML, Pr_ID);
+         if Success = False then
+           Ada.Text_IO.Put_Line("No such problem in storage");
+           return AWS.Response.URL(Location => "/");
+         end if;
+
          return AWS.Response.Build(Content_Type => AWS.MIME.Text_HTML,
                                    Message_Body => HTML_To_Fixed_String(HTML),
                                    Status_Code => AWS.Messages.S200);
index 18675dcce34126630690492a97462afedb25002b..84119aede7b4d3aec78c29c63c321e978f913ad4 100644 (file)
@@ -6,6 +6,8 @@ with AWS.Response;
 with AWS.Session;
 with AWS.Status;
 
+with Ada.Text_IO;
+
 with Global_Types;
 with Problem_Generator_Syswides;
 with Problem_Manager;
@@ -52,16 +54,31 @@ package body Handler_Next_Problem is
            end;
          end loop;
 
-         -- Display new problem
-         Success := Problem_Manager.Display_Next_Assignment(UID, Problem_Parameters, HTML);
-         if Success = False then
-           -- TODO: Handle error in a less reckless manner
+         -- Create a new problem
+         if Problem_Parameters.Find(Problem_Generator_Syswides.RESERVED_PROBLEM_CATEGORY_KEY) = Parameters_Info.No_Element then
+           Ada.Text_IO.Put_Line("No problem category tag");
            return AWS.Response.URL(Location => "/");
          end if;
 
-         return AWS.Response.Build(Content_Type => AWS.MIME.Text_HTML,
-                                   Message_Body => HTML_To_Fixed_String(HTML),
-                                   Status_Code => AWS.Messages.S200);
+         declare
+           Raw_P_Cat: constant String := Problem_Parameters.Element(Problem_Generator_Syswides.RESERVED_PROBLEM_CATEGORY_KEY);
+         begin
+           Success := Problem_Manager.Prepare_Problem(UID, Raw_P_Cat, Problem_Parameters);
+           if Success = False then
+             -- TODO: Handle error in a less reckless manner
+             return AWS.Response.URL(Location => "/");
+           end if;
+           -- Display new problem
+           Success := Problem_Manager.Display_Assignment(UID, HTML);
+           if Success = False then
+             -- TODO: Handle error in a less reckless manner
+             return AWS.Response.URL(Location => "/");
+           end if;
+
+           return AWS.Response.Build(Content_Type => AWS.MIME.Text_HTML,
+                                     Message_Body => HTML_To_Fixed_String(HTML),
+                                     Status_Code => AWS.Messages.S200);
+         end;
        end;
       when others =>
        return AWS.Response.URL(Location => "/");
index cd7008e4eb4b8532b5f0976899ae0e2943109a78..976b640b1b0fd9f83f333d892846a612ae14bd3f 100644 (file)
@@ -24,18 +24,9 @@ package body Handler_Start is
       when AWS.Status.GET =>
        declare
          Raw_Problem_Category: constant String := AWS.Status.Parameter(Request, "problem_category");
-         P_Cat: Problem_Manager.Problem_Category;
          UID: Unique_ID;
          Success: Boolean;
-       begin
-         if Raw_Problem_Category = Problem_Manager.Problem_Category'Image(Problem_Manager.Acidobazic) then
-           P_Cat := Problem_Manager.Acidobazic;
-         elsif Raw_Problem_Category = Problem_Manager.Problem_Category'Image(Problem_Manager.Solubility) then
-           P_Cat := Problem_Manager.Solubility;
-         else
-           return AWS.Response.URL(Location => "/");
-         end if;
-
+       begin   
          -- Register new UID if necessary and create a first problem
          Success := Problem_Manager.Get_UID(Raw_UID, UID);
          if Success = False then
@@ -52,14 +43,14 @@ package body Handler_Start is
          end if;
 
          -- We're all set, create a new problem
-         Success := Problem_Manager.Prepare_Problem(UID, P_Cat);
+         Success := Problem_Manager.Prepare_Problem(UID, Raw_Problem_Category);
          if Success = False then
            -- Something went wrong when generating the problem
            -- TODO: Print some sensible error message, for now just redirect to index
            return AWS.Response.URL(Location => "/");
          end if;
 
-         Success := Problem_Manager.Display_New_Assignment(UID, HTML);
+         Success := Problem_Manager.Display_Assignment(UID, HTML);
          if Success = False then
            HTML := To_HTML_Code("Cannot display assignment");
          end if;
index a13a9701407675781c16197be7976dd42b24e8db..688f18f4f1ae6f3f4693616890aae20d54f37683 100644 (file)
@@ -15,8 +15,8 @@ begin
   AWS.Config.Set.Server_Host(Server_Config, "localhost");
   AWS.Config.Set.Server_Port(Server_Config, 18400);
 
-  AWS.Config.Set.Session_Lifetime(1800.0);
-  AWS.Config.Set.Session_Cleanup_Interval(900.0);
+  AWS.Config.Set.Session_Lifetime(90.0);
+  AWS.Config.Set.Session_Cleanup_Interval(180.0);
 
   Ada.Text_IO.Put_Line("Starting server...");
   AWS.Server.Start(Web_Server => Web_Server,
index 84be0634871e113553f0a5b6faa6c27d32dea9c0..a7db2031e336ea1e3f47b5738248d598a5783baf 100644 (file)
@@ -12,13 +12,9 @@ package body Solubility_Suite is
   -- BEGIN: Inherited funcions
   function Create return access Solubility_Problem is
     Problem: access Solubility_Problem;
-    Parameters: Solubility_Parameters;
   begin
-    Parameters := (Ionic_Strength => False,
-                  P_Subtype => V_FROM_G_KS);
-
     Problem := new Solubility_Problem;
-    Problem.Parameters := Parameters;
+    Problem.Parameters := Solubility_Parameters'(Ionic_Strength => False, P_Subtype => V_FROM_G_KS);
     return Problem;
   end;
 
@@ -198,7 +194,6 @@ package body Solubility_Suite is
   begin
     Auto_Lock.Init(Guard, Problem.Mutex'Unchecked_Access);
     Guard.Lock;
-    -- Generate MW
     Reset(Gen => Float_RGen);
 
     -- Generate stochiometry of the molecul
@@ -229,7 +224,7 @@ package body Solubility_Suite is
     case Problem.Parameters.P_Subtype is
       when V_FROM_G_KS =>
        declare
-         MOLECULE_WEIGHT_RANGE: constant SS_Float := MOLECULE_WEIGHT_MAX - MOLECULE_WEIGHT_MIN;
+         MOLAR_WEIGHT_RANGE: constant SS_Float := MOLAR_WEIGHT_MAX - MOLAR_WEIGHT_MIN;
          SAMPLE_WEIGHT_RANGE: constant SS_Float := SAMPLE_WEIGHT_MAX - SAMPLE_WEIGHT_MIN;
          G: SS_Float;  -- Sample weight in grams
          Ks: SS_Float; -- Solubility product
@@ -239,7 +234,7 @@ package body Solubility_Suite is
          G := (SS_Float(Random(Gen => Float_RGen)) * SAMPLE_WEIGHT_RANGE) + SAMPLE_WEIGHT_MIN;
          G := Round_To_Valid_Nums(G, DECIMALS);
          Ks := Round_To_Valid_Nums(Generate_Solubility_Product, DECIMALS);
-         MW := (SS_Float(Random(Gen => Float_RGen)) * MOLECULE_WEIGHT_RANGE) + MOLECULE_WEIGHT_MIN;
+         MW := (SS_Float(Random(Gen => Float_RGen)) * MOLAR_WEIGHT_RANGE) + MOLAR_WEIGHT_MIN;
          MW := Round_To_Valid_Nums(MW, DECIMALS_MW);
          Prob_Data := (Option => V_FROM_G_KS,
                        M => SS_Float(M), N => SS_Float(N),
@@ -248,7 +243,7 @@ package body Solubility_Suite is
        end;
       when KS_FROM_G_V =>
        declare
-         MOLECULE_WEIGHT_RANGE: constant SS_Float := MOLECULE_WEIGHT_MAX - MOLECULE_WEIGHT_MIN;
+         MOLAR_WEIGHT_RANGE: constant SS_Float := MOLAR_WEIGHT_MAX - MOLAR_WEIGHT_MIN;
          SAMPLE_WEIGHT_RANGE: constant SS_Float := SAMPLE_WEIGHT_MAX - SAMPLE_WEIGHT_MIN;
          G: SS_Float;  -- Sample weight in grams
          MW: SS_Float; -- Molar mass
@@ -256,7 +251,7 @@ package body Solubility_Suite is
        begin
          G := (SS_Float(Random(Gen => Float_RGen)) * SAMPLE_WEIGHT_RANGE) + SAMPLE_WEIGHT_MIN;
          G := Round_To_Valid_Nums(G, DECIMALS);
-         MW := (SS_Float(Random(Gen => Float_RGen)) * MOLECULE_WEIGHT_RANGE) + MOLECULE_WEIGHT_MIN;
+         MW := (SS_Float(Random(Gen => Float_RGen)) * MOLAR_WEIGHT_RANGE) + MOLAR_WEIGHT_MIN;
          MW := Round_To_Valid_Nums(MW, DECIMALS_MW);
          V := Round_To_Valid_Nums(Generate_Sample_Volume, DECIMALS);
          Prob_Data := (Option => KS_FROM_G_V,
index 97c3d4803dc9379880c34ecac71bd1d8f50c9456..672f87fa71a63fb995a99fd67c2a445555dd9103 100644 (file)
@@ -1,19 +1,7 @@
 package body Problem_Generator is
 
-  protected body Problem_Mutex is
-    entry Lock when Locked = False is
-    begin
-      Locked := True;
-    end Lock; 
-
-    entry Unlock when Locked is
-    begin
-      Locked := False;
-    end Unlock;
-  end Problem_Mutex;
-
   package body Auto_Lock is
-    procedure Init(This: in out LC; Mutex: Problem_Mutex_All_Access; Auto_Unlock: in Boolean := True) is
+    procedure Init(This: in out LC; Mutex: Simple_Mutex_All_Access; Auto_Unlock: in Boolean := True) is
     begin
       This.Auto_Unlock := Auto_Unlock;
       This.Mutex := Mutex;
index 24928325480cc74fccc7acb461e1115edf9b662e..63bb6a957aa33d12912b457dc165ce6f179658ac 100644 (file)
@@ -5,16 +5,6 @@ with Ada.Finalization;
 use Global_Types;
 use Problem_Generator_Syswides;
 package Problem_Generator is
-
-  protected type Problem_Mutex is
-    entry Lock;
-    entry Unlock;
-
-  private
-    Locked: Boolean := False;
-  end Problem_Mutex;
-  type Problem_Mutex_All_Access is access all Problem_Mutex;
-
   type Chem_Problem is abstract tagged limited private;
 
   function Create return access Chem_Problem is abstract;
@@ -29,23 +19,21 @@ package Problem_Generator is
 private
   type Chem_Problem is abstract tagged limited
     record
-      Mutex: aliased Problem_Mutex;
+      Mutex: aliased Simple_Mutex;
     end record;
 
   package Auto_Lock is
     type LC is limited new Ada.Finalization.Limited_Controlled with private;
-      procedure Init(This: in out LC; Mutex: Problem_Mutex_All_Access; Auto_Unlock: in Boolean := True);
+      procedure Init(This: in out LC; Mutex: Simple_Mutex_All_Access; Auto_Unlock: in Boolean := True);
       procedure Lock(This: in out LC);
       procedure Unlock(This: in out LC);
     private
       type LC is limited new Ada.Finalization.Limited_Controlled with
        record
          Auto_Unlock: Boolean;
-         Mutex: Problem_Mutex_All_Access;
+         Mutex: Simple_Mutex_All_Access;
        end record;
       overriding procedure Finalize(This: in out LC);
-
-    Pragma Volatile(LC);
   end Auto_Lock;
 
   package Acidobazic_Suite is
@@ -129,8 +117,8 @@ private
 
     ELECTROLYTE_CONCENTRATION_LOG_MAX: constant SS_Float := 0.176;
     ELECTROLYTE_CONCENTRATION_LOG_MIN: constant SS_Float := -4.0;
-    MOLECULE_WEIGHT_MAX: constant SS_Float := 450.0;
-    MOLECULE_WEIGHT_MIN: constant SS_Float := 45.0;
+    MOLAR_WEIGHT_MAX: constant SS_Float := 450.0;
+    MOLAR_WEIGHT_MIN: constant SS_Float := 45.0;
     PKS_MAX: constant SS_Float := 54.0;
     PKS_MIN: constant SS_Float := 10.0;
     SAMPLE_VOLUME_LOG_MAX: constant SS_Float := 10.0;
index f058304a06bffad76730f153a76f59e9a144c99a..3694316f833a3763d5a75a9b133fe33e11a23c90 100644 (file)
@@ -20,6 +20,11 @@ package Problem_Generator_Syswides is
   PROBLEM_TYPE_KEY: constant String := "PROBLEM_TYPE";
   PROBLEM_TYPE_ACIDOBAZIC: constant String := Problem_Type'Image(Acidobazic);
   PROBLEM_TYPE_SOLUBILITY: constant String := Problem_Type'Image(Solubility);
+  --
+  RESERVED_PROBLEM_ID_KEY: constant String := "RESERVED__PROBLEM_ID";
+    RESERVED_PROBLEM_ID_VAL_KEY: constant String := "RESERVED__PROBLEM_ID_VAL";
+  RESERVED_PROBLEM_CATEGORY_KEY: constant String := "RESERVED__PROBLEM_CATEGORY";
+    RESERVED_PROBLEM_CATEGORY_VAL_KEY: constant String := "RESERVED__PROBLEM_CATEGORY_VAL";
 
   package Acidobazic_Suite is
     -- What effect to ignore in calculations?
index 91d3fcf26ac2dff1059d9ae31b59b23b6165962f..34dd9508f93d601cf2893b9439e075e1a33ca9a4 100644 (file)
@@ -1,92 +1,76 @@
 with Ada.Text_IO;
+with Ada.Unchecked_Deallocation;
 with Face_Generator;
 
 package body Problem_Manager is
 
-  function Display_Checked_Answer(UID: in Unique_ID; Answer: in Problem_Generator_Syswides.Answer_Info.Map; HTML: out HTML_Code) return Boolean is
+  function Display_Checked_Answer(UID: in Unique_ID; Answer: in Problem_Generator_Syswides.Answer_Info.Map; HTML: out HTML_Code; Pr_ID: in Problem_ID) return Boolean is
     Answer_Message: UB_Text;
     ARC: Problem_Generator_Syswides.Answer_RetCode;
     Assignment: Problem_Generator_Syswides.Assignment_Info.Map;
     Parameters: Problem_Generator_Syswides.Parameters_Info.Map;
+    Pr_Cat: Problem_Category;
     Success: Boolean;
-    USD: User_Session_Data_Access;
+    Stored: Stored_Problem_All_Access;
   begin
-    USD := Active_Sessions.Get_Session_Data(UID, Success);
-    if Success = False then
+    Stored := Active_Sessions.Get_Problem(UID, Pr_ID);
+    if Stored = null then
       return False;
     end if;
 
-    Success := USD.Problem.Get_Parameters(Parameters);
+    Success := Stored.Problem.Get_Parameters(Parameters);
     if Success = False then
       -- TODO: Handle error in a better way
+      Stored.Mutex.Unlock;
       return False;
     end if;
-    Success := USD.Problem.Get_Assignment(Assignment);
+    Success := Stored.Problem.Get_Assignment(Assignment);
     if Success = False then
       -- TODO: Handle error in a better way
+      Stored.Mutex.Unlock;
       return False;
     end if;
 
-    ARC := USD.Problem.Check_Answer(Answer, Answer_Message);
+    Ada.Text_IO.Put_Line("Checking problem ID " & Problem_ID'Image(Pr_ID));
+    ARC := Stored.Problem.Check_Answer(Answer, Answer_Message);
+    Pr_Cat := Stored.Category;
+    Stored.Mutex.Unlock;
     return Face_Generator.Generate_Face_With_Answer(Assignment => Assignment, Answer_Message => Answer_Message,
                                                    Answer_Code => ARC, HTML => HTML,
-                                                   Parameters => Parameters);
+                                                   Parameters => Parameters, Pr_ID => Problem_ID'Image(Pr_ID), Pr_Cat => Problem_Category'Image(Pr_Cat));
   end Display_Checked_Answer;
 
-  function Display_New_Assignment(UID: in Unique_ID; HTML: out HTML_Code) return Boolean is
+  function Display_Assignment(UID: in Unique_ID; HTML: out HTML_Code) return Boolean is
     Assignment: Problem_Generator_Syswides.Assignment_Info.Map;
     Parameters: Problem_Generator_Syswides.Parameters_Info.Map;
-    USD: User_Session_Data_Access;
+    Stored: Stored_Problem_All_Access;
+    Pr_ID: Problem_ID;
+    Pr_Cat: Problem_Category;
     Success: Boolean;
   begin
-    USD := Active_Sessions.Get_Session_Data(UID, Success);
-    if Success = False then
+    Stored := Active_Sessions.Get_Latest_Problem_With_ID(UID, Pr_ID);
+    if Stored = null then
       return False;
     end if;
 
-    -- Get default problem parameters
-    Success := USD.Problem.Get_Parameters(Parameters);
+    Success := Stored.Problem.Get_Parameters(Parameters);
     if Success = False then
+      Stored.Mutex.Unlock;
       return False;
     end if;
 
     -- Get assignment
-    Success := USD.Problem.Get_Assignment(Assignment);
-    if Success = False then
-      return False;
-    end if;
-
-    return Face_Generator.Generate_Face(Assignment => Assignment, HTML => HTML, Parameters => Parameters);
-  end Display_New_Assignment;
-
-  function Display_Next_Assignment(UID: in Unique_ID; Problem_Parameters: in Problem_Generator_Syswides.Parameters_Info.Map;
-                                  HTML: out HTML_Code) return Boolean is
-    Assignment: Problem_Generator_Syswides.Assignment_Info.Map;
-    Success: Boolean;
-    USD: User_Session_Data_Access;
-  begin
-    USD := Active_Sessions.Get_Session_Data(UID, Success);
+    Success := Stored.Problem.Get_Assignment(Assignment);
     if Success = False then
+      Stored.Mutex.Unlock;
       return False;
     end if;
 
-    -- Set parameters before creating a new problem
-    Success := USD.Problem.Set_Parameters(Problem_Parameters);
-    if Success = False then
-      -- TODO: Handle error
-      return False;
-    end if;
-
-    -- Create new problem and display it
-    USD.Problem.New_Problem;
-    Success := USD.Problem.Get_Assignment(Assignment);
-    if Success = False then
-      -- TODO: Handle error in a better way
-      return False;
-    end if;
-
-    return Face_Generator.Generate_Face(Assignment => Assignment, HTML => HTML, Parameters => Problem_Parameters);
-  end Display_Next_Assignment;
+    Pr_Cat := Stored.Category;
+    Stored.Mutex.Unlock;
+    return Face_Generator.Generate_Face(Assignment => Assignment, HTML => HTML, Parameters => Parameters, Pr_ID => Problem_ID'Image(Pr_ID),
+                                       Pr_Cat => Problem_Category'Image(Pr_Cat));
+  end Display_Assignment;
 
   function Get_UID(Raw_UID: in String; UID: out Unique_ID) return Boolean is
   begin
@@ -99,32 +83,59 @@ package body Problem_Manager is
     end;
   end Get_UID;
 
-  function Prepare_Problem(UID: in Unique_ID; P_Cat: in Problem_Category) return Boolean is
-    New_USD: User_Session_Data_Access;
+  function Prepare_Problem(UID: in Unique_ID; Raw_P_Cat: in String;
+                          Parameters: in Problem_Generator_Syswides.Parameters_Info.Map := Problem_Generator_Syswides.Parameters_Info.Empty_Map) return Boolean is
+    Problem: Chem_Problem_All_Access;
+    Storage: Stored_Problem_All_Access;
+    P_Cat: Problem_Category;
     Success: Boolean;
   begin
-    New_USD := new User_Session_Data;
-    New_USD.P_Cat := P_Cat;
+    if Raw_P_Cat = Problem_Manager.Problem_Category'Image(Problem_Manager.Acidobazic) then
+      P_Cat := Problem_Manager.Acidobazic;
+    elsif Raw_P_Cat = Problem_Manager.Problem_Category'Image(Problem_Manager.Solubility) then
+      P_Cat := Problem_Manager.Solubility;
+    else
+      return False;
+    end if;
 
     case P_Cat is
       when Acidobazic =>
-       New_USD.Problem := Problem_Generator.Get_Problem(Problem_Generator_Syswides.Acidobazic);
+       Problem := Problem_Generator.Get_Problem(Problem_Generator_Syswides.Acidobazic);
       when Solubility =>
-       New_USD.Problem := Problem_Generator.Get_Problem(Problem_Generator_Syswides.Solubility);
+       Problem := Problem_Generator.Get_Problem(Problem_Generator_Syswides.Solubility);
       when others =>
        return False;
     end case;
 
-    New_USD.Problem.New_Problem;
+    if Problem = null then
+      return False;
+    end if;
+    -- Initialize problem
+    if Parameters.Is_Empty = False then
+      Success := Problem.Set_Parameters(Parameters);
+      Ada.Text_IO.Put_Line("Setting problem parameters");
+      if Success = False then
+       Free_Chem_Problem(Problem);
+       return False;
+      end if;
+    end if;
+    Problem.New_Problem;
+
+    Storage := new Stored_Problem;
+    Storage.Problem := Problem;
+    Storage.Category := P_Cat;
+    Active_Sessions.Add_Problem(UID, Storage, Success);
 
-    Active_Sessions.Set_Session_Data(UID, New_USD, Success);
-    return Success;
+    if Success = False then
+      Free_Chem_Problem(Problem);
+      Free_Stored_Problem(Storage);
+    end if;
+    return True;
   end Prepare_Problem;
 
   function Register_UID(UID: out Unique_ID) return Boolean is
     Success: Boolean;
   begin
-    -- Raw UID is empty, assuming a new session
     Active_Sessions.Register_UID(UID, Success);
     return Success;
   end Register_UID;
@@ -136,17 +147,75 @@ package body Problem_Manager is
   begin
     Success := Get_UID(Raw_UID, UID);
     if Success = False then
-      Ada.Text_IO.Put_Line("Session expired: No such session");
       return;
     end if;
 
     Active_Sessions.Remove_Session(UID);
-    Ada.Text_IO.Put_Line("Session expired: Session " & Raw_UID & " data deleted");
   end Session_Expired;
 
   -- BEGIN: Private functions
+
+  procedure Free_Chem_Problem(Problem: in out Chem_Problem_All_Access) is
+    procedure Free_Chem_Problem_Internal is new Ada.Unchecked_Deallocation(Object => Problem_Generator.Chem_Problem'Class, Name => Chem_Problem_All_Access);
+  begin
+    Free_Chem_Problem_Internal(Problem);
+  end Free_Chem_Problem;
+
+  procedure Free_Stored_Problem(Problem: in out Stored_Problem_All_Access) is
+    procedure Free_Stored_Problem_Internal is new Ada.Unchecked_Deallocation(Object => Stored_Problem, Name => Stored_Problem_All_Access);
+  begin
+    Free_Stored_Problem_Internal(Problem);
+  end Free_Stored_Problem;
+
   protected body Active_Sessions is
 
+    procedure Add_Problem(UID: in Unique_ID; Problem: in Stored_Problem_All_Access; Success: out Boolean) is
+      use Problem_Storage;
+      use Session_Keeping;
+      use Ada.Containers;
+
+      C: Problem_Storage.Cursor;
+      USD: User_Session_All_Access;
+    begin
+      if Sessions.Find(UID) = Session_Keeping.No_Element then
+       Success := False;
+       return;
+      end if;
+      USD := Sessions.Element(UID);
+
+      USD.Problems.Insert(USD.Last_Problem_ID, Problem, C, Success);
+      if Success = False then
+       return;
+      end if;
+
+      USD.Last_Problem_ID := USD.Last_Problem_ID + 1;
+      -- Delete old problems
+      declare
+       Del_ID: Problem_ID;
+      begin
+       if USD.Last_Problem_ID - 1 <= MAX_STORED_PROBLEMS then
+         Del_ID := Problem_ID'Last - (MAX_STORED_PROBLEMS - (USD.Last_Problem_ID - 1));
+       else
+         Del_ID := (USD.Last_Problem_ID - 1) - MAX_STORED_PROBLEMS;
+       end if;
+
+       if USD.Problems.Find(Del_ID) /= Problem_Storage.No_Element then
+         declare
+           SP: Stored_Problem_All_Access := USD.Problems(Del_ID);
+         begin
+           -- Delete pointer to the problem, grab a lock to make sure that the pointer is not used
+           -- anywhere else while we are deleting it
+           SP.Mutex.Lock;
+           Free_Chem_Problem(SP.Problem);
+           SP.Mutex.Unlock;
+           Free_Stored_Problem(SP);
+           USD.Problems.Delete(Del_ID);
+         end;
+       end if;
+      end;
+      Success := True;
+    end Add_Problem;
+
     function Contains(UID: in Unique_ID) return Boolean is
       use Session_Keeping;
     begin
@@ -161,11 +230,18 @@ package body Problem_Manager is
       use Session_Keeping;
 
       C: Session_Keeping.Cursor;
+      NUSD: User_Session_All_Access;
     begin
       Success := False;
       if Sessions.Find(UID) = Session_Keeping.No_Element then
        -- We have a free slot
-       Sessions.Insert(UID, null, C, Success);
+       NUSD := new User_Session_Data;
+       if NUSD = null then
+         Success := False;
+         Stop := False;
+         return;
+       end if;
+       Sessions.Insert(UID, NUSD, C, Success);
        if Success then
          Last_UID := UID;
          -- Registration successful
@@ -181,28 +257,53 @@ package body Problem_Manager is
       Stop := False;
     end Check_Free_And_Register;
 
-    function Get_Session_Data(UID: in Unique_ID; Success: out Boolean) return User_Session_Data_Access is
+    procedure Free_Session_Data(Data: in out User_Session_All_Access) is
+      procedure Free_Session_Data_Internal is new Ada.Unchecked_Deallocation(Object => User_Session_Data, Name => User_Session_All_Access);
+    begin
+      Free_Session_Data_Internal(Data);
+    end Free_Session_Data; 
+    function Get_Problem(UID: in Unique_ID; Pr_ID: in Problem_ID) return Stored_Problem_All_Access is
+      use Problem_Storage;
       use Session_Keeping;
+      
+      USD: User_Session_All_Access;
+      S: Stored_Problem_All_Access;
     begin
       if Sessions.Find(UID) = Session_Keeping.No_Element then
-       Success := False;
        return null;
-      else
-       Success := True;
-       return Sessions.Element(UID);
       end if;
-    end Get_Session_Data;
+      USD := Sessions.Element(UID);
+
+      if USD.Problems.Find(Pr_ID) = Problem_Storage.No_Element then
+       return null;
+      end if;
 
-    procedure Set_Session_Data(UID: in Unique_ID; USD: in User_Session_Data_Access; Success: out Boolean) is
+
+      S := USD.Problems.Element(Pr_ID);
+      -- Check if the problem has not been deleted
+      if S = null then
+       return null;
+      end if;
+      if S.Problem = null then
+       return null;
+      end if;
+
+      S.Mutex.Lock;
+      return S;
+    end Get_Problem;
+
+    function Get_Latest_Problem_With_ID(UID: in Unique_ID; Pr_ID: out Problem_ID) return Stored_Problem_All_Access is
       use Session_Keeping;
+
     begin
       if Sessions.Find(UID) = Session_Keeping.No_Element then
-       Success := False;
-      else
-       Sessions.Replace(UID, USD);
-       Success := True;
+       return null;
       end if;
-    end Set_Session_Data;
+
+      Pr_ID := Sessions.Element(UID).Last_Problem_ID - 1;
+      return Get_Problem(UID, Pr_ID);
+    end Get_Latest_Problem_With_ID;
 
     procedure Register_UID(UID: out Unique_ID; Success: out Boolean) is
       Stop: Boolean;
@@ -230,8 +331,31 @@ package body Problem_Manager is
     end Register_UID;
 
     procedure Remove_Session(UID: in Unique_ID) is
+      use Problem_Storage;
       use Session_Keeping;
+
+      USD: User_Session_All_Access;
     begin
+      if Sessions.Find(UID) = Session_Keeping.No_Element then
+       return;
+      end if;
+
+      USD := Sessions.Element(UID);
+
+      while USD.Problems.Is_Empty = False loop
+       declare
+         SP: Stored_Problem_All_Access := USD.Problems.First_Element;
+       begin
+         SP.Mutex.Lock;
+         Free_Chem_Problem(SP.Problem);
+         SP.Mutex.Unlock;
+         Free_Stored_Problem(SP);
+         USD.Problems.Delete_First;
+       end;
+      end loop;
+
+      Free_Session_Data(USD);
+
       Sessions.Delete(UID);
     end Remove_Session;
 
index 57cfed52e85ee44185aad18c0bd0b114f623c920..542d73145958665e42180cafc56cd80b9c087e03 100644 (file)
@@ -9,38 +9,57 @@ package Problem_Manager is
 
   type Problem_Category is (Invalid, Acidobazic, Solubility);
 
-  function Display_Checked_Answer(UID: in Unique_ID; Answer: in Problem_Generator_Syswides.Answer_Info.Map; HTML: out HTML_Code) return Boolean;
-  function Display_New_Assignment(UID: in Unique_ID; HTML: out HTML_Code) return Boolean;
-  function Display_Next_Assignment(UID: in Unique_ID;
-                                  Problem_Parameters: in Problem_Generator_Syswides.Parameters_Info.Map;
-                                  HTML: out HTML_Code) return Boolean;
+  function Display_Checked_Answer(UID: in Unique_ID; Answer: in Problem_Generator_Syswides.Answer_Info.Map; HTML: out HTML_Code;
+                                 Pr_ID: in Problem_ID) return Boolean;
+  function Display_Assignment(UID: in Unique_ID; HTML: out HTML_Code) return Boolean;
+  --function Display_Next_Assignment(UID: in Unique_ID;
+  --                              Problem_Parameters: in Problem_Generator_Syswides.Parameters_Info.Map;
+  --                              HTML: out HTML_Code) return Boolean;
   function Get_UID(Raw_UID: in String; UID: out Unique_ID) return Boolean;
-  function Prepare_Problem(UID: in Unique_ID; P_Cat: in Problem_Category) return Boolean;
+  function Prepare_Problem(UID: in Unique_ID; Raw_P_Cat: in String;
+                          Parameters: in Problem_Generator_Syswides.Parameters_Info.Map := Problem_Generator_Syswides.Parameters_Info.Empty_Map) return Boolean;
   function Register_UID(UID: out Unique_ID) return Boolean;
   procedure Session_Expired(SID: AWS.Session.ID);
 
 private
+  type Chem_Problem_All_Access is access all Problem_Generator.Chem_Problem'Class;
+  procedure Free_Chem_Problem(Problem: in out Chem_Problem_All_Access);
+
+  type Stored_Problem is
+    record
+      Mutex: Simple_Mutex;
+      Problem: Chem_Problem_All_Access;
+      Category: Problem_Category;
+    end record;
+  type Stored_Problem_All_Access is access all Stored_Problem;
+  procedure Free_Stored_Problem(Problem: in out Stored_Problem_All_Access);
+
+  package Problem_Storage is new Ada.Containers.Ordered_Maps(Key_Type => Problem_ID, Element_Type => Stored_Problem_All_Access);
+
   type User_Session_Data is
     record
-      P_Cat: Problem_Category;
-      Problem: access Problem_Generator.Chem_Problem'Class;
+      Problems: Problem_Storage.Map;
+      Last_Problem_ID: Problem_ID := Problem_ID'First;
     end record;
-  type User_Session_Data_Access is access all User_Session_Data;
+  type User_Session_All_Access is access all User_Session_Data;
 
-  package Session_Keeping is new Ada.Containers.Ordered_Maps(Key_Type => Unique_ID, Element_Type => User_Session_Data_Access);
+  package Session_Keeping is new Ada.Containers.Ordered_Maps(Key_Type => Unique_ID, Element_Type => User_Session_All_Access);
 
   protected Active_Sessions is
+    procedure Add_Problem(UID: in Unique_ID; Problem: in Stored_Problem_All_Access; Success: out Boolean);
     function Contains(UID: in Unique_ID) return Boolean;
-    function Get_Session_Data(UID: in Unique_ID; Success: out Boolean) return User_Session_Data_Access;
-    procedure Set_Session_Data(UID: in Unique_ID; USD: in User_Session_Data_Access; Success: out Boolean);
+    function Get_Problem(UID: in Unique_ID; Pr_ID: in Problem_ID) return Stored_Problem_All_Access;
+    function Get_Latest_Problem_With_ID(UID: in Unique_ID; Pr_ID: out Problem_ID) return Stored_Problem_All_Access;
     procedure Register_UID(UID: out Unique_ID; Success: out Boolean);
     procedure Remove_Session(UID: in Unique_ID);
 
   private
     procedure Check_Free_And_Register(UID: in Unique_ID; Success: out Boolean; Stop: out Boolean);
+    procedure Free_Session_Data(Data: in out User_Session_All_Access);
 
     Sessions: Session_Keeping.Map;
     Last_UID: Unique_ID := Unique_ID'First;
   end Active_Sessions;
 
+  MAX_STORED_PROBLEMS: Problem_ID := 20;
 end Problem_Manager;