# Seed Data Generation

#### This guide can be used to create databases with seed data. Note that seed data generation should ONLY be used on development or test environments as, without modifications to the scripts below, the databases are dropped and recreated.

## Getting Started

### Step 1: Docker Seeding Script

Inside the `~/Documents/elentra-developer/resources/scripts` directory, create a file called 'install-elentra'.

Open that file and paste the following into it, replacing the database names with the ones used for testing:

```
#!/bin/bash
set -e 

echo "Dropping databases."
mysql -hmariadb -uroot -ppassword -e 'DROP DATABASE IF EXISTS <ELENTRA_ME>;'
mysql -hmariadb -uroot -ppassword -e 'DROP DATABASE IF EXISTS <ELENTRA_ME_CLERKSHIP>;'
mysql -hmariadb -uroot -ppassword -e 'DROP DATABASE IF EXISTS <ELENTRA_AUTH>;'

echo "Creating databases."
mysql -hmariadb -uroot -ppassword -e 'CREATE DATABASE <ELENTRA_ME>;'
mysql -hmariadb -uroot -ppassword -e 'CREATE DATABASE <ELENTRA_ME_CLERKSHIP>;'
mysql -hmariadb -uroot -ppassword -e 'CREATE DATABASE <ELENTRA_AUTH>;'

cd /var/www/vhosts/elentra-1x-me
rm www-root/core/config/config.inc.php

echo "Installing Elentra config script."

php www-root/setup/install.php \
      --entrada-url=http://elentra-1x-me.localhost \
      --entrada-absolute=$(pwd)/www-root \
      --entrada-storage=$(pwd)/www-root/core/storage \
      --database-adapter=mysqli \
      --database-host=mariadb \
      --database-username=root \
      --database-password=password \
      --entrada-database=<ELENTRA_ME> \
      --auth-database=<ELENTRA_AUTH> \
      --clerkship-database=<ELENTRA_ME_CLERKSHIP> \
      --admin-username=user1 \
      --admin-password=apple123 \
      --admin-firstname=System \
      --admin-lastname=Adminstrator \
      --admin-email=admin@example.com

echo "Running Migrations."
php elentra migrate --up --quiet


cd /var/www/vhosts/elentra-1x-me/www-root/core/library/vendor/elentrapackages/elentra-1x-api

#This could be used to test the seeding of a specific module
#php artisan db:seed --class=LotteriesSeeder

#This will seed all registered seed modules
php artisan db:seed --quiet
```

This file needs a chmod 777 on it for execution.

**Important Note:** This script will delete the **config.inc.php** file as well as **drop the specified databases.**

It will then recreate those 3 databases, and generate a new `www-root/core/config/config.inc.php` script to tell the system how to connect to the new databases.

Next, it runs all migration scripts inside the `www-root/core/library/Migrate` directory. This is to bring the databases up to date with whichever branch the developer is on.

Finally, it runs the seed files which are used to populate specified tables with test data using [Laravel's database seeding functionality](https://laravel.com/docs/9.x/seeding#:~:text=Laravel%20includes%20the%20ability%20to,to%20control%20the%20seeding%20order.).

In your Elentra API directory, change the `phpunit.xml` file from:

```
 <testsuite name="Functional">
   <directory suffix="Test.php">./tests/Functional</directory>
 </testsuite>
```

to:

```
 <testsuite name="Functional">
      <directory suffix="Test.php">./tests/Functional</directory>
      <directory suffix="Test.php">./tests/Integration</directory>
    </testsuite>
```

This will tell PHPUnit to also run any tests that reside in the Integration directory

Edit the `composer.json` file and change:

```
"autoload": {
    "psr-4": {
        "Entrada\\": "app/"
    },
    "classmap": [
        "database"
    ]
},
```

to:

```
"autoload": {
    "psr-4": {
        "Entrada\\": "app/"
    },
    "classmap": [
        "database/seeds",
        "database/factories"
    ]
},
```

Run `composer install` to regenerate the classmap autoloader so that all files inside the `database/seeds` and `database/factories` directories are recognized by the system.

### Step 2: Creating a JSON Seed File

Seed files are located within the module's Database/Seeds directory.

*eg: `elentra-1x-api/app/Modules/Locations/Database/Seeds/ClinicalLocationsSeedData.php`*

To create a seed data file, right click on the appropriate module's Database/Seeds directory and select 'New Class'. All seed data files are suffixed with 'SeedData'. eg:

* `ClinicalLocationsSeedData.php`
* `LotteriesSeedData.php`
* `UserAccessSeedData.php`

All seed data files must implement the `SeedDataInterface`. The `SeedDataInterface`\` has no namespace which means the Class definition would look like:

*`class ClinicalLocationsSeedData implements \SeedDataInterface`*

SeedDataInterface provides 2 methods:

```
public function getData(): array;

public function getType();
```

For a basic seed data file implementing the SeedDataInterface would look like this:

```
namespace Entrada\Modules\Courses\Database\Seeds;

use Entrada\Modules\Lotteries\Models\Base\CurriculumPeriod;

class CurriculumPeriodsSeedData implements \SeedDataInterface
{

    public static function getCurriculumPeriods()
    {
        return
            [
                [
                    "cperiod_id" => 1,
                    "curriculum_type_id" => 1,
                    "curriculum_period_title" => "Test Period 1",
                    "start_date" => 1565064000,
                    "finish_date" => 1596772799,
                    "active" => 1
                ]
            ];
    }

    public function getData(): array
    {
        return self::getCurriculumPeriods();
    }

    public function getType()
    {
        return CurriculumPeriod::class;
    }
}
```

The getData() method returns the predefined JSON array of values to be inserted into the database. The getType() method returns the Model class of the database table to be seeding with the JSON values.

#### Creating a Seed Data File With No Model Class

Not all tables have a corresponding Model class to associate to. An example is the `user_auth.user_access` table. All queries to the `user_auth.user_access` table are performed with joins and no Model class (eg: `UserAccess.php`) is required.

In this case we also implement the RawSeederInterface. The RawSeederInterface provides 2 methods:

```
public function getTablename() : string;

public function getTimestamps() : bool;
```

An example of a class definition would be:

*`class ScheduleFilesSeedData implements \SeedDataInterface, \RawSeederInterface`*

An example of a class would be:

```
namespace Entrada\Modules\Lotteries\Database\Seeds;

use Entrada\Modules\Clinical\Models\Entrada\RotationScheduleFile;

class ScheduleFilesSeedData implements \SeedDataInterface, \RawSeederInterface
{

    public static function getScheduleFiles()
    {
        return
            [
                [
                    "schedule_file_id" => 1,
                    "schedule_id" => 1,
                    "type" => "",
                    "size" => 0,
                    "name" => "Test Schedule File",
                    "label" => "",
                    "view" => "view",
                    "created_date" => 0,
                    "created_by" => 0,
                    "updated_date" => null,
                    "updated_by" => null,
                    "deleted_date" => null,
                    "deleted_by" => null
                ]
            ];
    }

    public function getData(): array
    {
        return self::getScheduleFiles();
    }

    public function getType()
    {
        return RotationScheduleFile::class;
    }

    public function getTablename(): string
    {
        return 'cbl_schedule_files';
    }

    public function getTimestamps(): bool
    {
        return true;
    }
}
```

The getTablename() method is returning the name of the table to insert the JSON data into. The getTimestamps() method is returning a true/false value to determine if timestamp columns are required. When a table contains the following columns:

* created\_at
* updated\_at

The getTimestamps method should return `true`. If those columns do not exist the method should return `false`. This is because Laravel will automatically append those columns to an insert query unless we explicitly tell it not to.

### Step 3: Registering the Seed File

Once a seed file is created it needs to be registered. Inside the `/database/seeds` directory at the root of the elentra-1x-api repository, create a new Seeder class. The file naming convention is as follows: Seeder.php eg: LotteriesSeeder.php

A seeder class must extend ElentraBaseSeeder. example:

```
class LocationsSeeder extends ElentraBaseSeeder
{

    public function __construct()
    {
        parent::__construct(array(
            new ClinicalLocationsSeedData(),
            new ClinicalPreceptorsSeedData()
        ));
    }
}
```

The Seeder classes have no methods except the constructor, which is where we register the SeedData files we created in the previous section. These are defined in the constructor and passed into the parent ElentraBaseSeeder which has the run() method to iterate any passed in SeedData classes.

**Tips for Making Json from Database**

* If you are on Windows, a useful IDE for MySQL/MariaDB is SqlYog. It allows you to select tables for export and select JSON as a format.
* If you are on Linux you can install "W\.ine I.s N.ot an E.mulator" (wine) and run SqlYog quite seamlessly.
* If you are on Mac and your IDE does not support exporting as JSON, you can export as SQL and use <https://codebeautify.org/sql-to-json-converter>. It'll take a couple of edits to the generated output from your IDE but works quite nicely for making JSON from SQL.

### 4: Adding the Seed Data Registry to Laravel

**In the older repository it is still performed this way:**

The final step is registering the Seeder class which is a registry of SeedData files with the Laravel provided DatabaseSeeder. This is located in the `/database/seeds` directory at the root of the API repository.

The DatabaseSeeder class has 1 Laravel-provided method:

```
/**
* Run the database seeds.
*
* @return void
*/
public function run(): void
{
    $this->call([
        CoursesTableSeeder::class,
        LotteriesSeeder::class,
        LocationsSeeder::class,
        UsersSeeder::class
    ]);
}
```

**In the newer repository it is performed this way:**

The final step is registering the Seeder class which is a registry of SeedData files with the `elentra.php` file. This is located in the `/config` directory at the root of the API repository.

```
 /* around line 67
    | Database
    |--------------------------------------------------------------------------
    |
    | Configuration for the database
    |
    */
    'database' => [
        'seeders' => [
            //UsersTableSeeder::class, /* Creates random users, not suitable for our tests that depend on specific values. */

             // Application seeders
            AssessmentsSeeder::class,
            BookmarksSeeder::class,
            CommunitiesSeeder::class,
            CoursesTableSeeder::class,
            DisclaimersTableSeeder::class,
            EventsSeeder::class,
            ExamsSeeder::class,
            GradebooksSeeder::class,
            LocationsSeeder::class,
            LotteriesSeeder::class,
            UsersSeeder::class,
        ],
    ],
```

This is where the Seeder classes are registered with the Laravel system.

A visual representation:

```
                   Lotteries Seeder is registered to the DatabaseSeeder class
                       |     Lottery Seed Data is registered to a LotteriesSeeder class
                       |                       |
                       |                       V
                       |             <--- LotteriesSeedData
                       V             <--- LotteryStagesSeedData
DatabaseSeeder: <-- LotteriesSeeder: <--- LotteryStagePhasesSeedData
                <-- CoursesSeeder
                <-- UsersSeeder
```

Once any new Seeder class is registered in the DatabaseSeeder class we need to flush the composer autoloader to force recognizing the new file:

```
composer dump-autoload
```

### Step 4: Running the Seed Scripts

From the `~/Documents/elentra-developer` directory, open a new developer shell:

```
./developer shell
```

inside the developer shell:

```
install-elentra
```

This will call the script from anywhere within the shell and will populate all seed data.

### Troubleshooting:

Whenever a new Seed script is registered, the Laravel system must be refreshed. From the developer shell:

```
composer dump-autoload
```

This will reset the system and recognize any new changes.

If you need to see the output of the seeder scripts for debugging, edit the `install-elentra` script so that the line:

```
php artisan db:seed --quiet
```

is changed to:

```
php artisan db:seed
```

Turning off 'quiet' mode will provide verbose logging and display any possible SQL exceptions that occur.

```
Class DatabaseSeeder not found
```

Check the symlink directory to ensure the changes in `composer.json` are present. If they aren't you'll need to redo the symlink:

```
cd /var/www/vhosts/elentra-1x-me/www-root/core/library/vendor/elentrapackages;

rm -Rf elentra-1x-api;

ln -s /var/www/vhosts/elentra-1x-api
```

#### TLDR;

1. Create the install-elentra script inside of `~/Documents/elentra-developer/resources/scripts`
2. Create a SeedData class inside a Module's `database/seeds` directory
3. Create a Seeder class inside the `elentra-1x-api/database/seeds` directory and register the SeedData class inside that Seeder's constructor
4. Edit the DatabaseSeeder class to call the Seeder class in its array
5. Inside the developer shell run `install-elentra`


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.elentra.org/technical/developers/troubleshooting/seed-data-generation.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
