elabftw/elabftw

View on GitHub
src/services/Populate.php

Summary

Maintainability
C
1 day
Test Coverage
<?php

/**
 * @author Nicolas CARPi <nico-git@deltablot.email>
 * @copyright 2012 Nicolas CARPi
 * @see https://www.elabftw.net Official website
 * @license AGPL-3.0
 * @package elabftw
 */

declare(strict_types=1);

namespace Elabftw\Services;

use Elabftw\Elabftw\UserParams;
use Elabftw\Enums\Action;
use Elabftw\Enums\BasePermissions;
use Elabftw\Enums\FileFromString;
use Elabftw\Models\ApiKeys;
use Elabftw\Models\Experiments;
use Elabftw\Models\ExperimentsCategories;
use Elabftw\Models\ExperimentsStatus;
use Elabftw\Models\Items;
use Elabftw\Models\ItemsStatus;
use Elabftw\Models\ItemsTypes;
use Elabftw\Models\Tags;
use Elabftw\Models\Teams;
use Elabftw\Models\Templates;
use Elabftw\Models\Users;

/**
 * This is used to generate data for dev purposes
 */
class Populate
{
    /** @var string DEFAULT_PASSWORD the password to use if none are provided */
    private const DEFAULT_PASSWORD = 'totototototo';

    /** @var int TEMPLATES_ITER number of templates to generate */
    private const TEMPLATES_ITER = 5;

    private \Faker\Generator $faker;

    // iter: iterations: number of things to generate
    public function __construct(private int $iter = 50)
    {
        $this->faker = \Faker\Factory::create();
    }

    /**
     * Populate the db with fake experiments or items
     */
    public function generate(Experiments | Items $Entity): void
    {
        $Teams = new Teams($Entity->Users, $Entity->Users->team);
        $Teams->bypassWritePermission = true;
        if ($Entity instanceof Experiments) {
            $Category = new ExperimentsCategories($Teams);
            $Status = new ExperimentsStatus($Teams);
            $tpl = 0;
        } else {
            $Category = new ItemsTypes($Entity->Users);
            $Category->bypassWritePermission = true;
            $Status = new ItemsStatus($Teams);
            $tpl = (int) $Category->readAll()[0]['id'];
        }
        $categoryArr = $Category->readAll();
        $statusArr = $Status->readAll();

        // we will randomly pick from these for canread and canwrite
        $visibilityArr = array(
            BasePermissions::Full->toJson(),
            BasePermissions::Organization->toJson(),
            BasePermissions::Team->toJson(),
            BasePermissions::User->toJson(),
            BasePermissions::UserOnly->toJson(),
        );

        $tagsArr = array(
            'Project X',
            'collaboration',
            'SCP-2702',
            'Western Blot',
            'HeLa',
            'Fly',
            'Dark Arts',
            'COVID-24',
            'FLIM',
            'FRET',
            'Open Source',
            'Software',
            'Secret',
            'Copper',
            'nanotechnology',
            'spectroscopy',
            'hardness testing',
            'cell culture',
            'DNA sequencing',
            'PCR',
            'gene expression',
            'protein purification',
            'biological samples',
            'data analysis',
            'lab safety',
            'genetics',
            'molecular biology',
            'cell biology',
            'biotechnology',
            'biochemistry',
            'microbiology',
            'ecology',
            'bioinformatics',
            'research methodology',
            'lab techniques',
            'experimental design',
            'ethics in research',
            'laboratory management',
            'scientific collaboration',
            'lab supplies',
            'scientific discovery',
            'data interpretation',
            'hypothesis testing',
            'cell culture techniques',
            'genomic analysis',
            'protein analysis',
            'molecular cloning',
            'biomolecular assays',
            'statistical analysis',
            'scientific literature',
        );

        for ($i = 0; $i <= $this->iter; $i++) {
            $id = $Entity->create($tpl);
            $Entity->setId($id);
            // variable tag number
            $Tags = new Tags($Entity);
            $tagNb = $this->faker->numberBetween(0, 5);
            for ($j = 0; $j <= $tagNb; $j++) {
                $Tags->postAction(Action::Create, array('tag' => $this->faker->randomElement($tagsArr)));
            }
            // random date in the past 5 years
            $date = $this->faker->dateTimeBetween('-5 years')->format('Ymd');
            $Entity->patch(Action::Update, array('title' => $this->faker->sentence(), 'date' => $date, 'body' => $this->faker->realText(1000)));

            // lock 10% of experiments (but not the first one because it is used in tests)
            if ($this->faker->randomDigit() > 8 && $i > 1) {
                $Entity->toggleLock();
            }

            // change the visibility, but not the first ones as they are often used in tests and this could cause permissions issues
            if ($this->faker->randomDigit() > 8 && $i > 10) {
                $Entity->patch(Action::Update, array('canread' => $this->faker->randomElement($visibilityArr)));
                $Entity->patch(Action::Update, array('canwrite' => $this->faker->randomElement($visibilityArr)));
            }

            // CATEGORY
            $category = $this->faker->randomElement($categoryArr);
            $Entity->patch(Action::Update, array('category' => (string) $category['id']));

            // STATUS
            $status = $this->faker->randomElement($statusArr);
            $Entity->patch(Action::Update, array('status' => (string) $status['id']));

            // maybe upload a file but not on the first one
            if ($this->faker->randomDigit() > 7 && $id !== 1) {
                $Entity->Uploads->postAction(Action::CreateFromString, array(
                    'file_type' => FileFromString::Json->value,
                    'real_name' => $this->faker->word() . $this->faker->word(),
                    'content' => '{ "some": "content" }',
                ));
            }

            // maybe add a few steps
            if ($this->faker->randomDigit() > 8) {
                // put two words so it's long enough
                $Entity->Steps->postAction(Action::Create, array('body' => $this->faker->word() . $this->faker->word()));
                $Entity->Steps->postAction(Action::Create, array('body' => $this->faker->word() . $this->faker->word()));
            }

            // maybe make it bookable
            if ($Entity instanceof Items && $this->faker->randomDigit() > 6) {
                $Entity->patch(Action::Update, array('is_bookable' => '1'));
            }
        }
    }

    // create a user based on options provided in yaml file
    public function createUser(Teams $Teams, array $user): void
    {
        $firstname = $user['firstname'] ?? $this->faker->firstName();
        $lastname = $user['lastname'] ?? $this->faker->lastName();
        $orgid = $user['orgid'] ?? null;
        $passwordHash = (new UserParams('password', $user['password'] ?? self::DEFAULT_PASSWORD))->getContent();
        // use yopmail.com instead of safeEmail() so we don't hard bounce on example.tld domains when testing mass emails
        $email = $user['email'] ?? sprintf('elabuser-%d@yopmail.com', $this->faker->randomNumber(6));

        $userid = $Teams->Users->createOne($email, array($user['team']), $firstname, $lastname, $passwordHash, null, true, false, null, $orgid);
        $team = $Teams->getTeamsFromIdOrNameOrOrgidArray(array($user['team']));
        $Requester = new Users(1, 1);
        $Users = new Users($userid, $team[0]['id'], $Requester);

        if ($user['is_sysadmin'] ?? false) {
            $Users->patch(Action::Update, array('is_sysadmin' => 1));
        }

        if ($user['create_mfa_secret'] ?? false) {
            $MfaHelper = new MfaHelper($userid);
            // use a fixed secret
            $MfaHelper->secret = 'EXAMPLE2FASECRET234567ABCDEFGHIJ';
            $MfaHelper->saveSecret();
        }
        if ($user['create_experiments'] ?? false) {
            $this->generate(new Experiments($Users));
        }
        if ($user['create_items'] ?? false) {
            $this->generate(new Items($Users));
        }
        if ($user['api_key'] ?? false) {
            $ApiKeys = new ApiKeys($Users);
            $ApiKeys->createKnown($user['api_key']);
        }
        if (isset($user['user_preferences'])) {
            $Users->patch(Action::Update, $user['user_preferences']);
        }

        if ($user['create_templates'] ?? false) {
            $Templates = new Templates($Users);
            for ($i = 0; $i <= self::TEMPLATES_ITER; $i++) {
                $id = $Templates->create($this->faker->sentence());
                $Templates->setId($id);
                $Templates->patch(Action::Update, array('body' => $this->faker->realText(1000)));
            }
        }
    }
}