Skip to content

Populations API

Description

A population is a group of plants or a species, identified by a unique name (population_name). Populations can be associated with multiple Experiments and Plots.

Module

This module defines the Population class, which represents a named germplasm grouping (e.g. a diversity panel, RIL population) in the Gemini API.

Population

Bases: APIBase

Source code in gemini/api/population.py
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
class Population(APIBase):

    id: Optional[ID] = Field(None, validation_alias=AliasChoices("id", "population_id"))

    population_name: str
    population_type: Optional[str] = None
    species: Optional[str] = None
    population_info: Optional[dict] = None

    def __str__(self):
        return f"Population(population_name={self.population_name}, id={self.id})"

    def __repr__(self):
        return f"Population(population_name={self.population_name}, id={self.id})"

    @classmethod
    def exists(
        cls,
        population_name: str,
    ) -> bool:
        """
        Check if a population with the given population and accession exists.

        Examples:
            >>> Population.exists("Wheat", "Accession123")
            True

            >>> Population.exists("Corn", "Accession456")
            False

        Args:
            population_name (str): The population of the population.
            population_name (str): The accession number of the population.

        Returns:
            bool: True if the population exists, False otherwise.
        """
        try:
            return PopulationModel.exists(population_name=population_name)
        except Exception as e:
            logger.error(f"Error checking the existence of population: {e}")
            return False

    @classmethod
    def create(
        cls,
        population_name: str,
        population_type: str = None,
        species: str = None,
        population_info: dict = None,
        experiment_name: str = None,
    ) -> Optional["Population"]:
        """
        Create a new population. If the population already exists, it will return the existing one.

        Examples:
            >>> population = Population.create("Wheat", "Accession123")
            >>> print(population)
            Population(population_name=Wheat, population_name=Accession123, id=UUID(...))

            >>> population = Population.create("Corn", "Accession456", {"info": "test"}, "Experiment1")
            >>> print(population)
            Population(population_name=Corn, population_name=Accession456, id=UUID(...))

        Args:
            population_name (str): The population of the population.
            population_name (str): The accession number of the population.
            population_info (dict, optional): Additional information about the population. Defaults to {}.
            experiment_name (str, optional): The name of the experiment to associate the population with. Defaults to None.

        Returns:
            Optional["Population"]: The created population, or None if an error occurred.
        """
        try:
            db_instance = PopulationModel.get_or_create(
                population_name=population_name,
                population_type=population_type,
                species=species,
                population_info=population_info,
            )
            population = cls.model_validate(db_instance)
            # Associate with experiment if provided
            if experiment_name:
                population.associate_experiment(experiment_name)
            return population
        except Exception as e:
            logger.error(f"Error creating population: {e}")
            return None

    @classmethod
    def get(cls, population_name: str, experiment_name: str = None) -> Optional["Population"]:
        try:
            db_instance = ExperimentPopulationsViewModel.get_by_parameters(
                population_name=population_name,
                experiment_name=experiment_name,
            )
            if not db_instance:
                logger.debug(f"Population with name {population_name} not found.")
                return None
            population = cls.model_validate(db_instance)
            return population
        except Exception as e:
            logger.error(f"Error getting population: {e}")
            return None

    @classmethod
    def get_by_id(cls, id: UUID | int | str) -> Optional["Population"]:
        """
        Get a population by its ID.

        Examples:
            >>> population = Population.get_by_id(UUID(...))
            >>> print(population)
            Population(population_name=Wheat, population_name=Accession123, id=UUID(...))

        Args:
            id (UUID | int | str): The ID of the population.

        Returns:
            Optional["Population"]: The population, or None if not found.
        """
        try:
            db_instance = PopulationModel.get(id)
            if not db_instance:
                logger.warning(f"Population with ID {id} does not exist.")
                return None
            population = cls.model_validate(db_instance)
            return population
        except Exception as e:
            logger.error(f"Error getting population by ID: {e}")
            return None

    @classmethod
    def get_all(cls, limit: int = None, offset: int = None) -> Optional[List["Population"]]:
        """
        Get all populations.

        Examples:
            >>> populations = Population.get_all()
            >>> for population in populations:
            ...     print(population)
            Population(population_name=Wheat, population_name=Accession123, id=UUID(...))
            Population(population_name=Corn, population_name=Accession456, id=UUID(...))


        Returns:
            Optional[List["Population"]]: A list of all populations, or None if an error occurred.
        """
        try:
            populations = PopulationModel.all(limit=limit, offset=offset)
            if not populations or len(populations) == 0:
                logger.info("No populations found.")
                return None
            populations = [cls.model_validate(population) for population in populations]
            return populations
        except Exception as e:
            logger.error(f"Error getting all populations: {e}")
            return None

    @classmethod
    def search(
        cls,
        population_name: str = None,
        population_type: str = None,
        species: str = None,
        population_info: dict = None,
        experiment_name: str = None,
    ) -> Optional[List["Population"]]:
        try:
            if not any([experiment_name, population_name, population_type, species, population_info]):
                logger.warning("At least one search parameter must be provided.")
                return None
            populations = ExperimentPopulationsViewModel.search(
                experiment_name=experiment_name,
                population_name=population_name,
                population_type=population_type,
                species=species,
                population_info=population_info,
            )
            if not populations or len(populations) == 0:
                logger.info("No populations found with the provided search parameters.")
                return None
            populations = [cls.model_validate(population) for population in populations]
            return populations
        except Exception as e:
            logger.error(f"Error searching populations: {e}")
            return None

    def update(
        self,
        population_name: str = None,
        population_type: str = None,
        species: str = None,
        population_info: dict = None,
    ) -> Optional["Population"]:
        try:
            if not any([population_name, population_type, species, population_info]):
                logger.warning("At least one parameter must be provided for update.")
                return None
            current_id = self.id
            population = PopulationModel.get(current_id)
            if not population:
                logger.warning(f"Population with ID {current_id} does not exist.")
                return None
            population = PopulationModel.update(
                population,
                population_name=population_name,
                population_type=population_type,
                species=species,
                population_info=population_info,
            )
            population = self.model_validate(population)
            self.refresh()
            return population
        except Exception as e:
            logger.error(f"Error updating population: {e}")
            return None

    def delete(self) -> bool:
        """
        Delete the population.

        Examples:
            >>> population = Population.get("Wheat", "Accession123")
            >>> success = population.delete()
            >>> print(success)
            True

        Returns:
            bool: True if the population was deleted successfully, False otherwise.
        """
        try:
            current_id = self.id
            population = PopulationModel.get(current_id)
            if not population:
                logger.warning(f"Population with ID {current_id} does not exist.")
                return False
            PopulationModel.delete(population)
            return True
        except Exception as e:
            return False


    def refresh(self) -> Optional["Population"]:
        """
        Refresh the population's data from the database. It is rarely needed to be called by the user,
        as the data is automatically refreshed when accessed.

        Examples:
            >>> population = Population.get("Wheat", "Accession123")
            >>> refreshed_population = population.refresh()
            >>> print(refreshed_population)
            Population(population_name=Wheat, population_name=Accession123, id=UUID(...))

        Returns:
            Optional["Population"]: The refreshed population, or None if an error occurred.
        """
        try:
            db_instance = PopulationModel.get(self.id)
            if not db_instance:
                logger.warning(f"Population with ID {self.id} does not exist.")
                return self
            instance = self.model_validate(db_instance)
            for key, value in instance.model_dump().items():
                if hasattr(self, key) and key != "id":
                    setattr(self, key, value)
            return self
        except Exception as e:
            logger.error(f"Error refreshing population: {e}")
            return None

    def get_info(self) -> Optional[dict]:
        """
        Get the additional information of the population.

        Examples:
            >>> population = Population.get("Wheat", "Accession123")
            >>> info = population.get_info()
            >>> print(info)
            {'key1': 'value1', 'key2': 'value2'}

        Returns:
            Optional[dict]: The population's information, or None if not found.
        """
        try:
            current_id = self.id
            population = PopulationModel.get(current_id)
            if not population:
                logger.warning(f"Population with ID {current_id} does not exist.")
                return None
            population_info = population.population_info
            if not population_info:
                logger.info("Population info is empty.")
                return None
            return population_info
        except Exception as e:
            logger.error(f"Error getting population info: {e}")
            return None

    def set_info(self, population_info: dict) -> Optional["Population"]:
        """
        Set the additional information of the population.

        Examples:
            >>> population = Population.get("Wheat", "Accession123")
            >>> updated_population = population.set_info({"key1": "value1", "key2": "value2"})
            >>> print(updated_population.get_info())
            {'key1': 'value1', 'key2': 'value2'}

        Args:
            population_info (dict): The new information to set.

        Returns:
            Optional["Population"]: The updated population, or None if an error occurred.
        """
        try:
            current_id = self.id
            population = PopulationModel.get(current_id)
            if not population:
                logger.warning(f"Population with ID {current_id} does not exist.")
                return None
            population = PopulationModel.update(
                population,
                population_info=population_info
            )
            population = self.model_validate(population)
            self.refresh()
            return population
        except Exception as e:
            logger.error(f"Error setting population info: {e}")
            return None

    def get_associated_experiments(self) -> Optional[List["Experiment"]]:
        """
        Get all experiments associated with the population. Which are the experiments
        that have this population as part of their population.

        Examples:
            >>> population = Population.get("Wheat", "Accession123")
            >>> experiments = population.get_associated_experiments()
            >>> for experiment in experiments:
            ...     print(experiment)
            Experiment(experiment_name=Experiment1, experiment_start_date=2023-01-01, experiment_end_date=2023-12-31, id=UUID(...))


        Returns:
            A list of associated experiments, or None if an error occurred.
        """
        try:
            from gemini.api.experiment import Experiment
            current_id = self.id
            experiment_populations = ExperimentPopulationsViewModel.search(population_id=current_id)
            if not experiment_populations or len(experiment_populations) == 0:
                logger.info("No associated experiments found.")
                return None
            experiments = [Experiment.model_validate(experiment_population) for experiment_population in experiment_populations]
            return experiments
        except Exception as e:
            logger.error(f"Error getting associated experiments: {e}")
            return None

    def associate_experiment(self, experiment_name: str) -> Optional["Experiment"]:
        """
        Associate the population with an experiment. If the population is already associated with the experiment,
        it will return the experiment without creating a new association.

        Examples:
            >>> population = Population.get("Wheat", "Accession123")
            >>> experiment = population.associate_experiment("Experiment1")
            >>> print(experiment)
            Experiment(experiment_name=Experiment1, experiment_start_date=2023-01-01, experiment_end_date=2023-12-31, id=UUID(...))

        Args:
            experiment_name (str): The name of the experiment to associate with.

        Returns:
            The associated experiment, or None if an error occurred.
        """
        try:
            from gemini.api.experiment import Experiment
            experiment = Experiment.get(experiment_name=experiment_name)
            if not experiment:
                logger.warning(f"Experiment {experiment_name} does not exist.")
                return None
            existing_association = ExperimentPopulationModel.get_by_parameters(
                experiment_id=experiment.id,
                population_id=self.id
            )
            if existing_association:
                logger.info(f"Population {self.population_name} is already associated with experiment {experiment_name}.")
                return experiment
            new_association = ExperimentPopulationModel.get_or_create(
                experiment_id=experiment.id,
                population_id=self.id
            )
            if not new_association:
                logger.info(f"Failed to associate population {self.population_name} with experiment {experiment_name}.")
                return None
            self.refresh()
            return experiment
        except Exception as e:
            logger.error(f"Error associating population with experiment: {e}")
            return None

    def unassociate_experiment(self, experiment_name: str) -> Optional["Experiment"]:
        """
        Unassociate the population from an experiment. If the population is not associated with the experiment,
        it will return None without making any changes.

        Examples:
            >>> population = Population.get("Wheat", "Accession123")
            >>> experiment = population.unassociate_experiment("Experiment1")
            >>> print(experiment)
            Experiment(experiment_name=Experiment1, experiment_start_date=2023-01-01, experiment_end_date=2023-12-31, id=UUID(...))

        Args:
            experiment_name (str): The name of the experiment to unassociate from.

        Returns:
            The unassociated experiment, or None if an error occurred.
        """
        try:
            from gemini.api.experiment import Experiment
            experiment = Experiment.get(experiment_name=experiment_name)
            if not experiment:
                logger.warning(f"Experiment {experiment_name} does not exist.")
                return None
            existing_association = ExperimentPopulationModel.get_by_parameters(
                experiment_id=experiment.id,
                population_id=self.id
            )
            if not existing_association:
                logger.info(f"Population {self.population_name} is not associated with experiment {experiment_name}.")
                return None
            is_deleted = ExperimentPopulationModel.delete(existing_association)
            if not is_deleted:
                logger.info(f"Failed to unassociate population {self.population_name} from experiment {experiment_name}.")
                return None
            self.refresh()
            return experiment
        except Exception as e:
            logger.error(f"Error unassociating population from experiment: {e}")
            return None

    def belongs_to_experiment(self, experiment_name: str) -> bool:
        """
        Check if the population belongs to a specific experiment.

        Examples:
            >>> population = Population.get("Wheat", "Accession123")
            >>> belongs = population.belongs_to_experiment("Experiment1")
            >>> print(belongs)
            True

            >>> belongs = population.belongs_to_experiment("NonExistentExperiment")
            >>> print(belongs)
            False 

        Args:
            experiment_name (str): The name of the experiment.

        Returns:
            bool: True if the population belongs to the experiment, False otherwise.
        """
        try:
            from gemini.api.experiment import Experiment
            experiment = Experiment.get(experiment_name=experiment_name)
            if not experiment:
                logger.warning(f"Experiment {experiment_name} does not exist.")
                return False
            association_exists = ExperimentPopulationModel.exists(
                experiment_id=experiment.id,
                population_id=self.id
            )
            return association_exists
        except Exception as e:
            logger.error(f"Error checking if population belongs to experiment: {e}")
            return False

    def get_associated_accessions(self) -> Optional[List["Accession"]]:
        try:
            from gemini.api.accession import Accession
            from gemini.db.models.accessions import AccessionModel
            assocs = PopulationAccessionModel.search(population_id=self.id)
            if not assocs or len(assocs) == 0:
                return None
            accessions = []
            for assoc in assocs:
                acc = AccessionModel.get(assoc.accession_id)
                if acc:
                    accessions.append(Accession.model_validate(acc))
            return accessions if accessions else None
        except Exception as e:
            logger.error(f"Error getting associated accessions: {e}")
            return None

    def associate_accession(self, accession_name: str) -> Optional["Accession"]:
        try:
            from gemini.api.accession import Accession
            accession = Accession.get(accession_name=accession_name)
            if not accession:
                logger.warning(f"Accession {accession_name} does not exist.")
                return None
            existing = PopulationAccessionModel.get_by_parameters(
                population_id=self.id,
                accession_id=accession.id,
            )
            if existing:
                return accession
            PopulationAccessionModel.get_or_create(
                population_id=self.id,
                accession_id=accession.id,
            )
            self.refresh()
            return accession
        except Exception as e:
            logger.error(f"Error associating population with accession: {e}")
            return None

    def unassociate_accession(self, accession_name: str) -> Optional["Accession"]:
        try:
            from gemini.api.accession import Accession
            accession = Accession.get(accession_name=accession_name)
            if not accession:
                return None
            existing = PopulationAccessionModel.get_by_parameters(
                population_id=self.id,
                accession_id=accession.id,
            )
            if not existing:
                return None
            PopulationAccessionModel.delete(existing)
            self.refresh()
            return accession
        except Exception as e:
            logger.error(f"Error unassociating population from accession: {e}")
            return None

associate_experiment(experiment_name)

Associate the population with an experiment. If the population is already associated with the experiment, it will return the experiment without creating a new association.

Examples:

>>> population = Population.get("Wheat", "Accession123")
>>> experiment = population.associate_experiment("Experiment1")
>>> print(experiment)
Experiment(experiment_name=Experiment1, experiment_start_date=2023-01-01, experiment_end_date=2023-12-31, id=UUID(...))

Parameters:

Name Type Description Default
experiment_name str

The name of the experiment to associate with.

required

Returns:

Type Description
Optional[Experiment]

The associated experiment, or None if an error occurred.

Source code in gemini/api/population.py
def associate_experiment(self, experiment_name: str) -> Optional["Experiment"]:
    """
    Associate the population with an experiment. If the population is already associated with the experiment,
    it will return the experiment without creating a new association.

    Examples:
        >>> population = Population.get("Wheat", "Accession123")
        >>> experiment = population.associate_experiment("Experiment1")
        >>> print(experiment)
        Experiment(experiment_name=Experiment1, experiment_start_date=2023-01-01, experiment_end_date=2023-12-31, id=UUID(...))

    Args:
        experiment_name (str): The name of the experiment to associate with.

    Returns:
        The associated experiment, or None if an error occurred.
    """
    try:
        from gemini.api.experiment import Experiment
        experiment = Experiment.get(experiment_name=experiment_name)
        if not experiment:
            logger.warning(f"Experiment {experiment_name} does not exist.")
            return None
        existing_association = ExperimentPopulationModel.get_by_parameters(
            experiment_id=experiment.id,
            population_id=self.id
        )
        if existing_association:
            logger.info(f"Population {self.population_name} is already associated with experiment {experiment_name}.")
            return experiment
        new_association = ExperimentPopulationModel.get_or_create(
            experiment_id=experiment.id,
            population_id=self.id
        )
        if not new_association:
            logger.info(f"Failed to associate population {self.population_name} with experiment {experiment_name}.")
            return None
        self.refresh()
        return experiment
    except Exception as e:
        logger.error(f"Error associating population with experiment: {e}")
        return None

belongs_to_experiment(experiment_name)

Check if the population belongs to a specific experiment.

Examples:

>>> population = Population.get("Wheat", "Accession123")
>>> belongs = population.belongs_to_experiment("Experiment1")
>>> print(belongs)
True
>>> belongs = population.belongs_to_experiment("NonExistentExperiment")
>>> print(belongs)
False 

Parameters:

Name Type Description Default
experiment_name str

The name of the experiment.

required

Returns:

Name Type Description
bool bool

True if the population belongs to the experiment, False otherwise.

Source code in gemini/api/population.py
def belongs_to_experiment(self, experiment_name: str) -> bool:
    """
    Check if the population belongs to a specific experiment.

    Examples:
        >>> population = Population.get("Wheat", "Accession123")
        >>> belongs = population.belongs_to_experiment("Experiment1")
        >>> print(belongs)
        True

        >>> belongs = population.belongs_to_experiment("NonExistentExperiment")
        >>> print(belongs)
        False 

    Args:
        experiment_name (str): The name of the experiment.

    Returns:
        bool: True if the population belongs to the experiment, False otherwise.
    """
    try:
        from gemini.api.experiment import Experiment
        experiment = Experiment.get(experiment_name=experiment_name)
        if not experiment:
            logger.warning(f"Experiment {experiment_name} does not exist.")
            return False
        association_exists = ExperimentPopulationModel.exists(
            experiment_id=experiment.id,
            population_id=self.id
        )
        return association_exists
    except Exception as e:
        logger.error(f"Error checking if population belongs to experiment: {e}")
        return False

create(population_name, population_type=None, species=None, population_info=None, experiment_name=None) classmethod

Create a new population. If the population already exists, it will return the existing one.

Examples:

>>> population = Population.create("Wheat", "Accession123")
>>> print(population)
Population(population_name=Wheat, population_name=Accession123, id=UUID(...))
>>> population = Population.create("Corn", "Accession456", {"info": "test"}, "Experiment1")
>>> print(population)
Population(population_name=Corn, population_name=Accession456, id=UUID(...))

Parameters:

Name Type Description Default
population_name str

The population of the population.

required
population_name str

The accession number of the population.

required
population_info dict

Additional information about the population. Defaults to {}.

None
experiment_name str

The name of the experiment to associate the population with. Defaults to None.

None

Returns:

Type Description
Optional[Population]

Optional["Population"]: The created population, or None if an error occurred.

Source code in gemini/api/population.py
@classmethod
def create(
    cls,
    population_name: str,
    population_type: str = None,
    species: str = None,
    population_info: dict = None,
    experiment_name: str = None,
) -> Optional["Population"]:
    """
    Create a new population. If the population already exists, it will return the existing one.

    Examples:
        >>> population = Population.create("Wheat", "Accession123")
        >>> print(population)
        Population(population_name=Wheat, population_name=Accession123, id=UUID(...))

        >>> population = Population.create("Corn", "Accession456", {"info": "test"}, "Experiment1")
        >>> print(population)
        Population(population_name=Corn, population_name=Accession456, id=UUID(...))

    Args:
        population_name (str): The population of the population.
        population_name (str): The accession number of the population.
        population_info (dict, optional): Additional information about the population. Defaults to {}.
        experiment_name (str, optional): The name of the experiment to associate the population with. Defaults to None.

    Returns:
        Optional["Population"]: The created population, or None if an error occurred.
    """
    try:
        db_instance = PopulationModel.get_or_create(
            population_name=population_name,
            population_type=population_type,
            species=species,
            population_info=population_info,
        )
        population = cls.model_validate(db_instance)
        # Associate with experiment if provided
        if experiment_name:
            population.associate_experiment(experiment_name)
        return population
    except Exception as e:
        logger.error(f"Error creating population: {e}")
        return None

delete()

Delete the population.

Examples:

>>> population = Population.get("Wheat", "Accession123")
>>> success = population.delete()
>>> print(success)
True

Returns:

Name Type Description
bool bool

True if the population was deleted successfully, False otherwise.

Source code in gemini/api/population.py
def delete(self) -> bool:
    """
    Delete the population.

    Examples:
        >>> population = Population.get("Wheat", "Accession123")
        >>> success = population.delete()
        >>> print(success)
        True

    Returns:
        bool: True if the population was deleted successfully, False otherwise.
    """
    try:
        current_id = self.id
        population = PopulationModel.get(current_id)
        if not population:
            logger.warning(f"Population with ID {current_id} does not exist.")
            return False
        PopulationModel.delete(population)
        return True
    except Exception as e:
        return False

exists(population_name) classmethod

Check if a population with the given population and accession exists.

Examples:

>>> Population.exists("Wheat", "Accession123")
True
>>> Population.exists("Corn", "Accession456")
False

Parameters:

Name Type Description Default
population_name str

The population of the population.

required
population_name str

The accession number of the population.

required

Returns:

Name Type Description
bool bool

True if the population exists, False otherwise.

Source code in gemini/api/population.py
@classmethod
def exists(
    cls,
    population_name: str,
) -> bool:
    """
    Check if a population with the given population and accession exists.

    Examples:
        >>> Population.exists("Wheat", "Accession123")
        True

        >>> Population.exists("Corn", "Accession456")
        False

    Args:
        population_name (str): The population of the population.
        population_name (str): The accession number of the population.

    Returns:
        bool: True if the population exists, False otherwise.
    """
    try:
        return PopulationModel.exists(population_name=population_name)
    except Exception as e:
        logger.error(f"Error checking the existence of population: {e}")
        return False

get_all(limit=None, offset=None) classmethod

Get all populations.

Examples:

>>> populations = Population.get_all()
>>> for population in populations:
...     print(population)
Population(population_name=Wheat, population_name=Accession123, id=UUID(...))
Population(population_name=Corn, population_name=Accession456, id=UUID(...))

Returns:

Type Description
Optional[List[Population]]

Optional[List["Population"]]: A list of all populations, or None if an error occurred.

Source code in gemini/api/population.py
@classmethod
def get_all(cls, limit: int = None, offset: int = None) -> Optional[List["Population"]]:
    """
    Get all populations.

    Examples:
        >>> populations = Population.get_all()
        >>> for population in populations:
        ...     print(population)
        Population(population_name=Wheat, population_name=Accession123, id=UUID(...))
        Population(population_name=Corn, population_name=Accession456, id=UUID(...))


    Returns:
        Optional[List["Population"]]: A list of all populations, or None if an error occurred.
    """
    try:
        populations = PopulationModel.all(limit=limit, offset=offset)
        if not populations or len(populations) == 0:
            logger.info("No populations found.")
            return None
        populations = [cls.model_validate(population) for population in populations]
        return populations
    except Exception as e:
        logger.error(f"Error getting all populations: {e}")
        return None

get_associated_experiments()

Get all experiments associated with the population. Which are the experiments that have this population as part of their population.

Examples:

>>> population = Population.get("Wheat", "Accession123")
>>> experiments = population.get_associated_experiments()
>>> for experiment in experiments:
...     print(experiment)
Experiment(experiment_name=Experiment1, experiment_start_date=2023-01-01, experiment_end_date=2023-12-31, id=UUID(...))

Returns:

Type Description
Optional[List[Experiment]]

A list of associated experiments, or None if an error occurred.

Source code in gemini/api/population.py
def get_associated_experiments(self) -> Optional[List["Experiment"]]:
    """
    Get all experiments associated with the population. Which are the experiments
    that have this population as part of their population.

    Examples:
        >>> population = Population.get("Wheat", "Accession123")
        >>> experiments = population.get_associated_experiments()
        >>> for experiment in experiments:
        ...     print(experiment)
        Experiment(experiment_name=Experiment1, experiment_start_date=2023-01-01, experiment_end_date=2023-12-31, id=UUID(...))


    Returns:
        A list of associated experiments, or None if an error occurred.
    """
    try:
        from gemini.api.experiment import Experiment
        current_id = self.id
        experiment_populations = ExperimentPopulationsViewModel.search(population_id=current_id)
        if not experiment_populations or len(experiment_populations) == 0:
            logger.info("No associated experiments found.")
            return None
        experiments = [Experiment.model_validate(experiment_population) for experiment_population in experiment_populations]
        return experiments
    except Exception as e:
        logger.error(f"Error getting associated experiments: {e}")
        return None

get_by_id(id) classmethod

Get a population by its ID.

Examples:

>>> population = Population.get_by_id(UUID(...))
>>> print(population)
Population(population_name=Wheat, population_name=Accession123, id=UUID(...))

Parameters:

Name Type Description Default
id UUID | int | str

The ID of the population.

required

Returns:

Type Description
Optional[Population]

Optional["Population"]: The population, or None if not found.

Source code in gemini/api/population.py
@classmethod
def get_by_id(cls, id: UUID | int | str) -> Optional["Population"]:
    """
    Get a population by its ID.

    Examples:
        >>> population = Population.get_by_id(UUID(...))
        >>> print(population)
        Population(population_name=Wheat, population_name=Accession123, id=UUID(...))

    Args:
        id (UUID | int | str): The ID of the population.

    Returns:
        Optional["Population"]: The population, or None if not found.
    """
    try:
        db_instance = PopulationModel.get(id)
        if not db_instance:
            logger.warning(f"Population with ID {id} does not exist.")
            return None
        population = cls.model_validate(db_instance)
        return population
    except Exception as e:
        logger.error(f"Error getting population by ID: {e}")
        return None

get_info()

Get the additional information of the population.

Examples:

>>> population = Population.get("Wheat", "Accession123")
>>> info = population.get_info()
>>> print(info)
{'key1': 'value1', 'key2': 'value2'}

Returns:

Type Description
Optional[dict]

Optional[dict]: The population's information, or None if not found.

Source code in gemini/api/population.py
def get_info(self) -> Optional[dict]:
    """
    Get the additional information of the population.

    Examples:
        >>> population = Population.get("Wheat", "Accession123")
        >>> info = population.get_info()
        >>> print(info)
        {'key1': 'value1', 'key2': 'value2'}

    Returns:
        Optional[dict]: The population's information, or None if not found.
    """
    try:
        current_id = self.id
        population = PopulationModel.get(current_id)
        if not population:
            logger.warning(f"Population with ID {current_id} does not exist.")
            return None
        population_info = population.population_info
        if not population_info:
            logger.info("Population info is empty.")
            return None
        return population_info
    except Exception as e:
        logger.error(f"Error getting population info: {e}")
        return None

refresh()

Refresh the population's data from the database. It is rarely needed to be called by the user, as the data is automatically refreshed when accessed.

Examples:

>>> population = Population.get("Wheat", "Accession123")
>>> refreshed_population = population.refresh()
>>> print(refreshed_population)
Population(population_name=Wheat, population_name=Accession123, id=UUID(...))

Returns:

Type Description
Optional[Population]

Optional["Population"]: The refreshed population, or None if an error occurred.

Source code in gemini/api/population.py
def refresh(self) -> Optional["Population"]:
    """
    Refresh the population's data from the database. It is rarely needed to be called by the user,
    as the data is automatically refreshed when accessed.

    Examples:
        >>> population = Population.get("Wheat", "Accession123")
        >>> refreshed_population = population.refresh()
        >>> print(refreshed_population)
        Population(population_name=Wheat, population_name=Accession123, id=UUID(...))

    Returns:
        Optional["Population"]: The refreshed population, or None if an error occurred.
    """
    try:
        db_instance = PopulationModel.get(self.id)
        if not db_instance:
            logger.warning(f"Population with ID {self.id} does not exist.")
            return self
        instance = self.model_validate(db_instance)
        for key, value in instance.model_dump().items():
            if hasattr(self, key) and key != "id":
                setattr(self, key, value)
        return self
    except Exception as e:
        logger.error(f"Error refreshing population: {e}")
        return None

set_info(population_info)

Set the additional information of the population.

Examples:

>>> population = Population.get("Wheat", "Accession123")
>>> updated_population = population.set_info({"key1": "value1", "key2": "value2"})
>>> print(updated_population.get_info())
{'key1': 'value1', 'key2': 'value2'}

Parameters:

Name Type Description Default
population_info dict

The new information to set.

required

Returns:

Type Description
Optional[Population]

Optional["Population"]: The updated population, or None if an error occurred.

Source code in gemini/api/population.py
def set_info(self, population_info: dict) -> Optional["Population"]:
    """
    Set the additional information of the population.

    Examples:
        >>> population = Population.get("Wheat", "Accession123")
        >>> updated_population = population.set_info({"key1": "value1", "key2": "value2"})
        >>> print(updated_population.get_info())
        {'key1': 'value1', 'key2': 'value2'}

    Args:
        population_info (dict): The new information to set.

    Returns:
        Optional["Population"]: The updated population, or None if an error occurred.
    """
    try:
        current_id = self.id
        population = PopulationModel.get(current_id)
        if not population:
            logger.warning(f"Population with ID {current_id} does not exist.")
            return None
        population = PopulationModel.update(
            population,
            population_info=population_info
        )
        population = self.model_validate(population)
        self.refresh()
        return population
    except Exception as e:
        logger.error(f"Error setting population info: {e}")
        return None

unassociate_experiment(experiment_name)

Unassociate the population from an experiment. If the population is not associated with the experiment, it will return None without making any changes.

Examples:

>>> population = Population.get("Wheat", "Accession123")
>>> experiment = population.unassociate_experiment("Experiment1")
>>> print(experiment)
Experiment(experiment_name=Experiment1, experiment_start_date=2023-01-01, experiment_end_date=2023-12-31, id=UUID(...))

Parameters:

Name Type Description Default
experiment_name str

The name of the experiment to unassociate from.

required

Returns:

Type Description
Optional[Experiment]

The unassociated experiment, or None if an error occurred.

Source code in gemini/api/population.py
def unassociate_experiment(self, experiment_name: str) -> Optional["Experiment"]:
    """
    Unassociate the population from an experiment. If the population is not associated with the experiment,
    it will return None without making any changes.

    Examples:
        >>> population = Population.get("Wheat", "Accession123")
        >>> experiment = population.unassociate_experiment("Experiment1")
        >>> print(experiment)
        Experiment(experiment_name=Experiment1, experiment_start_date=2023-01-01, experiment_end_date=2023-12-31, id=UUID(...))

    Args:
        experiment_name (str): The name of the experiment to unassociate from.

    Returns:
        The unassociated experiment, or None if an error occurred.
    """
    try:
        from gemini.api.experiment import Experiment
        experiment = Experiment.get(experiment_name=experiment_name)
        if not experiment:
            logger.warning(f"Experiment {experiment_name} does not exist.")
            return None
        existing_association = ExperimentPopulationModel.get_by_parameters(
            experiment_id=experiment.id,
            population_id=self.id
        )
        if not existing_association:
            logger.info(f"Population {self.population_name} is not associated with experiment {experiment_name}.")
            return None
        is_deleted = ExperimentPopulationModel.delete(existing_association)
        if not is_deleted:
            logger.info(f"Failed to unassociate population {self.population_name} from experiment {experiment_name}.")
            return None
        self.refresh()
        return experiment
    except Exception as e:
        logger.error(f"Error unassociating population from experiment: {e}")
        return None