_binary From fcee3bdb572312d4f25ff488af728aac3ff9cd3c Mon Sep 17 00:00:00 2001
From: Sujith Kakarlapudi <sujith.kakarlapudi@sjsu.edu>
Date: Wed, 12 Mar 2025 23:24:03 -0700
Subject: [PATCH] New Approach to create test data for Impressions at random
 timestamps

---
 src/models/GroupModel.php      |  14 +-
 src/models/ImpressionModel.php |  35 ++--
 tests/ManyUserExperiment.php   | 296 +++++++++++++++++++++++++++++++++
 3 files changed, 330 insertions(+), 15 deletions(-)

diff --git a/src/models/GroupModel.php b/src/models/GroupModel.php
index 8d7d0f419..962ac7c42 100644
--- a/src/models/GroupModel.php
+++ b/src/models/GroupModel.php
@@ -263,12 +263,14 @@ class GroupModel extends Model implements MediaConstants
      *      live before they expire and are deleted
      * @param int $encryption 0 means don't encrypt group, 1 means encrypt
      *      group
+     * @param boolean $skip_impression if false, initializes impression tracking
+     *      for the group; if true, skips initializing impression tracking
      * @return int id of group added
      */
     public function addGroup($group_name, $user_id, $wiki_editors,
         $register = C\REQUEST_JOIN, $member = C\GROUP_READ,
         $voting = C\NON_VOTING_GROUP, $post_lifetime = C\FOREVER,
-        $encryption = 0)
+        $encryption = 0, $skip_impression = false)
     {
         $db = $this->db;
         $private_db = $this->private_db;
@@ -290,10 +292,12 @@ class GroupModel extends Model implements MediaConstants
             JOIN_DATE) VALUES
             ($user_id, $last_id, " . C\ACTIVE_STATUS . ", $now)";
         $db->execute($sql);
-        ImpressionModel::initWithDb($user_id, $last_id, C\GROUP_IMPRESSION,
-            $db);
-        ImpressionModel::initWithDb(C\PUBLIC_GROUP_ID, $last_id,
-            C\GROUP_IMPRESSION, $db);
+        if ($skip_impression === false) {
+            ImpressionModel::initWithDb($user_id, $last_id, C\GROUP_IMPRESSION,
+                $db);
+            ImpressionModel::initWithDb(C\PUBLIC_GROUP_ID, $last_id,
+                C\GROUP_IMPRESSION, $db);
+        }
         if ($encryption != 0) {
             $sql = "INSERT INTO TYPE_KEYS (TYPE_ID, KEY_NAME) VALUES (?, ?)";
             //AES 256 is 32 bytes long (8*32 bits)
diff --git a/src/models/ImpressionModel.php b/src/models/ImpressionModel.php
index 3807e959c..2088bf6e7 100644
--- a/src/models/ImpressionModel.php
+++ b/src/models/ImpressionModel.php
@@ -78,10 +78,14 @@ class ImpressionModel extends Model
      *      information of
      * @param int $type_id type of particular item we are adding analytic
      *      information of (group, wiki, thread, etc)
+     * @param int $time timestamp representing when the analytic
+     *      information is recorded; if not provided or invalid, the current
+     *      time will be used
      */
-    public function add($user_id, $item_id, $type_id)
+    public function add($user_id, $item_id, $type_id, $time = -1)
     {
-        $this->addWithDb($user_id, $item_id, $type_id, $this->db);
+        $time = ($time > 0) ? $time : time();
+        $this->addWithDb($user_id, $item_id, $type_id, $this->db, $time);
     }
     /**
      * Used to add a count record related to a web search to the impression
@@ -113,8 +117,9 @@ class ImpressionModel extends Model
             $this->initWithDb(C\PUBLIC_USER_ID, $db->insertID("QUERY_ITEM"),
                 $impression_type, $db);
         } else {
+            $input_timestamp = time();
             $this->addWithDb(C\PUBLIC_USER_ID, $row['ID'],
-                $impression_type, $db);
+                $impression_type, $db, $input_timestamp);
         }
     }
     /**
@@ -150,8 +155,9 @@ class ImpressionModel extends Model
             $this->initWithDb(C\PUBLIC_USER_ID, $db->insertID("QUERY_ITEM"),
                 C\QUERY_IMPRESSION_EXIT, $db);
         } else {
+            $input_timestamp = time();
             $this->addWithDb(C\PUBLIC_USER_ID, $row['ID'],
-                C\QUERY_IMPRESSION_EXIT, $db);
+                C\QUERY_IMPRESSION_EXIT, $db, $input_timestamp);
         }
     }
     /**
@@ -496,7 +502,8 @@ class ImpressionModel extends Model
         $num_views, $fuzzy_num_views)
     {
             $sql = "UPDATE ITEM_IMPRESSION_STAT SET FUZZY_NUM_VIEWS=?,
-                NUM_VIEWS=? WHERE ITEM_ID=? AND ITEM_TYPE=? AND UPDATE_PERIOD=?";
+                NUM_VIEWS=? WHERE ITEM_ID=? AND ITEM_TYPE=? AND
+                UPDATE_PERIOD=?";
             $parameters = [$fuzzy_num_views, $num_views, $item_id,
                 $item_type, $period];
             $result = $this->db->execute($sql, $parameters);
@@ -557,7 +564,8 @@ class ImpressionModel extends Model
             (?, ?, ?, " . C\FOREVER .", 0, 0, -1, -1)";
         $sql = $db->insertIgnore($sql, $dbinfo);
         $db->execute($sql, [C\PUBLIC_USER_ID, $item_id, $type_id]);
-        self::addWithDb($user_id, $item_id, $type_id, $db);
+        $input_timestamp = time();
+        self::addWithDb($user_id, $item_id, $type_id, $db, $input_timestamp);
     }
     /**
      * Used to add a count record related to a particular user for a particular
@@ -574,21 +582,28 @@ class ImpressionModel extends Model
      *      information of (group, wiki, thread, etc)
      * @param object $db a DatasourceManager used to query the Yioop
      *      Yioop database
+     * @param int $input_timestamp timestamp representing when the analytic
+     *      information is recorded; if not provided or invalid, the current
+     *      time will be used
      */
-    public static function addWithDb($user_id, $item_id, $type_id, $db)
+    public static function addWithDb($user_id, $item_id, $type_id, $db,
+        $input_timestamp)
     {
         if (!C\GROUP_ANALYTICS_MODE || C\GROUP_ANALYTICS_MODE == "0" ||
             !is_numeric($user_id) || !is_numeric($item_id)) {
             return;
         }
+        $input_timestamp = ($input_timestamp > 0) ? $input_timestamp : time();
         $dbinfo = ["DBMS" => C\DBMS, "DB_HOST" => C\DB_HOST,
             "DB_USER" => C\DB_USER, "DB_PASSWORD" => C\DB_PASSWORD,
             "DB_NAME" => C\DB_NAME];
         $sql = "INSERT INTO ITEM_IMPRESSION VALUES (?, ?, ?, ?)";
         if ($user_id != C\PUBLIC_USER_ID) {
-            $db->execute($sql, [$user_id, $item_id, $type_id, time()]);
+            $db->execute($sql, [$user_id, $item_id, $type_id,
+                $input_timestamp]);
         }
-        $db->execute($sql, [C\PUBLIC_USER_ID, $item_id, $type_id, time()]);
+        $db->execute($sql, [C\PUBLIC_USER_ID, $item_id, $type_id,
+            $input_timestamp]);
         $sql = "DELETE FROM ITEM_IMPRESSION_SUMMARY
             WHERE USER_ID=? AND ITEM_ID=? AND ITEM_TYPE=? AND UPDATE_PERIOD = ".
             C\MOST_RECENT_VIEW;
@@ -596,7 +611,7 @@ class ImpressionModel extends Model
         $sql = "INSERT INTO ITEM_IMPRESSION_SUMMARY VALUES
             (?, ?, ?, " . C\MOST_RECENT_VIEW .", ?, 0, -1, -1)";
         $sql = $db->insertIgnore($sql, $dbinfo);
-        $db->execute($sql, [$user_id, $item_id, $type_id, time()]);
+        $db->execute($sql, [$user_id, $item_id, $type_id, $input_timestamp]);
         $sql = "UPDATE ITEM_IMPRESSION_SUMMARY
             SET NUM_VIEWS = NUM_VIEWS + 1 WHERE USER_ID=? AND
             ITEM_ID=? AND ITEM_TYPE=? AND UPDATE_PERIOD = ". C\FOREVER . "
diff --git a/tests/ManyUserExperiment.php b/tests/ManyUserExperiment.php
index 56a3994cc..0130aff3a 100644
--- a/tests/ManyUserExperiment.php
+++ b/tests/ManyUserExperiment.php
@@ -382,3 +382,299 @@ for ($i = 0; $i < C\ME_NUM_USERS; $i++) {
             C\RESOURCE_IMPRESSION);
     }
 }
+/**
+ * This script introduces a new method for generating test data,
+ * distinct from the existing one in this file. Similar to the previous
+ * approach, it creates users, groups, and wiki pages, then adds dummy media
+ * as resources. In addition, it simulates user impressions on random resources
+ * at random timestamps.
+ */
+C\nsconddefine("ME_NUM_USERS", 500);
+C\nsconddefine("ME_NUM_GROUPS", 100);
+C\nsconddefine("ME_NUM_RESOURCES_IMPRESSION", 15);
+$new_user_model = new M\UserModel();
+$new_group_model = new M\GroupModel();
+//Add lots of users
+$new_user_ids = [];
+for ($i = 0; $i < C\ME_NUM_USERS; $i++) {
+    echo "Adding User $i\n";
+    $id = $new_user_model->addUser("User$i", "test", "First$i", "Last$i",
+        "user$i@email.net", C\ACTIVE_STATUS);
+    if ($id === false) {
+        echo "Problem inserting user into DB, aborting...\n";
+        exit(1);
+    }
+    $new_personal_group_name = C\PERSONAL_GROUP_PREFIX . $id;
+    if ($new_group_model->getGroupId($new_personal_group_name) < 0) {
+        $new_group_model->addGroup($new_personal_group_name,
+            $id, ["User$i"], C\INVITE_ONLY_JOIN, C\ACTIVE_STATUS,
+            C\NON_VOTING_GROUP, C\FOREVER, 0, true);
+    }
+    $new_user_ids[$i] = $id;
+}
+// add lots of groups
+$new_group_ids = [];
+for ($i = 0; $i < C\ME_NUM_GROUPS; $i++) {
+    echo "Creating Group $i\n";
+    $new_group_ids[$i] = $new_group_model->addGroup("Group$i",
+        $new_user_ids[$i],["User$i"], C\PUBLIC_JOIN, C\GROUP_READ_WIKI,
+        C\NON_VOTING_GROUP,C\FOREVER, 0, true);
+}
+// add lots of users to groups (everyone belongs to at least group 1)
+for ($i = 0; $i < C\ME_NUM_GROUPS; $i++) {
+    $new_user_id = $new_user_ids[$i];
+    for ($j = 1; $j < ($i/10) + 1; $j++) {
+        if ($j != $i) {//by above user $i is owner (and so in) group $i
+            echo "Adding User $i (user id $new_user_id) to ".
+                "Group $j (group id {$new_group_ids[$j]})\n";
+            $new_group_model->addUserGroup($new_user_id, $new_group_ids[$j],
+                C\ACTIVE_STATUS);
+        }
+    }
+}
+$new_impression_model = new M\ImpressionModel();
+$new_page = <<< 'EOD'
+page_type=media_list
+
+page_alias=
+
+page_border=solid-border
+
+toc=true
+
+title=
+
+author=
+
+robots=
+
+description=
+
+alternative_path=
+
+page_header=
+
+page_footer=
+
+sort=aname
+
+update_description=files-and-folders
+
+END_HEAD_VARS
+==This is test page.==
+EOD;
+$new_page_content = str_replace("'", "&#039;", $new_page);
+$new_page_name = "Test";
+echo "Adding wiki page $new_page_name to group $new_group_ids[0]...\n";
+$new_page_id = $new_group_model->setPageName($new_user_ids[0],
+  $new_group_ids[0], $new_page_name, $new_page_content, "en-US",
+  "Creating test wiki page", "{$new_page_name} Wiki Page Created!",
+  "Discuss the page in this thread!");
+echo "Added the wiki page...\n";
+echo "Creating movies folder and adding dummy mp4 resources\n";
+$new_resources = [];
+$new_folders_path = [];
+// create movie folder in the wiki page
+$new_folders = $new_group_model->getGroupPageResourcesFolders($new_group_ids[0],
+    $new_page_id,"movies", true);
+// add resources to the movies subfolder
+$new_movies = [
+    "The 355.mp4",
+    "The Legend of La Llorona.mp4",
+    "The Commando.mp4",
+    "American Siege.mp4",
+    "Scream.mp4",
+    "Hotel Transylvania Transformania.mp4",
+    "Shattered.mp4",
+    "The Royal Treatment.mp4",
+    "Redeeming Love.mp4",
+    "The Kings Daughter.mp4",
+    "The Tiger Rising.mp4",
+    "Hellblazers.mp4",
+    "The Fallout.mp4",
+    "Home Team.mp4",
+    "The Ice Age Adventures of Buck Wild.mp4",
+    "Clean.mp4",
+    "Sundown.mp4",
+    "Jackass Forever.mp4",
+    "Moonfall.mp4",
+    "Last Looks.mp4",
+    "The Long Night.mp4",
+    "Kimi.mp4",
+    "Shut In.mp4",
+    "Death on the Nile.mp4",
+    "Marry Me.mp4",
+    "Blacklight.mp4",
+    "I Want You Back.mp4",
+    "Tall Girl 2.mp4",
+    "The Sky Is Everywhere.mp4",
+    "The In Between.mp4",
+    "Fistful of Vengeance.mp4",
+    "Uncharted.mp4",
+    "Dog.mp4",
+    "Texas Chainsaw Massacre.mp4",
+    "The Cursed.mp4",
+    "A Fairy Tale After All.mp4",
+    "Cyrano.mp4",
+    "Studio 666.mp4",
+    "A Madea Homecoming.mp4",
+    "No Exit.mp4",
+    "Gasoline Alley.mp4",
+    "The Batman.mp4",
+    "After Yang.mp4",
+    "Fresh.mp4",
+    "A Day to Die.mp4",
+    "The Adam Project.mp4",
+    "Turning Red.mp4",
+    "Tysons Run.mp4",
+    "Rescued by Ruby.mp4",
+    "The Outfit.mp4",
+    "Deep Water.mp4",
+    "Cheaper by the Dozen.mp4",
+    "Windfall.mp4",
+    "X.mp4",
+    "Umma.mp4",
+    "Alice.mp4",
+    "Master.mp4",
+    "Measure of Revenge.mp4",
+    "The Lost City.mp4",
+    "Everything Everywhere All at Once.mp4"
+];
+$new_resources[] = $new_movies;
+$new_folders_path[] = $new_folders;
+foreach ($new_movies as $new_movie) {
+    $new_group_model->setPageResource($new_movie, "This is test data",
+        $new_group_ids[0], $new_page_id, "movies");
+}
+echo "Creating web_series folder and adding dummy mp4 resources\n";
+// create web_series folder in the wiki page
+$new_folders = $new_group_model->getGroupPageResourcesFolders($new_group_ids[0],
+    $new_page_id, "web_series", true);
+// add resources to the web_series subfolder
+$new_web_series = [
+    "Unforgotten Season 2.mp4",
+    "The Devils Hour Season 1.mp4",
+    "Indian Predator Murder in a Courtroom.mp4",
+    "If Only.mp4",
+    "Flames Season 3.mp4",
+    "Jhansi.mp4",
+    "Dubai Bling.mp4",
+    "The Mysterious Benedict Society Season 2.mp4",
+    "Sumo Do, Sumo Dont!.mp4",
+    "Star Wars Tales of the Jedi.mp4",
+    "Shadow Detective.mp4",
+    "Tripling Season 3.mp4",
+    "The Serpent Queen.mp4",
+    "The Peripheral.mp4",
+    "From Scratch.mp4",
+    "Four More Shots Please! Season 3.mp4",
+    "Barbarians Season 2.mp4",
+    "Notre-Dame.mp4",
+    "Shantaram.mp4",
+    "Mismatched Season 2.mp4",
+    "Good Bad Girl.mp4",
+    "Everything Calls for Salvation.mp4",
+    "The Watcher.mp4",
+    "The Playlist.mp4",
+    "Exception Season 1.mp4",
+    "Rookie Cops.mp4",
+    "Big Shot Season 2.mp4",
+    "Belascoarán, Pl Season 1.mp4",
+    "Werewolf by Night.mp4",
+    "The Midnight Club.mp4",
+    "Man on Pause.mp4",
+    "Derry Girls Season 3.mp4",
+    "Conversations with a Killer The Jeffrey Dahmer Tapes.mp4",
+    "Aashiqana Season 2.mp4",
+    "Shipwreck Hunters Australia.mp4"
+];
+$new_resources[] = $new_web_series;
+$new_folders_path[] = $new_folders;
+foreach ($new_web_series as $new_series) {
+    $new_group_model->setPageResource($new_series, "This is test data",
+        $new_group_ids[0], $new_page_id, "web_series");
+}
+echo "Creating books folder and adding dummy pdf resources\n";
+// create books folder in the wiki page
+$new_folders = $new_group_model->getGroupPageResourcesFolders($new_group_ids[0],
+                  $new_page_id, "books", true);
+// add resources to the books subfolder
+$new_books = [
+    "Book Lovers.pdf",
+    "Gallant.pdf",
+    "Daughter of the Moon Goddess.pdf",
+    "Book of Night.pdf",
+    "The Girl Who Fell Beneath the Sea.pdf",
+    "I Kissed Shara Wheeler.pdf",
+    "House of Sky and Breath.pdf",
+    "A Magic Steeped in Poison.pdf",
+    "Love on the Brain.pdf",
+    "Only a Monster.pdf",
+    "This Woven Kingdom.pdf",
+    "Delilah Green Doesnt Care.pdf",
+    "Portrait of a Thief.pdf",
+    "The Paris Apartment.pdf",
+    "Foul Lady Fortune.pdf",
+    "Bloodmarked.pdf",
+    "Belladonna.pdf",
+    "Ophelia After All.pdf",
+    "Violet Made of Thorns.pdf",
+    "Carrie Soto Is Back.pdf",
+    "Hook, Line, and Sinker.pdf",
+    "The MaidThe Maid.pdf",
+    "A Far Wilder Magic.pdf",
+    "Sea of Tranquility.pdf",
+    "The Book of Cold Cases.pdf",
+    "All My Rage.pdf",
+    "Kaikeyi.pdf",
+    "How High We Go in the Dark.pdf",
+    "The Sunbearer Trials.pdf",
+    "Hotel Magnifique.pdf",
+    "What Moves the Dead.pdf",
+    "Stuck with You.pdf",
+    "Below Zero.pdf",
+    "The House Across the Lake.pdf",
+    "Nettle & Bone.pdf",
+    "A Million to One.pdf",
+    "An Arrow to the Moon.pdf",
+    "Something Wilder.pdf",
+    "Anatomy: A Love Story.pdf",
+    "The Final Gambit.pdf",
+    "The City of Dusk.pdf",
+    "The Ivory Key.pdf",
+    "So This Is Ever After.pdf",
+    "The Book Eaters.pdf",
+    "The Red Palace.pdf",
+    "The Stardust Thief.pdf",
+    "She Gets the Girl.pdf",
+    "Weather Girl.pdf",
+    "The Bone Spindle.pdf"
+];
+$new_resources[] = $new_books;
+$new_folders_path[] = $new_folders;
+foreach ($new_books as $new_book) {
+    $new_group_model->setPageResource($new_book, "This is test data",
+        $new_group_ids[0], $new_page_id, "books");
+}
+echo "Creating users impression with random resources at random timestamps\n";
+$current_timestamp = time();
+$one_year_ago_timestamp = $current_timestamp - (365 * 24 * 60 * 60);
+for ($i = 0; $i < C\ME_NUM_USERS; $i++) {
+    $new_user_id = $new_user_ids[$i];
+    for ($j = 0; $j < C\ME_NUM_RESOURCES_IMPRESSION; $j++) {
+        $random_timestamp = rand($one_year_ago_timestamp,
+            $current_timestamp - 60);
+        $new_resource_type = rand(0,2);
+        $new_resource_index = rand(0, count($new_resources[$new_resource_type])
+            - 1);
+        $new_resource_name = $new_resources[$new_resource_type]
+            [$new_resource_index];
+        $new_resource_id = unpack('n', md5($new_group_ids[0] . $new_page_id .
+            $new_folders_path[$new_resource_type][1] . "/" . $new_resource_name,
+            true))[1];
+        $new_impression_model->add($new_user_id, $new_resource_id,
+            C\RESOURCE_IMPRESSION, $random_timestamp);
+        echo "Creating RESOURCE_IMPRESSION for user $new_user_id with resource"
+            . " $new_resource_name" . " with timestamp: $random_timestamp\n";
+    }
+}
-- 
2.46.0.windows.1

