kopia lustrzana https://github.com/sepandhaghighi/samila
commit
f3de359440
17
CHANGELOG.md
17
CHANGELOG.md
|
@ -5,6 +5,20 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
|
|||
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
|
||||
|
||||
## [Unreleased]
|
||||
## [0.8] - 2022-06-01
|
||||
### Added
|
||||
- `INVALID_COLOR_TYPE_ERROR` error
|
||||
- `COLOR_NOT_FOUND_WARNING` warning
|
||||
- `BOTH_COLOR_COMPLEMENT_WARNING` warning
|
||||
- `set_background` function
|
||||
- `is_valid_color` function
|
||||
- `color_complement` function
|
||||
- `select_color` function
|
||||
### Changed
|
||||
- Transparent mode support for `bgcolor` parameter
|
||||
- Random mode modified
|
||||
- Complementary color support for `color` and `bgcolor` parameters
|
||||
- `filter_color` function modified
|
||||
## [0.7] - 2022-05-04
|
||||
### Added
|
||||
- `fill_data` function
|
||||
|
@ -109,7 +123,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.
|
|||
- `generate` method
|
||||
- `nft_storage` method
|
||||
|
||||
[Unreleased]: https://github.com/sepandhaghighi/samila/compare/v0.7...dev
|
||||
[Unreleased]: https://github.com/sepandhaghighi/samila/compare/v0.8...dev
|
||||
[0.8]: https://github.com/sepandhaghighi/samila/compare/v0.7...v0.8
|
||||
[0.7]: https://github.com/sepandhaghighi/samila/compare/v0.6...v0.7
|
||||
[0.6]: https://github.com/sepandhaghighi/samila/compare/v0.5...v0.6
|
||||
[0.5]: https://github.com/sepandhaghighi/samila/compare/v0.4...v0.5
|
||||
|
|
16
README.md
16
README.md
|
@ -88,7 +88,7 @@ Samila is a generative art generator written in Python, Samila let's you create
|
|||
|
||||
|
||||
### Source code
|
||||
- Download [Version 0.7](https://github.com/sepandhaghighi/samila/archive/v0.7.zip) or [Latest Source ](https://github.com/sepandhaghighi/samila/archive/dev.zip)
|
||||
- Download [Version 0.8](https://github.com/sepandhaghighi/samila/archive/v0.8.zip) or [Latest Source ](https://github.com/sepandhaghighi/samila/archive/dev.zip)
|
||||
- Run `pip install -r requirements.txt` or `pip3 install -r requirements.txt` (Need root access)
|
||||
- Run `python3 setup.py install` or `python setup.py install` (Need root access)
|
||||
|
||||
|
@ -96,7 +96,7 @@ Samila is a generative art generator written in Python, Samila let's you create
|
|||
|
||||
|
||||
- Check [Python Packaging User Guide](https://packaging.python.org/installing/)
|
||||
- Run `pip install samila==0.7` or `pip3 install samila==0.7` (Need root access)
|
||||
- Run `pip install samila==0.8` or `pip3 install samila==0.8` (Need root access)
|
||||
|
||||
### Easy install
|
||||
|
||||
|
@ -175,10 +175,14 @@ Samila is a generative art generator written in Python, Samila let's you create
|
|||
* Supported colors are available in `VALID_COLORS` list
|
||||
* `color` and `bgcolor` parameters supported formats:
|
||||
|
||||
1. Color name (example: `yellow`)
|
||||
2. RGB/RGBA (example: `(0.1,0.1,0.1)`, `(0.1,0.1,0.1,0.1)`)
|
||||
3. Hex (example: `#eeefff`)
|
||||
4. Random (example: `random`)
|
||||
1. Color name (example: `color="yellow"`)
|
||||
2. RGB/RGBA (example: `color=(0.1,0.1,0.1)`, `color=(0.1,0.1,0.1,0.1)`)
|
||||
3. Hex (example: `color="#eeefff"`)
|
||||
4. Random (example: `color="random"`)
|
||||
5. Complement (example: `color="complement", bgcolor="blue"`)
|
||||
6. Transparent (example: `bgcolor="transparent"`)
|
||||
|
||||
⚠️ **Transparent** mode is only available for background
|
||||
|
||||
### Regeneration
|
||||
```pycon
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
matplotlib==3.5.1
|
||||
matplotlib==3.5.2
|
||||
art==5.6
|
||||
vulture>=1.0
|
||||
bandit>=1.5.1
|
||||
|
|
|
@ -157,10 +157,14 @@
|
|||
"source": [
|
||||
"* `color` and `bgcolor` parameters supported formats:\n",
|
||||
"\n",
|
||||
" 1. Color name (example: `yellow`)\n",
|
||||
" 2. RGB/RGBA (example: `(0.1,0.1,0.1)`, `(0.1,0.1,0.1,0.1)`)\n",
|
||||
" 3. Hex (example: `#eeefff`)\n",
|
||||
" 4. Random (example: `random`)"
|
||||
" 1. Color name (example: `color=\"yellow\"`)\n",
|
||||
" 2. RGB/RGBA (example: `color=(0.1,0.1,0.1)`, `color=(0.1,0.1,0.1,0.1)`)\n",
|
||||
" 3. Hex (example: `color=\"#eeefff\"`)\n",
|
||||
" 4. Random (example: `color=\"random\"`)\n",
|
||||
" 5. Complement (example: `color=\"complement\", bgcolor=\"blue\"`)\n",
|
||||
" 6. Transparent (example: `bgcolor=\"transparent\"`)\n",
|
||||
"\n",
|
||||
"⚠️ **Transparent** mode is only available for background"
|
||||
]
|
||||
},
|
||||
{
|
||||
|
@ -351,4 +355,4 @@
|
|||
},
|
||||
"nbformat": 4,
|
||||
"nbformat_minor": 4
|
||||
}
|
||||
}
|
|
@ -4,7 +4,7 @@ import os
|
|||
import sys
|
||||
import codecs
|
||||
Failed = 0
|
||||
SAMILA_VERSION = "0.7"
|
||||
SAMILA_VERSION = "0.8"
|
||||
|
||||
|
||||
SETUP_ITEMS = [
|
||||
|
|
|
@ -13,9 +13,12 @@ from .params import DEFAULT_BACKGROUND_COLOR, DEFAULT_SPOT_SIZE, DEFAULT_PROJECT
|
|||
from .params import Projection, VALID_COLORS, HEX_COLOR_PATTERN, NFT_STORAGE_API, NFT_STORAGE_LINK, OVERVIEW
|
||||
from .params import DATA_TYPE_ERROR, CONFIG_TYPE_ERROR, PLOT_DATA_ERROR, CONFIG_NO_STR_FUNCTION_ERROR
|
||||
from .params import NO_FIG_ERROR_MESSAGE, FIG_SAVE_SUCCESS_MESSAGE, NFT_STORAGE_SUCCESS_MESSAGE, SAVE_NO_DATA_ERROR
|
||||
from .params import INVALID_COLOR_TYPE_ERROR
|
||||
from .params import BOTH_COLOR_COMPLEMENT_WARNING, COLOR_NOT_FOUND_WARNING
|
||||
from .params import DATA_SAVE_SUCCESS_MESSAGE, SEED_LOWER_BOUND, SEED_UPPER_BOUND
|
||||
from .params import ELEMENTS_LIST, ARGUMENTS_LIST, OPERATORS_LIST, RANDOM_COEF_LIST
|
||||
from .errors import samilaDataError, samilaPlotError, samilaConfigError
|
||||
from warnings import warn
|
||||
|
||||
|
||||
def random_equation_gen():
|
||||
|
@ -95,26 +98,114 @@ def distance_calc(s1, s2):
|
|||
return distances[-1]
|
||||
|
||||
|
||||
def filter_color(color):
|
||||
def is_valid_color(color):
|
||||
"""
|
||||
Filter given color and return it.
|
||||
Check that input color format is valid or not.
|
||||
|
||||
:param color: given color
|
||||
:type color: str or tuple
|
||||
:return: filtered version of color
|
||||
:type color: any format
|
||||
:return: result as bool
|
||||
"""
|
||||
if color == None:
|
||||
return True
|
||||
try:
|
||||
_ = matplotlib.colors.to_hex(color)
|
||||
return True
|
||||
except ValueError:
|
||||
return False
|
||||
|
||||
|
||||
def color_complement(color):
|
||||
"""
|
||||
Calculate complement color.
|
||||
|
||||
:param color: given color (hex format)
|
||||
:type color: str
|
||||
:return: complement color (hex format) as str
|
||||
"""
|
||||
color = color[1:]
|
||||
color = int(color, 16)
|
||||
comp_color = 0xFFFFFF ^ color
|
||||
comp_color = "#%06x" % comp_color
|
||||
return comp_color
|
||||
|
||||
|
||||
def filter_color(color, bgcolor):
|
||||
"""
|
||||
Filter given color and bgcolor.
|
||||
|
||||
:param color: given color
|
||||
:type color: any format
|
||||
:param bgcolor: given background color
|
||||
:type bgcolor: any format
|
||||
:return: filtered version of color and bgcolor
|
||||
"""
|
||||
color = select_color(color)
|
||||
bgcolor = select_color(bgcolor)
|
||||
if color == "COMPLEMENT" and bgcolor == "COMPLEMENT":
|
||||
warn(BOTH_COLOR_COMPLEMENT_WARNING, RuntimeWarning)
|
||||
return None, None
|
||||
if color == "COMPLEMENT":
|
||||
bgcolor = matplotlib.colors.to_hex(bgcolor)
|
||||
color = color_complement(bgcolor)
|
||||
if bgcolor == "COMPLEMENT":
|
||||
color = matplotlib.colors.to_hex(color)
|
||||
bgcolor = color_complement(color)
|
||||
return color, bgcolor
|
||||
|
||||
|
||||
def select_color(color):
|
||||
"""
|
||||
Select color and return it.
|
||||
|
||||
:param color: given color
|
||||
:type color: any format
|
||||
:return: color
|
||||
"""
|
||||
if isinstance(color, tuple):
|
||||
return color
|
||||
if isinstance(color, str):
|
||||
if color.upper() == "TRANSPARENT":
|
||||
return "TRANSPARENT"
|
||||
if color.upper() == "COMPLEMENT":
|
||||
return "COMPLEMENT"
|
||||
if color.upper() == "RANDOM":
|
||||
return random_hex_color_gen()
|
||||
if re.match(HEX_COLOR_PATTERN, color):
|
||||
return color
|
||||
return color.lower()
|
||||
distance_list = list(map(lambda x: distance_calc(color, x),
|
||||
VALID_COLORS))
|
||||
min_distance = min(distance_list)
|
||||
return VALID_COLORS[distance_list.index(min_distance)]
|
||||
return None
|
||||
most_similar_color = VALID_COLORS[distance_list.index(min_distance)]
|
||||
if min_distance != 0:
|
||||
warn(
|
||||
COLOR_NOT_FOUND_WARNING.format(
|
||||
color,
|
||||
most_similar_color),
|
||||
RuntimeWarning)
|
||||
return most_similar_color
|
||||
if is_valid_color(color):
|
||||
return color
|
||||
raise samilaPlotError(INVALID_COLOR_TYPE_ERROR)
|
||||
|
||||
|
||||
def set_background(bgcolor, fig, ax):
|
||||
"""
|
||||
Set background for figure and axis.
|
||||
|
||||
:param bgcolor: given background color
|
||||
:type bgcolor: any format
|
||||
:param fig: figure
|
||||
:type fig: matplotlib.figure.Figure
|
||||
:param ax: axis
|
||||
:type ax: matplotlib.axes._subplots.AxesSubplot
|
||||
:return: None
|
||||
"""
|
||||
if bgcolor == "TRANSPARENT":
|
||||
ax.patch.set_visible(False)
|
||||
fig.patch.set_visible(False)
|
||||
return
|
||||
fig.set_facecolor(bgcolor)
|
||||
ax.set_facecolor(bgcolor)
|
||||
return
|
||||
|
||||
|
||||
def filter_projection(projection):
|
||||
|
@ -196,7 +287,7 @@ def plot_params_filter(
|
|||
raise samilaPlotError(PLOT_DATA_ERROR.format(1))
|
||||
if g.data2 is None:
|
||||
raise samilaPlotError(PLOT_DATA_ERROR.format(2))
|
||||
color, bgcolor = map(filter_color, [color, bgcolor])
|
||||
color, bgcolor = filter_color(color, bgcolor)
|
||||
projection = filter_projection(projection)
|
||||
alpha = filter_float(alpha)
|
||||
linewidth = filter_float(linewidth)
|
||||
|
|
|
@ -8,6 +8,7 @@ import matplotlib.pyplot as plt
|
|||
from .functions import _GI_initializer, plot_params_filter, generate_params_filter, save_params_filter
|
||||
from .functions import float_range, save_data_file, save_fig_file, save_fig_buf, save_config_file
|
||||
from .functions import load_data, load_config, random_equation_gen, nft_storage_upload, fill_data
|
||||
from .functions import set_background
|
||||
from .params import *
|
||||
from warnings import warn
|
||||
|
||||
|
@ -126,9 +127,8 @@ class GenerativeImage:
|
|||
linewidth)
|
||||
fig = plt.figure()
|
||||
fig.set_size_inches(self.size[0], self.size[1])
|
||||
fig.set_facecolor(self.bgcolor)
|
||||
ax = fig.add_subplot(111, projection=self.projection)
|
||||
ax.set_facecolor(self.bgcolor)
|
||||
set_background(self.bgcolor, fig, ax)
|
||||
ax.scatter(
|
||||
self.data2,
|
||||
self.data1,
|
||||
|
|
|
@ -4,7 +4,7 @@ import math
|
|||
from enum import Enum
|
||||
from matplotlib import colors as mcolors
|
||||
|
||||
SAMILA_VERSION = "0.7" # pragma: no cover
|
||||
SAMILA_VERSION = "0.8" # pragma: no cover
|
||||
|
||||
OVERVIEW = '''
|
||||
Samila is a generative art generator written in Python, Samila let's you
|
||||
|
@ -39,8 +39,11 @@ CONFIG_TYPE_ERROR = "Provided config file is not supported. It should be either
|
|||
CONFIG_NO_STR_FUNCTION_ERROR = "Config file can't be saved. At least one of the function1_str or function2_str is None."
|
||||
PLOT_DATA_ERROR = "Plotting process can't be Done because data{0} is empty. Use generate method first."
|
||||
SAVE_NO_DATA_ERROR = "Data file can't be saved. At least one of the data1 or data2 is None."
|
||||
INVALID_COLOR_TYPE_ERROR = "Given color/bgcolor type is not supported."
|
||||
MATPLOTLIB_VERSION_WARNING = "Source matplotlib version({0}) is different from yours, plots may be different."
|
||||
CALCULATION_EXCEPTION_WARNING = "The given functions are undefined at some points. Your plot may not be complete."
|
||||
BOTH_COLOR_COMPLEMENT_WARNING = "It is not possible to set color and bgcolor to 'complement' at the same time! Both are automatically set to the previous or default selection."
|
||||
COLOR_NOT_FOUND_WARNING = "color '{0}' not found. Replacing it with '{1}'"
|
||||
|
||||
|
||||
class Projection(Enum):
|
||||
|
@ -68,6 +71,7 @@ RANDOM_COEF_LIST = [
|
|||
"random.lognormvariate(0,1)"]
|
||||
|
||||
ELEMENTS_LIST = [
|
||||
"{0}*math.atan({1})",
|
||||
"{0}*math.asinh({1})",
|
||||
"{0}*math.acosh(abs({1})+1)",
|
||||
"{0}*math.erf({1})",
|
||||
|
@ -88,6 +92,8 @@ ARGUMENTS_LIST = [
|
|||
"y-x",
|
||||
"x-y",
|
||||
"x+y",
|
||||
"x**3",
|
||||
"y**3",
|
||||
"x**2",
|
||||
"y**2",
|
||||
"(x**2)*y",
|
||||
|
@ -97,4 +103,4 @@ ARGUMENTS_LIST = [
|
|||
"x*(y**3)",
|
||||
"y*(x**3)"]
|
||||
|
||||
OPERATORS_LIST = ["+", "-", "*"]
|
||||
OPERATORS_LIST = ["+", "-", "*", "/"]
|
||||
|
|
4
setup.py
4
setup.py
|
@ -32,14 +32,14 @@ def read_description():
|
|||
setup(
|
||||
name='samila',
|
||||
packages=['samila'],
|
||||
version='0.7',
|
||||
version='0.8',
|
||||
description='Generative ART',
|
||||
long_description=read_description(),
|
||||
long_description_content_type='text/markdown',
|
||||
author='Samila Development Team',
|
||||
author_email='info@4r7.ir',
|
||||
url='https://github.com/sepandhaghighi/samila',
|
||||
download_url='https://github.com/sepandhaghighi/samila/tarball/v0.7',
|
||||
download_url='https://github.com/sepandhaghighi/samila/tarball/v0.8',
|
||||
keywords="generative-art art nft file nft-storage",
|
||||
project_urls={
|
||||
'Source': 'https://github.com/sepandhaghighi/samila',
|
||||
|
|
|
@ -34,10 +34,28 @@ samila.errors.samilaDataError: Data file can't be saved. At least one of the dat
|
|||
Traceback (most recent call last):
|
||||
...
|
||||
samila.errors.samilaPlotError: Plotting process can't be Done because data2 is empty. Use generate method first.
|
||||
>>> g.generate()
|
||||
>>> g.plot(color=(1, 2, 3, 4, 5))
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
samila.errors.samilaPlotError: Given color/bgcolor type is not supported.
|
||||
>>> g = GenerativeImage(lambda x,y: x, lambda x,y: y)
|
||||
>>> result = g.save_config()
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
samila.errors.samilaConfigError: Config file can't be saved. At least one of the function1_str or function2_str is None.
|
||||
>>> from samila.functions import *
|
||||
>>> select_color(2)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
samila.errors.samilaPlotError: Given color/bgcolor type is not supported.
|
||||
>>> filter_color(2,2)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
samila.errors.samilaPlotError: Given color/bgcolor type is not supported.
|
||||
>>> g.plot(color=2, bgcolor=2)
|
||||
Traceback (most recent call last):
|
||||
...
|
||||
samila.errors.samilaPlotError: Given color/bgcolor type is not supported.
|
||||
>>> os.remove('data.json')
|
||||
"""
|
||||
|
|
|
@ -2,6 +2,33 @@
|
|||
"""
|
||||
>>> import random
|
||||
>>> from samila.functions import *
|
||||
>>> is_valid_color("blue")
|
||||
True
|
||||
>>> is_valid_color((0,0,0))
|
||||
True
|
||||
>>> is_valid_color((0.1,0.1,0,1))
|
||||
True
|
||||
>>> is_valid_color([1,1,1,1])
|
||||
True
|
||||
>>> is_valid_color("#FFFAAF")
|
||||
True
|
||||
>>> color_complement("#FFFFFF")
|
||||
'#000000'
|
||||
>>> color_complement("#FFAFBF")
|
||||
'#005040'
|
||||
>>> color_complement("#000000")
|
||||
'#ffffff'
|
||||
>>> select_color("blue")
|
||||
'blue'
|
||||
>>> select_color("#FFFFFA")
|
||||
'#fffffa'
|
||||
>>> select_color((0.1,0.1,0.1))
|
||||
(0.1, 0.1, 0.1)
|
||||
>>> select_color(None)
|
||||
>>> select_color("complement")
|
||||
'COMPLEMENT'
|
||||
>>> select_color("transparent")
|
||||
'TRANSPARENT'
|
||||
>>> s = list(float_range(1,1.5,0.1))
|
||||
>>> s
|
||||
[1.0, 1.1, 1.2000000000000002, 1.3000000000000003, 1.4000000000000004]
|
||||
|
@ -13,16 +40,16 @@ False
|
|||
False
|
||||
>>> is_same_data(s,[])
|
||||
False
|
||||
>>> filter_color("yellow")
|
||||
'yellow'
|
||||
>>> filter_color((0.2,0.3,0.4))
|
||||
(0.2, 0.3, 0.4)
|
||||
>>> filter_color("#FFFFFF")
|
||||
'#FFFFFF'
|
||||
>>> filter_color("yellow", "blue")
|
||||
('yellow', 'blue')
|
||||
>>> filter_color((0.2,0.3,0.4), (0.2,0.3,0.4,1))
|
||||
((0.2, 0.3, 0.4), (0.2, 0.3, 0.4, 1))
|
||||
>>> filter_color("#FFFFFF", "#ffffe1")
|
||||
('#ffffff', '#ffffe1')
|
||||
>>> random.seed(2)
|
||||
>>> color1 = filter_color("random")
|
||||
>>> color1, bgcolor1 = filter_color("random", "random")
|
||||
>>> random.seed(3)
|
||||
>>> color2 = filter_color("RANDOM")
|
||||
>>> color2, bgcolor2 = filter_color("RANDOM", "RANDOM")
|
||||
>>> color1 == color2
|
||||
False
|
||||
>>> random.seed(2)
|
||||
|
@ -35,8 +62,6 @@ False
|
|||
7
|
||||
>>> len(color2)
|
||||
7
|
||||
>>> filter_color(2)
|
||||
>>> filter_color(4)
|
||||
>>> filter_size(2)
|
||||
>>> filter_size((2, 'test'))
|
||||
>>> filter_size((2, 3.5))
|
||||
|
|
|
@ -67,12 +67,27 @@ True
|
|||
>>> g.projection
|
||||
'polar'
|
||||
>>> g.color
|
||||
'#EEE245'
|
||||
'#eee245'
|
||||
>>> g.bgcolor
|
||||
'#000000'
|
||||
>>> g.plot(projection=Projection.POLAR, color=(.1, .2, .8))
|
||||
>>> g.color
|
||||
(0.1, 0.2, 0.8)
|
||||
>>> g.plot(projection=Projection.POLAR, color="#FFFFF1", bgcolor="complement")
|
||||
>>> g.color
|
||||
'#fffff1'
|
||||
>>> g.bgcolor
|
||||
'#00000e'
|
||||
>>> g.plot(projection=Projection.POLAR, color="complement", bgcolor="#AAAAAA")
|
||||
>>> g.color
|
||||
'#555555'
|
||||
>>> g.bgcolor
|
||||
'#aaaaaa'
|
||||
>>> g.plot(projection=Projection.POLAR, color="complement", bgcolor="complement")
|
||||
>>> g.color
|
||||
'#555555'
|
||||
>>> g.bgcolor
|
||||
'#aaaaaa'
|
||||
>>> g.plot(bgcolor=(.1, .2, .8), spot_size=0.1)
|
||||
>>> g.plot(size=(20, 20))
|
||||
>>> g.size
|
||||
|
@ -119,7 +134,7 @@ False
|
|||
False
|
||||
>>> socket.socket = guard
|
||||
>>> g.generate()
|
||||
>>> g.plot(color=2,bgcolor=2)
|
||||
>>> g.plot()
|
||||
>>> result = g.nft_storage("")
|
||||
>>> result["status"]
|
||||
False
|
||||
|
@ -146,6 +161,9 @@ True
|
|||
True
|
||||
>>> g = GenerativeImage()
|
||||
>>> g.generate()
|
||||
>>> g.plot(color="white", bgcolor="transparent")
|
||||
>>> g.bgcolor == "TRANSPARENT"
|
||||
True
|
||||
>>> g.plot(color="white", bgcolor="black", spot_size=0.1)
|
||||
>>> result = g.save_config()
|
||||
>>> result["status"]
|
||||
|
|
|
@ -23,6 +23,10 @@ True
|
|||
>>> g = GenerativeImage(lambda x, y: 1 / x, lambda x, y: 1 / (y - 1))
|
||||
>>> with warns(RuntimeWarning, match=r"The given functions are undefined at some points. Your plot may not be complete."):
|
||||
... g.generate(start=0, stop=2, step=0.1)
|
||||
>>> with warns(RuntimeWarning, match=r"It is not possible to set color and bgcolor to 'complement' at the same time! Both are automatically set to the previous or default selection."):
|
||||
... g.plot(color='complement', bgcolor='complement')
|
||||
>>> with warns(RuntimeWarning, match=r"color 'rad' not found. Replacing it with 'red'"):
|
||||
... g.plot(color='rad')
|
||||
>>> os.remove('data.json')
|
||||
>>> os.remove('config.json')
|
||||
"""
|
||||
|
|
Ładowanie…
Reference in New Issue