# -*- coding: utf-8 -*-
import copy
import string
import random
import datetime
import re
import os.path
from django.conf import settings
from django.db import models
from django.core.exceptions import ObjectDoesNotExist, ValidationError
from m3_ext.ui.fields.base import BaseExtField
from m3_ext.ui.fields.simple import ExtCheckBox,\
ExtStringField, ExtNumberField,\
ExtDateField, ExtDateTimeField,\
ExtTextArea, ExtHTMLEditor
from m3_ext.ui.fields.complex import ExtFileUploadField,\
ExtImageUploadField
from objectpack.ui import WindowTab, TabbedEditWindow, BaseEditWindow
from objectpack.models import VirtualModel, ModelProxy
[документация]def get_obj_by_id(obj_model, obj_id):
"""
Возвращает объект заданной модели по id или None
:param obj_model
:type obj_model: django.db.model
:param id:
:type id: int
:return: object
"""
try:
return obj_model.objects.get(id=int(obj_id))
except (AttributeError, ObjectDoesNotExist):
return None
[документация]def parse_to_filter(orig_filter):
"""
Переворачиваем дату от пользователя
:param orig_filter: подготовительный словарь для будущей записи в БД,
ключи - имена полей модели model,
значения - значения для полей
:type orig_filter: dict
:return: подготовительный словарь для будущей записи в БД,
ключи - имена полей модели model,
значения - значения для полей
:rtype: dict
"""
temp_filter = dict(zip(orig_filter.keys(), orig_filter.values()))
for k in temp_filter.keys():
re_match = re.match(
r'^(\d\d)\.(\d\d)\.(\d\d\d\d)$',
'%s' % temp_filter[k]
)
if re_match:
temp_filter[k] = '-'.join(re_match.groups()[::-1])
return temp_filter
[документация]def generate_good_uniq(temp_filter, model, condition):
"""
:param temp_filter: подготовительный словарь для будущей записи в БД,
ключи - имена полей модели model,
значения - значения для полей
:type temp_filter: dict
:param model: модель, для которой проверяем запись на уникальность
:type model: django.db.model
:param condition: условия, по которым будем генеририровать даныне,
в случае нарушения уникальности
:type condition: dict
:return: подготовительный словарь для будущей записи в БД,
ключи - имена полей модели model,
значения - значения для полей
:rtype: dict
"""
last_fk = None
last_key = None
for key in temp_filter.keys():
field_name = key[0:-3] if key.endswith('_id') else key
temp_field = get_meta_for_model(model).get_field_by_name(field_name)[0]
if (isinstance(temp_field, models.ForeignKey) and
temp_field.rel.to not in condition.keys()):
last_fk = temp_field
last_key = key
id_list = temp_field.rel.to.objects.exclude(
id=temp_filter[key]
).values_list('id', flat=True)
for temp_id in id_list:
if not model.objects.filter(**parse_to_filter(
temp_filter)).exists():
temp_filter[key] = temp_id
return temp_filter
if last_fk and last_key:
temp_filter[last_key] = GM(last_fk.rel.to, condition).get().id
return temp_filter
[документация]def gen_uniq(params, model, condition):
"""
:param params: подготовительный словарь для будущей записи в БД,
ключи - имена полей модели model,
значения - значения для полей
:type params: dict
:param model: модель, для которой проверяем запись на уникальность
:type model: django.db.model
:param condition: условия, по которым будем генеририровать даныне,
в случае нарушения уникальности
:type condition: dict
:return: подготовительный словарь для будущей записи в БД,
ключи - имена полей модели model,
значения - значения для полей
:rtype: dict
"""
uniq_list = list(get_meta_for_model(model).unique_together)
for i in get_meta_for_model(model).fields:
if isinstance(i, models.OneToOneField) or\
(isinstance(i, models.ForeignKey) and i.unique):
uniq_list.append((i.name, ))
if not uniq_list:
return params
for uniq_tuple in uniq_list:
temp_filter = {}
for key in params.keys():
for uniq in uniq_tuple:
if key.endswith('_id'):
uniq = '_'.join([uniq, 'id'])
if uniq == key and temp_filter.get(key, -1) == -1:
temp_filter[key] = params[key]
if temp_filter:
if model.objects.filter(**parse_to_filter(temp_filter)).exists():
uniq_filter = generate_good_uniq(temp_filter, model, condition)
for i in uniq_filter.keys():
params[i] = uniq_filter[i]
return params
[документация]class PackValueForContext(object):
"""
Класс для поиска и генерации значений
(либо берем из БД, либо создаем свой)
по записям declare_context
"""
def __init__(self, key, val, condition, gm=False):
"""
Инициализация созданного объекта
:param key: имя параметра, для которого необходимо
сгенерировать/найти данные
:param val: type of key
:param condition: условия, по которым ищем или генерируем данные
:type condition: dict
:param gm: gm=True, генерируем, в противном случае - нет
:type gm: bool
"""
self.key = key # название св-ва, как правило это id_param_name
self.val = val # описание св-ва {'type': int}
self.condition = copy.copy(condition) # словарь условий, {Model: id}
self.gm = gm # Если не нашли в БД, генерируем, по умолчанию нет
[документация] def get(self):
"""
Основной метод для получения необхдимого значения контекста
"""
from objectpack.tools import int_or_zero, int_list, str_to_date
if self.val['type'] == str or self.val['type'] == 'str':
if 'date' in self.key:
return datetime.datetime.now().strftime("%d.%m.%Y")
if (self.val['type'] == 'date' or self.val['type'] == str_to_date or
self.val['type'] == 'str_to_date'):
val = datetime.datetime.now()
delta = datetime.timedelta(days=5)
if 'begin' in self.key or 'from' in self.key:
val -= delta
if 'end' in self.key or 'to 'in self.key:
val += delta
return val.strftime("%d.%m.%Y")
if self.val['type'] == 'json':
return '[]'
if self.val['type'] == 'time':
return datetime.time(1, 1)
if self.key == 'ids':
return [0]
# если ключ целое число, значит возможный ID
if self.key != 'id' and (
self.val['type'] == int or
self.val['type'] == 'int' or
self.val['type'] == int_or_zero or
self.val['type'] == 'int_or_zero' or
self.val['type'] == int_list or
self.val['type'] == 'int_list' or
self.val['type'] == 'int_or_none'
):
return self.find_value_from_db()
[документация] def find_value_from_db(self):
"""
Среди зарегистррованных паков ищем тот,
у которого id_param_name свопадает с искомым ключом.
В найденном паке берем модель и достаем объект из нее (или генерим)
"""
from .integration_test import PATCHED_BASE_INTEGRATION_TEST
pack_list = PATCHED_BASE_INTEGRATION_TEST.get_list_pack_from_observer()
for pack in pack_list:
if hasattr(pack, 'model') and hasattr(pack, 'id_param_name'):
if pack.id_param_name == self.key and (
self.key.count('.') > 2 or
pack._is_primary_for_model
):
modelcls = pack.model
if VirtualModel in modelcls.__mro__:
modelcls = modelcls.model
filter_cond, exclude_cond = \
PackValueForContext._build_condition(
modelcls,
self.condition
)
try:
res = modelcls.objects.exclude(
**exclude_cond
).filter(
**filter_cond
)
res = res[0].id
except:
res = None
if not res and self.gm:
res = GM(modelcls, self.condition).get().id
return res
@staticmethod
def _build_condition(modelcls, condition):
"""
Для моедли modelcls строим рекурсивный обход для поиска связей,
указанных в conditions
:param modelcls: модель, для которой получаем фильтры
:type modelcls: django.db.model
:param condition: условия, по которым строим фильтры
:type condition: dict
:return: filter, exclude - фильтры поиска/критерии создания записей БД
:rtype: tuple of dict
"""
filter_cond = {}
exclude_cond = {}
find_filter_obj = []
for obj_tp, obj_id in condition.iteritems():
if obj_tp == modelcls:
if type(obj_id) == int:
obj_id = [obj_id]
filter_cond['pk__in'] = obj_id
find_filter_obj.append(obj_tp)
# ищем в текущих полях выбранное ОУ
for i in get_meta_for_model(modelcls).fields:
if i.name == 'name' or i.name == 'fullname':
# в кортежах БД могут быть служебные для теста парметры,
# например юзеры dev_test
exclude_cond['%s__icontains' % i.name] = 'dev_test'
if isinstance(i, models.ForeignKey):
for obj_tp, obj_id in condition.iteritems():
if obj_tp not in find_filter_obj:
if type(obj_id) == int:
obj_id = [obj_id]
if i.rel.to is obj_tp:
filter_cond[i.name + '__id__in'] = obj_id
find_filter_obj.append(obj_tp)
# не нашли, уходим в рекурсию
for obj_tp, obj_id in condition.iteritems():
if type(obj_id) == int:
obj_id = [obj_id]
if obj_tp not in find_filter_obj:
name = PackValueForContext._find_recurse_field_in_model(
modelcls,
obj_tp,
[]
)
if name:
filter_cond[name + '__id__in'] = obj_id
if modelcls == obj_tp:
filter_cond['id__in'] = obj_id
return filter_cond, exclude_cond
@staticmethod
def _find_recurse_field_in_model(model, m, exclude_model):
"""
Бегаем по FK и ищем где спрятан unit
model - где ищем
m - что ищем
exclude_model - список моделей, которые уже просмотрены
"""
name = None
for i in get_meta_for_model(model).fields:
if i not in exclude_model and\
isinstance(i, models.ForeignKey) and\
not i.blank:
exclude_model.append(i)
if i.rel.to is m:
name = i.name
break
name = PackValueForContext._find_recurse_field_in_model(
i.rel.to,
m,
exclude_model
)
if name:
name = '%s__%s' % (i.name, name)
break
return name
[документация]class ModelValueForContext(object):
"""
Класс для поиска и генерации значений по классу
"""
def __init__(self, model, condition, gm=False):
"""
Инициализация созданного объекта
:param model: модель, для которой получаем или генерируем данные
:type model: django.db.model
:param condition: условия, по которым ищем или генерируем данные
:type condition: dict
:param gm: gm=True, генерируем, в противном случае - нет
:type gm: bool
"""
self.model = model # модель, для которой ищем/генерируем значения
self.condition = copy.copy(condition) # словарь условий, {Model: id}
# Если не нашли в БД, генерируем или нет, по умолчанию нет
self.gm = gm
[документация] def get(self):
"""
Основной метод получения/генерации id объекта модели из БД
:return: id объекта модели
:rtype: int
"""
filter_cond, exclude_cond = PackValueForContext._build_condition(
self.model,
self.condition
)
modelcls = self.model
if VirtualModel in modelcls.__mro__ or\
ModelProxy in modelcls.__mro__:
modelcls = modelcls.model
try:
res = modelcls.objects.exclude(
**exclude_cond
).filter(
**filter_cond
)[0].id
except:
res = None
if not res and self.gm:
res = GM(modelcls, self.condition).get().id
return res
[документация]class GM(object):
"""
Класс для генерации значений по модели и условию
"""
def __init__(self, model, condition):
"""
Инициализация созданного объекта
:param model: модель, для которой получаем или генерируем данные
:type model: django.db.model
:param condition: условия, по которым ищем или генерируем данные
:type condition: dict
"""
self.modelcls = model
if VirtualModel in self.modelcls.__mro__ or\
ModelProxy in self.modelcls.__mro__:
self.modelcls = self.modelcls.model
self.condition = condition
[документация] def get(self):
"""
Основной метод генерации id объекта модели из БД
:return: id объекта модели
:rtype: int
"""
obj = self.modelcls()
date_condition = None
time_condition = None
datetime_condition = None
uniq_context = {}
for i in get_meta_for_model(self.modelcls).fields:
# наследуемые классы, создается все скопом из текущего
if i.name.endswith('_ptr'):
continue
# это можно не заполнять, но пусть поле name будет всегда
if i.blank and i.null and i.name != 'name':
if isinstance(i, models.ForeignKey):
if i.rel.to not in self.condition:
continue
else:
continue
if hasattr(i, 'choices') and i.choices:
setattr(
obj,
i.name,
i.choices[0][0] if i.choices[0][0] else i.choices[1][0]
)
elif isinstance(i, models.ForeignKey):
from django.contrib.auth.models import User
exclude_find_model = [User, ]
res = None
if i.rel.to not in exclude_find_model:
filter_cond, exclude_cond = \
PackValueForContext._build_condition(
i.rel.to,
self.condition
)
try:
res = i.rel.to.objects.exclude(
**exclude_cond
).filter(
**filter_cond
)[0]
except IndexError:
if i.rel.to in self.condition and\
self.condition[i.rel.to]:
try:
if type(self.condition[i.rel.to]) == int:
obj_id = self.condition[i.rel.to]
else:
obj_id = self.condition[i.rel.to][0]
res = get_obj_by_id(i.rel.to, obj_id)
except (AttributeError, IndexError):
pass
key = '%s_id' % i.name
uniq_context[key] = None
if res:
uniq_context[key] = res.id
# проверяем, что найденное поле соответсвует
# всем критериям на уникальность
uniq_context = gen_uniq(
uniq_context,
self.modelcls,
self.condition
)
if not uniq_context[key]:
uniq_context[key] = GM(i.rel.to, self.condition).get().id
setattr(obj, key, uniq_context[key])
elif isinstance(i, models.CharField) or\
isinstance(i, models.TextField):
if i.name in ['last_name', 'first_name', 'middle_name']:
ml = getattr(i, 'max_length', 55)
val = GM.str_generator(ml if ml < 55 else 55)
elif i.name.startswith('time') or i.name.endswith('time'):
val = '%s:%s' % (
random.randint(0, 23),
random.randint(0, 59)
)
else:
val = GM.str_generator(
getattr(i, 'max_length', 255) or 255
)
setattr(obj, i.name, val)
elif isinstance(i, models.TimeField):
val = datetime.datetime.now()
delta_hour = datetime.timedelta(seconds=60*60)
if 'begin' in i.name or 'start' in i.name:
if time_condition is not None:
val = time_condition - delta_hour
else:
time_condition = val
if 'end' in i.name or 'finish' in i.name:
if time_condition is not None:
val = time_condition + delta_hour
else:
time_condition = val
val = val.time().strftime('%H:%M')
setattr(obj, i.name, val)
elif isinstance(i, models.DateField):
val = datetime.datetime.now()
if 'begin' in i.name or 'start' in i.name:
if date_condition is not None:
val = date_condition - datetime.timedelta(days=5)
else:
date_condition = val
if 'end' in i.name or 'finish' in i.name:
if date_condition is not None:
val = date_condition + datetime.timedelta(days=5)
else:
date_condition = val
val = val.date()
setattr(obj, i.name, val)
elif isinstance(i, models.DateTimeField):
val = datetime.datetime.now()
day_delta = datetime.timedelta(days=5, seconds=60*60)
if 'begin' in i.name or 'start' in i.name:
if datetime_condition is not None:
val = datetime_condition - day_delta
else:
datetime_condition = val
if 'end' in i.name or 'finish' in i.name:
if datetime_condition is not None:
val = datetime_condition + day_delta
else:
datetime_condition = val
setattr(obj, i.name, val)
elif isinstance(i, models.BooleanField):
setattr(obj, i.name, random.randint(0, 1))
# SmallIntegerField instance IntegerField
elif isinstance(i, models.SmallIntegerField):
setattr(obj, i.name, random.randint(0, 32767))
elif isinstance(i, models.IntegerField) or\
isinstance(i, models.DecimalField) or\
isinstance(i, models.FloatField):
max_length = getattr(i, 'max_length', 4) or 4
if max_length > 9:
max_length = 9
setattr(
obj,
i.name,
random.randint(0, 10**max_length)
)
elif isinstance(i, models.FileField):
basedir = os.path.join(
settings.MEDIA_ROOT,
'test_files',
)
if not os.path.exists(basedir):
os.makedirs(basedir)
path = os.path.join(
basedir,
GM.str_generator(
(getattr(i, 'max_length', 255) or 255) -
len(basedir) - 1
)
)
created_file = open(path, "w+")
# wipe the existing content
created_file.truncate()
created_file.close()
setattr(obj, i.name, path)
uhahaha = True
while uhahaha:
try:
obj.validate_unique()
uhahaha = False
except ValidationError:
uniq_list = list(
get_meta_for_model(
self.modelcls
).unique_together
)
for uniq_tuple in uniq_list:
if not any([i.endswith('id') for i in uniq_tuple]):
for uniq in uniq_tuple:
param = getattr(obj, uniq)
if param:
if isinstance(param, datetime.date):
param += datetime.timedelta(
days=5, seconds=60*60)
elif isinstance(param, basestring):
param = GM.str_generator(len(param))
else:
param += 1
setattr(obj, uniq, param)
# raise BitField
except TypeError:
uhahaha = False
# не модель, а хрень какая-то
except AttributeError:
uhahaha = False
try:
obj.save()
except Exception as ex:
ex.args = (ex.args if ex.args else tuple()) + (self.modelcls,)
raise
return obj
@staticmethod
[документация] def str_generator(size=6, chars=string.ascii_lowercase):
"""
Возвращает произвольную строку длины size
:param size: условия, размер будущей строки
:type size: int
:param chars: символы, которые будут учавствовать в генерации
:type chars: str
:return: сгенерированная произвольная строка
:rtype: str
"""
return ''.join(random.choice(chars) for x in range(size))
[документация]class GMWindowField(object):
"""
Класс для поиска и генерации значений
(либо берем из БД, либо создаем свой)
по записям declare_context
"""
def __init__(self, pack, win, context, condition):
"""
Инициализация созданного объекта
:param objectpack.ObjectpPack pack: инстанс пака
:param objectpack.BaseEditWindow win: инстанс окна
:param objectpack.dict context: контекст запроса (из setUp)
:param objectpack.dict condition: условия генерациия для моделей
"""
self.pack = pack
self.win = win
self.context = context
self.condition = condition
@staticmethod
[документация] def get_default_data(some_field, model):
if isinstance(some_field, ExtStringField):
temp_field = None
mask_re = some_field.mask_re
try:
temp_field = get_meta_for_model(
model
).get_field_by_name(some_field.name)
except:
pass
if temp_field is not None:
if isinstance(temp_field[0], models.TimeField):
return u'12:12'
if hasattr(some_field, 'regex') and some_field.regex and\
re.match(some_field.regex, u'12:12'):
return u'12:12'
gen_param = {}
if mask_re:
all_chars = u''.join([
u'абвгдеёжзийклмнопрстуфхцчшщьъэюяa',
string.ascii_lowercase,
'0123456789'])
nchars = re.findall(mask_re, all_chars)
gen_param.update({'chars': nchars})
if hasattr(some_field, 'max_length') and some_field.max_length:
gen_param.update({'size': some_field.max_length})
return GM.str_generator(**gen_param)
elif isinstance(some_field, (ExtHTMLEditor, ExtTextArea)):
return GM.str_generator()
elif isinstance(some_field, ExtNumberField):
return 111
elif isinstance(some_field, ExtDateTimeField):
return format(datetime.datetime.now(), '%d.%m.%Y %H:%M:%S')
elif isinstance(some_field, ExtDateField):
return format(datetime.datetime.today(), '%d.%m.%Y')
elif isinstance(some_field, ExtCheckBox):
return False
elif isinstance(some_field, ExtFileUploadField):
basedir = os.path.join(
settings.MEDIA_ROOT,
'test_files',
)
if not os.path.exists(basedir):
os.makedirs(basedir)
file_name = '%s.jpeg' % (
GM.str_generator(50),
)
file_path = os.path.join(
basedir,
file_name,
)
if isinstance(some_field, ExtImageUploadField):
import Image
pystr = ""
for i in xrange(30*30*4):
pystr += str(i)
im_out = Image.fromstring("RGBA", (30, 30), pystr)
im_out.save(file_path, format='jpeg')
else:
created_file = open(file_path, "w+")
# wipe the existing content
created_file.truncate()
created_file.close()
return {
'file_%s' % some_field.name: (open(file_path, 'rb')),
some_field.name: file_name
}
return None
[документация] def get(self):
all_fields = []
temp_tabs = None
instance_aw = self.win
context_new = self.context
if isinstance(instance_aw, TabbedEditWindow):
temp_tabs = instance_aw.tabs
elif isinstance(instance_aw, BaseEditWindow):
temp_tabs = []
for i in dir(instance_aw):
if isinstance(getattr(instance_aw, i), WindowTab):
temp_tabs.append(getattr(instance_aw, i))
if temp_tabs:
for tab in temp_tabs:
temp_some_fields = tab.__dict__.values()
for some_cur_field in temp_some_fields:
if isinstance(some_cur_field, list):
all_fields.extend(some_cur_field)
else:
all_fields.append(some_cur_field)
all_fields.extend(instance_aw.__dict__.values())
# Табы. Если есть, берем все поля и генерим по ним данные
for ex_local_filed in all_fields:
if isinstance(ex_local_filed, BaseExtField):
if ex_local_filed.name not in context_new:
store = getattr(ex_local_filed, 'store', None)
if ex_local_filed.name and ex_local_filed.name not in context_new:
if store:
if getattr(store, 'data', None):
try:
context_new[ex_local_filed.name] =\
store.data[0][0]
except IndexError:
pass
else:
y_model = getattr(
ex_local_filed.pack,
'model',
None
) or\
getattr(
ex_local_filed.pack,
'tree_model',
None
)
if y_model:
context_new[ex_local_filed.name] =\
ModelValueForContext(
model=y_model,
condition=self.condition,
gm=True
).get()
else:
for_new_context = GMWindowField.get_default_data(
ex_local_filed, self.pack.model)
if for_new_context:
if isinstance(for_new_context, dict):
context_new.update(for_new_context)
else:
if ex_local_filed.name not in context_new:
context_new[ex_local_filed.name] =\
for_new_context
else:
try:
from kladr.addrfield import ExtAddrComponent
if isinstance(ex_local_filed, ExtAddrComponent):
for i in [
'place', 'street',
'house', 'zipcode',
'addr', 'corps',
'flat'
]:
if not getattr(
ex_local_filed,
'%s_allow_blank' % i,
True
) or i in ['zipcode', 'addr']:
key = getattr(
ex_local_filed,
'%s_field_name' % i,
None
)
if key not in context_new:
context_new[key] = \
random.randint(0, 10**5)
except ImportError:
pass
return context_new