Browse Source

migração da app 'servidores'

stable/2.0
Breno Teixeira 11 years ago
parent
commit
e39df2b328
  1. 0
      sigi/apps/contatos/fixtures/initial_data.txt
  2. 0
      sigi/apps/servidores/__init__.py
  3. 87
      sigi/apps/servidores/admin.py
  4. 60
      sigi/apps/servidores/forms.py
  5. 0
      sigi/apps/servidores/management/__init__.py
  6. 0
      sigi/apps/servidores/management/commands/__init__.py
  7. 236
      sigi/apps/servidores/management/commands/migra.py
  8. 100
      sigi/apps/servidores/management/commands/sync_ldap.py
  9. 223
      sigi/apps/servidores/models.py
  10. 33
      sigi/apps/servidores/views.py
  11. 1
      sigi/settings.py

0
sigi/apps/contatos/fixtures/initial_data.json → sigi/apps/contatos/fixtures/initial_data.txt

0
sigi/apps/servidores/__init__.py

87
sigi/apps/servidores/admin.py

@ -0,0 +1,87 @@
# -*- coding: utf-8 -*-
from django.contrib import admin
from django.contrib.contenttypes import generic
from sigi.apps.utils.admin_widgets import AdminImageWidget
from sigi.apps.servidores.models import Servidor, Funcao, Licenca, Ferias
from sigi.apps.contatos.models import Endereco, Telefone
from sigi.apps.servidores.forms import FeriasForm, LicencaForm, FuncaoForm
class FuncaoAdmin(admin.ModelAdmin):
form = FuncaoForm
list_display = ('servidor', 'funcao', 'cargo','inicio_funcao', 'fim_funcao')
list_filter = ('inicio_funcao', 'fim_funcao')
search_fields = ('funcao', 'cargo', 'descricao',
'servidor__nome_completo', 'servidor__obs', 'servidor__apontamentos',
'servidor__user__email', 'servidor__user__first_name',
'servidor__user__last_name', 'servidor__user__username')
class FeriasAdmin(admin.ModelAdmin):
form = FeriasForm
list_display = ('servidor', 'inicio_ferias', 'fim_ferias')
list_filter = ('inicio_ferias', 'fim_ferias')
search_fields = ('obs',
'servidor__nome_completo', 'servidor__email_pessoal',
'servidor__user__email', 'servidor__user__username')
class LicencaAdmin(admin.ModelAdmin):
form = LicencaForm
list_display = ('servidor', 'inicio_licenca', 'fim_licenca')
list_filter = ('servidor', 'inicio_licenca', 'fim_licenca')
search_fields = ('obs',
'servidor__nome_completo', 'servidor__email_pessoal',
'servidor__user__email', 'servidor__user__username')
class EnderecoInline(generic.GenericStackedInline):
model = Endereco
extra = 0
raw_id_fields = ('municipio',)
class TelefonesInline(generic.GenericTabularInline):
extra = 1
model = Telefone
class ServidorAdmin(admin.ModelAdmin):
def is_active(self, servidor):
return servidor.user.is_active
is_active.admin_order_field = 'user__is_active'
is_active.boolean = True
is_active.short_description = 'ativo'
list_display = ('nome_completo', 'is_active', 'foto', 'servico', )
list_filter = ('user', 'sexo', 'servico',)
search_fields = ('nome_completo', 'obs', 'apontamentos',
'user__email', 'user__first_name',
'user__last_name', 'user__username')
raw_id_fields = ('user',)
inlines= (TelefonesInline,EnderecoInline)
fieldsets = (
(u'Autenticação', {
'fields': ('user',),
}),
('Cadastro', {
'fields': ('nome_completo', 'foto', 'email_pessoal', 'rg', 'cpf', 'sexo', 'data_nascimento', 'matricula', 'ramal', 'data_nomeacao', 'ato_numero', 'ato_exoneracao')
}),
('Lotação', {
'fields': ('servico', 'turno', 'de_fora'),
}),
(u'Observações', {
'fields': ('apontamentos', 'obs'),
}),
)
def lookup_allowed(self, lookup, value):
return super(ServidorAdmin, self).lookup_allowed(lookup, value) or \
lookup in ['user__is_active__exact']
def formfield_for_dbfield(self, db_field, **kwargs):
if db_field.name == 'foto':
request = kwargs.pop("request", None)
kwargs['widget'] = AdminImageWidget
return db_field.formfield(**kwargs)
return super(ServidorAdmin,self).formfield_for_dbfield(db_field, **kwargs)
admin.site.register(Servidor, ServidorAdmin)
admin.site.register(Funcao, FuncaoAdmin)
admin.site.register(Ferias, FeriasAdmin)
admin.site.register(Licenca, LicencaAdmin)

60
sigi/apps/servidores/forms.py

@ -0,0 +1,60 @@
# -*- coding: utf8 -*-
from django import forms
from sigi.apps.utils.validators import valida_data, valida_periodo_data
from sigi.apps.servidores.models import Ferias, Licenca, Funcao, Servidor
class FeriasForm(forms.ModelForm):
class Meta:
model = Ferias
def clean(self):
data = self.cleaned_data
if valida_data(data.get('inicio_ferias'), data.get('fim_ferias')):
raise forms.ValidationError(u"""A data de início deve ser menor
que a data final. Verifique novamente""")
return data
class LicencaForm(forms.ModelForm):
class Meta:
model = Licenca
def clean(self):
data = self.cleaned_data
if valida_data(data.get('inicio_licenca'), data.get('fim_licenca')):
raise forms.ValidationError(u"""A data de início deve ser menor
que a data final. Verifique novamente""")
return data
class FuncaoForm(forms.ModelForm):
class Meta:
model = Funcao
def clean(self):
data = self.cleaned_data
if valida_data(data.get('inicio_funcao'), data.get('fim_funcao')):
raise forms.ValidationError(u"""A data de início deve ser menor
que a data final. Verifique
novamente""")
# Verifica na função anterior, se o seu período é igual
# ou está entre o período da função atual.
servidor = Servidor.objects.get(nome_completo=data.get('servidor'))
if len(servidor.funcao_set.all()):
if len(servidor.funcao_set.all()) > 1:
funcao_anterior = servidor.funcao_set.all()[1]
elif len(servidor.funcao_set.all()) == 1:
funcao_anterior = servidor.funcao_set.all()[0]
if valida_periodo_data(funcao_anterior.inicio_funcao,
funcao_anterior.fim_funcao, data.get('inicio_funcao'),
data.get('fim_funcao')):
raise forms.ValidationError(u"""Você não pode exercer
uma função no mesmo período que a anterior, como também,
não pode ser entre o período da mesma.""")
return data

0
sigi/apps/servidores/management/__init__.py

0
sigi/apps/servidores/management/commands/__init__.py

236
sigi/apps/servidores/management/commands/migra.py

@ -0,0 +1,236 @@
# coding= utf-8
import sys
import csv
import re
from datetime import datetime
from django.core.management.base import BaseCommand, CommandError
from django.contrib.auth.models import User
from sigi.apps.servidores.models import Servidor, Servico, Subsecretaria, Funcao, Ferias, Licenca
from sigi.apps.contatos.models import Municipio
#Funcao.objects.all().delete()
#Ferias.objects.all().delete()
#Licenca.objects.all().delete()
#for u in User.objects.filter(date_joined__gte=datetime(2011, 12, 9, 10, 58, 49, 83734)).all():
# u.servidor_set.all().delete()
# u.delete()
class MigrationError(Exception):
pass
class Command(BaseCommand):
help = 'Migra usuários do antigo Sistema de RH'
def to_date(self, data):
return datetime.strptime(data, "%Y-%m-%d 00:00:00")
def handle(self, *args, **options):
reader = csv.reader(open("/tmp/pessoal.csv"), delimiter=',', quotechar="\"")
BRASILIA = Municipio.objects.get(codigo_ibge=5300108)
# Read the column names from the first line of the file
fields = reader.next()
for row in reader:
# cria um dict com a primeira e a linha atual
pessoa = zip(fields, row)
p = {}
for (name, value) in pessoa:
p[name] = value.strip()
user = None
if not p['email']:
username = ''
email = ''
elif not ('@interlegis' in p['email']):
username = p['email'].split('@')[0].strip().lower()
email = ''
else:
username = p['email'].split('@')[0].strip().lower()
email = username + '@interlegis.gov.br'
# buscar usuário e servidor da linha atual
try:
# procuro o usuario por email do interlegis
if email:
try: user = User.objects.get(email=email)
except User.DoesNotExist:
email = username + '@interlegis.leg.br'
try: user = User.objects.get(email=email)
except User.DoesNotExist: pass
if not user and username:
try: user = User.objects.get(username=username)
except User.DoesNotExist:
try: user = User.objects.get(username=username + "__")
except User.DoesNotExist: pass
if not user:
if not username:
raise MigrationError
if not email:
# cria um username a partir do email sem
# colidir com os usuarios ldap
username = username + '__'
names = p['nome_completo'].split(' ')
first_name = names[0]
last_name = " ".join(names[1:])
user = User.objects.create(
username = username,
email = email,
first_name = first_name,
last_name = last_name[:30],
is_active= False
)
servidor = user.servidor
except Servidor.DoesNotExist:
servidor = Servidor.objects.create(
user=user,
nome_completo= "%s %s" % (user.first_name, user.last_name)
)
except MigrationError, e:
print ", ".join(row)
continue
# mapeando dados simples
servidor.nome_completo = p['nome_completo']
servidor.cpf = p['cpf']
servidor.rg = p['identidade']
servidor.apelido = p['username']
servidor.matricula = p['matricula']
servidor.ato_exoneracao = p['ato_exoneracao']
servidor.ato_numero = p['ato_numero']
servidor.ramal = p['ramal']
if p['email'] and not '@interlegis' in p['email']:
servidor.email_pessoal= p['email']
if p['inativo']=="-1":
servidor.user.is_active = False
else:
servidor.user.is_active = True
servidor.user.save()
if p['de_fora']=="-1":
servidor.de_fora = True
else:
servidor.de_fora = False
if p['sexo'].upper() == 'M':
servidor.sexo = 'M'
elif p['sexo'].upper() == 'F':
servidor.sexo = 'F'
if p['turno']=="1":
servidor.turno = 'M'
elif p['turno']=="2":
servidor.turno = 'T'
elif p['turno']=="3":
servidor.turno = 'N'
if p['aniversario']:
servidor.data_nascimento = self.to_date(p['aniversario'])
if p['data_nomeacao']:
servidor.data_nomeacao = self.to_date(p['data_nomeacao'])
if p['secretaria_sigla']:
if ' - ' in p['secretaria_nome']:
secretaria_nome = p['secretaria_nome'].split(' - ')[1]
else:
secretaria_nome = p['secretaria_nome']
secretaria = Subsecretaria.objects.get_or_create(
sigla = p['secretaria_sigla'],
nome = secretaria_nome
)[0]
if ' - ' in p['servico_nome']:
servico_nome = p['servico_nome'].split(' - ')[1]
else:
servico_nome = p['servico_nome']
servico = Servico.objects.get_or_create(
sigla = p['servico_sigla'],
nome = servico_nome
)[0]
servico.subsecretaria = secretaria
servico.save()
servidor.servico = servico
if p['telefone']:
try:
t = servidor.telefones.get(numero=p['telefone'])
except:
t = servidor.telefones.create(numero=p['telefone'])
t.tipo = 'F'
t.save()
if p['celular']:
try:
t = servidor.telefones.get(numero=p['celular'])
except:
t = servidor.telefones.create(numero=p['celular'])
t.tipo = 'M'
t.save()
if p['endereco']:
try:
e = servidor.endereco.get(logradouro=p['endereco'])
except:
e = servidor.endereco.create(logradouro=p['endereco'])
e.municipio = BRASILIA
e.bairro = p['cidade'] # bizarro mas é isso mesmo
e.cep = re.sub("\D", "", p['cep'])
e.save()
servidor.apontamentos = p['apontamentos']
servidor.obs = p['obs']
if p['cargo'] or p['funcao']:
funcao = servidor.funcao_set.get_or_create(
funcao = p['funcao'],
cargo = p['cargo'],
)[0]
if p['data_bap_entrada']:
funcao.data_bap_entrada = self.to_date(p['data_bap_entrada'])
if p['data_bap_saida']:
funcao.data_bap_saida = self.to_date(p['data_bap_saida'])
if p['data_entrada']:
funcao.inicio_funcao = self.to_date(p['data_entrada'])
if p['data_saida']:
funcao.fim_funcao = self.to_date(p['data_saida'])
funcao.bap_entrada = p['bap_entrada']
funcao.bap_saida = p['bap_saida']
funcao.save()
if re.search(r'estagi.ri[o|a]',p['cargo'],re.I):
#TODO inserir dados de estagio
pass
if p['inicio_ferias'] and p['final_ferias']:
servidor.ferias_set.get_or_create(
inicio_ferias = self.to_date(p['inicio_ferias']),
fim_ferias = self.to_date(p['final_ferias']),
obs = p['obs_ferias']
)
if p['inicio_licenca'] and p['fim_licenca']:
servidor.licenca_set.get_or_create(
inicio_licenca = self.to_date(p['inicio_licenca']),
fim_licenca = self.to_date(p['fim_licenca']),
obs = p['obs_licenca']
)
servidor.save()

100
sigi/apps/servidores/management/commands/sync_ldap.py

@ -0,0 +1,100 @@
# coding= utf-8
import ldap
from django.core.management.base import BaseCommand, CommandError
from django.contrib.auth.models import User, Group
from sigi.settings import *
from sigi.apps.servidores.models import Servidor
class Command(BaseCommand):
help = 'Sincroniza Usuários e Servidores com o LDAP'
def handle(self, *args, **options):
self.sync_groups()
self.sync_users()
def get_ldap_groups(self):
filter = "(&(objectclass=Group))"
values = ['cn',]
l = ldap.initialize(AUTH_LDAP_SERVER_URI)
l.protocol_version = ldap.VERSION3
l.simple_bind_s(AUTH_LDAP_BIND_DN.encode('utf-8'),AUTH_LDAP_BIND_PASSWORD)
result_id = l.search(AUTH_LDAP_GROUP, ldap.SCOPE_SUBTREE, filter, values)
result_type, result_data = l.result(result_id, 1)
l.unbind()
return result_data
def get_ldap_users(self):
filter = "(&(objectclass=user))"
values = ['sAMAccountName', 'userPrincipalName', 'givenName', 'sn', 'cn' ]
l = ldap.initialize(AUTH_LDAP_SERVER_URI)
l.protocol_version = ldap.VERSION3
l.simple_bind_s(AUTH_LDAP_BIND_DN.encode('utf-8'),AUTH_LDAP_BIND_PASSWORD)
result_id = l.search(AUTH_LDAP_USER.encode('utf-8'), ldap.SCOPE_SUBTREE, filter, values)
result_type, result_data = l.result(result_id, 1)
l.unbind()
return result_data
def sync_groups(self):
ldap_groups = self.get_ldap_groups()
for ldap_group in ldap_groups:
try: group_name = ldap_group[1]['cn'][0]
except: pass
else:
try: group = Group.objects.get(name=group_name)
except Group.DoesNotExist:
group = Group(name=group_name)
group.save()
print "Group '%s' created." % group_name
print "Groups are synchronized."
def sync_users(self):
ldap_users = self.get_ldap_users()
for ldap_user in ldap_users:
try: username = ldap_user[1]['sAMAccountName'][0]
except: pass
else:
try: email = ldap_user[1]['userPrincipalName'][0]
except: email = ''
try: first_name = ldap_user[1]['givenName'][0]
except: first_name = username
try: last_name = ldap_user[1]['sn'][0][:30]
except: last_name = ''
try: user = User.objects.get(username=username)
except User.DoesNotExist:
try:
user = User.objects.get(email=email)
user.username = username
except User.DoesNotExist:
user = User.objects.create_user(
username = username,
email = email
)
user.first_name = first_name
user.last_name = last_name
print "User '%s' created." % username
try: nome_completo = ldap_user[1]['cn'][0]
except: nome_completo = ''
try:
servidor = user.servidor
if not servidor.nome_completo == nome_completo.decode('utf8'):
servidor.nome_completo = nome_completo
print "Servidor '%s' updated." % nome_completo
except Servidor.DoesNotExist:
try: servidor = Servidor.objects.get(nome_completo=nome_completo)
except Servidor.DoesNotExist:
servidor = user.servidor_set.create(nome_completo=nome_completo)
print "Servidor '%s' created." % nome_completo
else:
if not user.email == email.decode('utf8'):
user.email = email
print "User '%s' email updated." % username
if not user.first_name == first_name.decode('utf8'):
user.first_name = first_name
print "User '%s' first name updated." % username
if not user.last_name == last_name.decode('utf8'):
user.last_name = last_name
print "User '%s' last name updated." % username
servidor.user = user
servidor.save()
user.save()
print "Users are synchronized."

223
sigi/apps/servidores/models.py

@ -0,0 +1,223 @@
# -*- coding: utf-8 -*-
from django.db import models
from django.db.models.signals import post_save
from django.contrib.contenttypes import generic
from django.contrib.auth.models import User
class Subsecretaria(models.Model):
""" Modelo para representação das Subsecretarias do Interlegis
"""
nome = models.CharField(max_length=250, null=True)
sigla = models.CharField(max_length=10, null=True)
# servidor responsavel por dirigir a Subsecretaria
responsavel = models.ForeignKey('servidores.Servidor', related_name='diretor', null=True)
class Meta:
ordering = ('nome',)
def __unicode__(self):
return '%s (%s)' % (unicode(self.nome), unicode(self.sigla))
class Servico(models.Model):
""" Modelo para representação dos Serviços de uma Subsecretaria
"""
nome = models.CharField(max_length=250, null=True)
sigla = models.CharField(max_length=10, null=True)
subsecretaria = models.ForeignKey(Subsecretaria, null=True)
# servidor responsavel por chefiar o serviço
responsavel = models.ForeignKey('servidores.Servidor', related_name='chefe', null=True)
class Meta:
ordering = ('nome',)
verbose_name = 'serviço'
verbose_name_plural = 'serviços'
def __unicode__(self):
return '%s (%s)' % (unicode(self.nome), unicode(self.sigla))
class Servidor(models.Model):
""" Modelo para representação de um Servidor.
Um servidor pertence a um Serviço e uma Subsecretaria os campos
deste modelo são referente as informações básicas de cadastro.
"""
SEXO_CHOICES = (
('M', u'Masculino'),
('F', u'Feminino'),
)
TURNO_CHOICES = (
('M', u'Manhã'),
('T', u'Tarde'),
('N', u'Noite'),
)
# usuario responsavel pela autenticação do servidor no sistema
user = models.ForeignKey(User, unique=True)
user.is_active__filter = True
nome_completo = models.CharField(max_length=128)
nome_completo.alphabetic_filter = True
apelido = models.CharField(max_length=50, blank=True)
# caminho no sistema para arquivo com a imagem
foto = models.ImageField(
upload_to='fotos/servidores',
width_field='foto_largura',
height_field='foto_altura',
blank=True
)
foto_largura = models.SmallIntegerField(editable=False, null=True)
foto_altura = models.SmallIntegerField(editable=False, null=True)
sexo = models.CharField(
max_length=1,
choices=SEXO_CHOICES,
blank=True,
null=True,
)
data_nascimento = models.DateField(
'data de nascimento',
blank=True,
null=True,
)
servico = models.ForeignKey('servidores.Servico', blank=True, null=True)
matricula = models.CharField(u'matrícula', max_length=25, blank=True, null=True)
turno= models.CharField(
max_length=1,
choices=TURNO_CHOICES,
blank=True,
null=True,
)
de_fora = models.BooleanField(default=False)
data_nomeacao = models.DateField(u'data de nomeação', blank=True, null=True)
ato_exoneracao = models.CharField(u'ato de exoneração',max_length=150, blank=True, null=True)
ato_numero = models.CharField(u'ato de exoneração',max_length=150, blank=True, null=True)
cpf = models.CharField('CPF', max_length=11, blank=True, null=True)
rg = models.CharField('RG', max_length=25, blank=True, null=True)
obs = models.TextField(u'observação', blank=True, null=True)
apontamentos = models.TextField(u'apontamentos', blank=True, null=True)
# Informações de contato
email_pessoal = models.EmailField('email pessoal', blank=True, null=True)
endereco = generic.GenericRelation('contatos.Endereco')
telefones = generic.GenericRelation('contatos.Telefone')
ramal = models.CharField(max_length=25, blank=True, null=True)
class Meta:
ordering = ('nome_completo',)
verbose_name_plural = 'servidores'
def is_chefe(self):
""" Verifica se o servidor é chefe ou diretor
"""
pass
def data_entrada(self):
""" Verifica a data de entrada da função mais antiga
"""
pass
def data_saida(self):
""" Verifica a data de saída da função mais recente
de um servidor desativado
Caso o usuário esteja ativo retorna None
"""
pass
@property
def diagnosticos(self):
""" Retorna todos os diagnosticos que este servidor
participa, isto é, como responsavel ou parte da equipe
"""
diagnosticos = set(self.diagnostico_set.filter(publicado=True).all())
for equipe in self.equipe_set.all():
diagnosticos.add(equipe.diagnostico)
return list(diagnosticos)
def __unicode__(self):
return self.nome_completo
# Soluçao alternativa para extender o usuário do django
# Acessa do servidor de um objeto user criando um profile
# baseado nos dados do LDAP
User.servidor = property(lambda user: Servidor.objects.get(user=user))
# Sinal para ao criar um usuário criar um servidor
# baseado no nome contino no LDAP
def create_user_profile(sender, instance, created, **kwargs):
if created:
Servidor.objects.create(
user=instance,
nome_completo= "%s %s" % (instance.first_name, instance.last_name)
)
post_save.connect(create_user_profile, sender=User)
class Funcao(models.Model):
""" Modelo para guardar o histórico de funções dos
servidores no Interlegis
"""
servidor = models.ForeignKey(Servidor)
funcao = models.CharField(max_length=250, null=True)
cargo = models.CharField(max_length=250, null=True)
inicio_funcao = models.DateField(u'início da função', null=True)
fim_funcao = models.DateField(u'fim da função', blank=True, null=True)
descricao = models.TextField(u'descrição', blank=True, null=True)
bap_entrada = models.CharField('BAP de entrada',max_length=50, blank=True, null=True)
data_bap_entrada = models.DateField('data BAP de entrada', blank=True, null=True)
bap_saida = models.CharField(u'BAP de saída',max_length=50, blank=True, null=True)
data_bap_saida = models.DateField(u'data BAP de saída', blank=True, null=True)
class Meta:
verbose_name = u'função'
verbose_name_plural = u'funções'
def __unicode__(self):
return str(self.id)
class Licenca(models.Model):
""" Modelo que representa as licenças tiradas pelos servidores
"""
servidor = models.ForeignKey(Servidor)
inicio_licenca = models.DateField(u'início da licença')
fim_licenca = models.DateField(u'fim da licença')
obs = models.TextField(u'observação', blank=True, null=True)
class Meta:
verbose_name = u'licença'
verbose_name_plural = u'licenças'
def days():
""" Calcula a quantidade de dias da licença
"""
pass
def __unicode__(self):
return str(self.id)
class Ferias(models.Model):
""" Modelo que representa as férias tiradas pelos servidores
"""
servidor = models.ForeignKey(Servidor)
inicio_ferias = models.DateField(u'início das férias')
fim_ferias = models.DateField(u'fim das férias')
obs = models.TextField(u'observação', blank=True, null=True)
class Meta:
verbose_name = u'férias'
verbose_name_plural = u'férias'
def days():
""" Calcula a quantidade de dias das férias
"""
pass
def __unicode__(self):
return str(self.id)

33
sigi/apps/servidores/views.py

@ -0,0 +1,33 @@
# -*- coding: utf8 -*-
import new
from django.template import RequestContext
from django.shortcuts import render_to_response, get_object_or_404, redirect
from django.db.models import Avg, Max, Min, Count
from sigi.apps.servidores.models import Servidor, Funcao
from sigi.shortcuts import render_to_pdf
def servidores_por_funcao(request):
report = Funcao.objects.values('funcao').annotate(funcao__count=Count('funcao')).order_by('funcao__count')
total = Funcao.objects.count()
context = RequestContext(request, {
'pagesize':'A4',
'report': report,
'total': total
})
return render_to_pdf('servidores/servidores_por_funcao.html', context)
def servidores_por_cargo(request):
report = Funcao.objects.values('cargo').annotate(cargo__count=Count('cargo')).order_by('cargo__count')
total = Funcao.objects.count()
context = RequestContext(request, {
'pagesize':'A4',
'report': report,
'total': total
})
return render_to_pdf('servidores/servidores_por_cargo.html', context)

1
sigi/settings.py

@ -39,6 +39,7 @@ INSTALLED_APPS = (
# Local apps
'sigi.apps.contatos',
'sigi.apps.servidores',
)
MIDDLEWARE_CLASSES = (

Loading…
Cancel
Save