"""Provides personal data."""
import hashlib
import re
import typing as t
import uuid
from datetime import date, datetime
from string import ascii_letters, digits, punctuation
from mimesis.datasets import (
BLOOD_GROUPS,
CALLING_CODES,
EMAIL_DOMAINS,
GENDER_CODES,
GENDER_SYMBOLS,
USERNAMES,
)
from mimesis.enums import Gender, TitleType
from mimesis.providers.base import BaseDataProvider
from mimesis.types import Date
__all__ = ["Person"]
[docs]
class Person(BaseDataProvider):
"""Class for generating personal data."""
[docs]
def __init__(self, *args: t.Any, **kwargs: t.Any) -> None:
"""Initialize attributes.
:param locale: Current locale.
:param seed: Seed.
"""
super().__init__(*args, **kwargs)
class Meta:
name = "person"
datafile = f"{name}.json"
def _validate_birth_year_params(self, min_year: int, max_year: int) -> None:
if min_year > max_year:
raise ValueError("min_year must be less than or equal to max_year")
if min_year < 1900:
raise ValueError("min_year must be greater than or equal to 1900")
if max_year > datetime.now().year:
raise ValueError(
"The max_year must be less than or equal to the current year"
)
def _is_leap_year(self, year: int) -> bool:
return (year % 4 == 0 and year % 100 != 0) or (year % 400 == 0)
[docs]
def birthdate(self, min_year: int = 1980, max_year: int = 2023) -> Date:
"""Generates a random birthdate as a :py:class:`datetime.date` object.
:param min_year: Maximum birth year.
:param max_year: Minimum birth year.
:return: Random date object.
"""
self._validate_birth_year_params(min_year, max_year)
year = self.random.randint(min_year, max_year)
feb_days = 29 if self._is_leap_year(year) else 28
month_days_map = {
1: 31,
2: feb_days,
3: 31,
4: 30,
5: 31,
6: 30,
7: 31,
8: 31,
9: 30,
10: 31,
11: 30,
12: 31,
}
month = self.random.randint(1, 12)
max_day = month_days_map[month]
day = self.random.randint(1, max_day)
return date(year=year, month=month, day=day)
[docs]
def name(self, gender: Gender | None = None) -> str:
"""Generates a random name.
:param gender: Gender's enum object.
:return: Name.
:Example:
John.
"""
key = self.validate_enum(gender, Gender)
names: list[str] = self._extract(["names", key])
return self.random.choice(names)
[docs]
def first_name(self, gender: Gender | None = None) -> str:
"""Generates a random first name.
..note: An alias for :meth:`~.name`.
:param gender: Gender's enum object.
:return: First name.
"""
return self.name(gender)
[docs]
def surname(self, gender: Gender | None = None) -> str:
"""Generates a random surname.
:param gender: Gender's enum object.
:return: Surname.
:Example:
Smith.
"""
surnames: t.Sequence[str] = self._extract(["surnames"])
# Surnames separated by gender.
if isinstance(surnames, dict):
key = self.validate_enum(gender, Gender)
surnames = surnames[key]
return self.random.choice(surnames)
[docs]
def last_name(self, gender: Gender | None = None) -> str:
"""Generates a random last name.
..note: An alias for :meth:`~.surname`.
:param gender: Gender's enum object.
:return: Last name.
"""
return self.surname(gender)
[docs]
def title(
self,
gender: Gender | None = None,
title_type: TitleType | None = None,
) -> str:
"""Generates a random title for name.
You can generate a random prefix or suffix
for name using this method.
:param gender: The gender.
:param title_type: TitleType enum object.
:return: The title.
:raises NonEnumerableError: if gender or title_type in incorrect format.
:Example:
PhD.
"""
gender_key = self.validate_enum(gender, Gender)
title_key = self.validate_enum(title_type, TitleType)
titles: list[str] = self._extract(["title", gender_key, title_key])
return self.random.choice(titles)
[docs]
def full_name(
self,
gender: Gender | None = None,
reverse: bool = False,
) -> str:
"""Generates a random full name.
:param reverse: Return reversed full name.
:param gender: Gender's enum object.
:return: Full name.
:Example:
Johann Wolfgang.
"""
name = self.name(gender)
surname = self.surname(gender)
return f"{surname} {name}" if reverse else f"{name} {surname}"
[docs]
def username(
self, mask: str | None = None, drange: tuple[int, int] = (1800, 2100)
) -> str:
"""Generates a username by mask.
Masks allow you to generate a variety of usernames.
- **C** stands for capitalized username.
- **U** stands for uppercase username.
- **l** stands for lowercase username.
- **d** stands for digits in the username.
You can also use symbols to separate the different parts
of the username: **.** **_** **-**
:param mask: Mask.
:param drange: Digits range.
:raises ValueError: If template is not supported.
:return: Username as string.
Example:
>>> username(mask='C_C_d')
Cotte_Article_1923
>>> username(mask='U.l.d')
ELKINS.wolverine.2013
>>> username(mask='l_l_d', drange=(1900, 2021))
plasmic_blockader_1907
"""
if len(drange) != 2:
raise ValueError("The drange parameter must contain only two integers.")
if mask is None:
mask = "l_d"
required_tags = "CUl"
tags = re.findall(r"[CUld.\-_]", mask)
if not any(tag in tags for tag in required_tags):
raise ValueError(
"Username mask must contain at least one of these: (C, U, l)."
)
final_username = ""
for tag in tags:
username = self.random.choice(USERNAMES)
if tag == "C":
final_username += username.capitalize()
if tag == "U":
final_username += username.upper()
elif tag == "l":
final_username += username.lower()
elif tag == "d":
final_username += str(self.random.randint(*drange))
elif tag in "-_.":
final_username += tag
return final_username
[docs]
def password(self, length: int = 8, hashed: bool = False) -> str:
"""Generates a password or hash of password.
:param length: Length of password.
:param hashed: SHA256 hash.
:return: Password or hash of password.
:Example:
k6dv2odff9#4h
"""
characters = ascii_letters + digits + punctuation
password = "".join(self.random.choices(characters, k=length))
if hashed:
sha256 = hashlib.sha256()
sha256.update(password.encode())
return sha256.hexdigest()
return password
[docs]
def email(
self,
domains: t.Sequence[str] | None = None,
unique: bool = False,
) -> str:
"""Generates a random email.
:param domains: List of custom domains for emails.
:param unique: Makes email addresses unique.
:return: Email address.
:raises ValueError: if «unique» is True and the provider was seeded.
:Example:
foretime10@live.com
"""
if unique and self._has_seed():
raise ValueError(
"You cannot use «unique» parameter with the seeded provider"
)
if not domains:
domains = EMAIL_DOMAINS
domain = self.random.choice(domains)
if not domain.startswith("@"):
domain = "@" + domain
if unique:
name = str(uuid.uuid4().hex)
else:
name = self.username(mask="ld")
return f"{name}{domain}"
[docs]
def gender_symbol(self) -> str:
"""Generate a random sex symbol.
:Example:
♂
"""
return self.random.choice(GENDER_SYMBOLS)
[docs]
def gender_code(self) -> int:
"""Generate a random ISO/IEC 5218 gender code.
Generate a random title of gender code for the representation
of human sexes is an international standard that defines a
representation of human sexes through a language-neutral single-digit
code or symbol of gender.
Codes for the representation of human sexes is an international
standard (0 - not known, 1 - male, 2 - female, 9 - not applicable).
:return:
"""
return self.random.choice(GENDER_CODES)
[docs]
def gender(self) -> str:
"""Generates a random gender title.
:Example:
Male
"""
genders: list[str] = self._extract(["gender"])
return self.random.choice(genders)
[docs]
def sex(self) -> str:
"""An alias for method :meth:`~.gender`.
:return: Sex.
"""
return self.gender()
[docs]
def height(self, minimum: float = 1.5, maximum: float = 2.0) -> str:
"""Generates a random height in meters.
:param minimum: Minimum value.
:param float maximum: Maximum value.
:return: Height.
:Example:
1.85.
"""
h = self.random.uniform(minimum, maximum)
return f"{h:0.2f}"
[docs]
def weight(self, minimum: int = 38, maximum: int = 90) -> int:
"""Generates a random weight in Kg.
:param minimum: min value
:param maximum: max value
:return: Weight.
:Example:
48.
"""
return self.random.randint(minimum, maximum)
[docs]
def blood_type(self) -> str:
"""Generates a random blood type.
:return: Blood type (blood group).
:Example:
A+
"""
return self.random.choice(BLOOD_GROUPS)
[docs]
def occupation(self) -> str:
"""Generates a random job.
:return: The name of job.
:Example:
Programmer.
"""
jobs: list[str] = self._extract(["occupation"])
return self.random.choice(jobs)
[docs]
def political_views(self) -> str:
"""Get a random political views.
:return: Political views.
:Example:
Liberal.
"""
views: list[str] = self._extract(["political_views"])
return self.random.choice(views)
[docs]
def worldview(self) -> str:
"""Generates a random worldview.
:return: Worldview.
:Example:
Pantheism.
"""
views: list[str] = self._extract(["worldview"])
return self.random.choice(views)
[docs]
def views_on(self) -> str:
"""Get a random views on.
:return: Views on.
:Example:
Negative.
"""
views: list[str] = self._extract(["views_on"])
return self.random.choice(views)
[docs]
def nationality(self, gender: Gender | None = None) -> str:
"""Generates a random nationality.
:param gender: Gender.
:return: Nationality.
:Example:
Russian
"""
nationalities: list[str] = self._extract(["nationality"])
# Separated by gender
if isinstance(nationalities, dict):
key = self.validate_enum(gender, Gender)
nationalities = nationalities[key]
return self.random.choice(nationalities)
[docs]
def university(self) -> str:
"""Generates a random university name.
:return: University name.
:Example:
MIT.
"""
universities: list[str] = self._extract(["university"])
return self.random.choice(universities)
[docs]
def academic_degree(self) -> str:
"""Generates a random academic degree.
:return: Degree.
:Example:
Bachelor.
"""
degrees: list[str] = self._extract(["academic_degree"])
return self.random.choice(degrees)
[docs]
def language(self) -> str:
"""Generates a random language name.
:return: Random language.
:Example:
Irish.
"""
languages: list[str] = self._extract(["language"])
return self.random.choice(languages)
[docs]
def phone_number(self, mask: str = "", placeholder: str = "#") -> str:
"""Generates a random phone number.
:param mask: Mask for formatting number.
:param placeholder: A placeholder for a mask (default is #).
:return: Phone number.
:Example:
+7-(963)-409-11-22.
"""
if not mask:
code = self.random.choice(CALLING_CODES)
default = f"{code}-(###)-###-####"
masks = self._extract(["telephone_fmt"], default=[default])
mask = self.random.choice(masks)
return self.random.generate_string_by_mask(mask=mask, digit=placeholder)
[docs]
def telephone(self, *args: t.Any, **kwargs: t.Any) -> str:
"""An alias for :meth:`~.phone_number`."""
return self.phone_number(*args, **kwargs)
[docs]
def identifier(self, mask: str = "##-##/##") -> str:
"""Generates a random identifier by mask.
With this method, you can generate any identifiers that
you need by specifying the mask.
:param mask:
The mask. Here ``@`` is a placeholder for characters and ``#`` is
placeholder for digits.
:return: An identifier.
:Example:
07-97/04
"""
return self.random.generate_string_by_mask(mask=mask)