style Seguir las directrices de estilo de código de Django

* Use single quotes for strings, or a double quote if the string contains a single quote.
* An exception to PEP 8 is our rules on line lengths. Don’t limit lines of code to
  79 characters if it means the code looks significantly uglier or is harder to read.
  We allow up to 119 characters as this is the width of GitHub code review;
  anything longer requires horizontal scrolling which makes review more difficult.

Ver <https://docs.djangoproject.com/en/dev/internals/contributing/writing-code/coding-style/>
parent 4ef397c2
Pipeline #532 failed with stage
in 0 seconds
[flake8]
exclude=**/settings*.py,**/__init__.py,**/migrations/*.py
ignore = W503
max-line-length = 119
max-complexity = 10
select = B,C,E,F,W
......@@ -9,12 +9,7 @@ class CustomUserAdmin(UserAdmin):
add_form = CustomUserCreationForm
form = CustomUserChangeForm
model = CustomUser
list_display = [
"username",
"email",
"first_name",
"last_name",
] # Campos que se muestran en el listado
list_display = ['username', 'email', 'first_name', 'last_name'] # Campos que se muestran en el listado
admin.site.register(CustomUser, CustomUserAdmin)
......@@ -2,4 +2,4 @@ from django.apps import AppConfig
class AccountsConfig(AppConfig):
name = "accounts"
name = 'accounts'
......@@ -10,119 +10,78 @@ class Migration(migrations.Migration):
initial = True
dependencies = [("auth", "0009_alter_user_last_name_max_length")]
dependencies = [('auth', '0009_alter_user_last_name_max_length')]
operations = [
migrations.CreateModel(
name="CustomUser",
name='CustomUser',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('password', models.CharField(max_length=128, verbose_name='password')),
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
(
"id",
models.AutoField(
auto_created=True,
primary_key=True,
serialize=False,
verbose_name="ID",
),
),
("password", models.CharField(max_length=128, verbose_name="password")),
(
"last_login",
models.DateTimeField(
blank=True, null=True, verbose_name="last login"
),
),
(
"is_superuser",
'is_superuser',
models.BooleanField(
default=False,
help_text="Designates that this user has all permissions without explicitly assigning them.",
verbose_name="superuser status",
help_text='Designates that this user has all permissions without explicitly assigning them.',
verbose_name='superuser status',
),
),
(
"username",
'username',
models.CharField(
error_messages={
"unique": "A user with that username already exists."
},
help_text="Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.",
error_messages={'unique': 'A user with that username already exists.'},
help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.',
max_length=150,
unique=True,
validators=[
django.contrib.auth.validators.UnicodeUsernameValidator()
],
verbose_name="username",
),
),
(
"first_name",
models.CharField(
blank=True, max_length=30, verbose_name="first name"
validators=[django.contrib.auth.validators.UnicodeUsernameValidator()],
verbose_name='username',
),
),
('first_name', models.CharField(blank=True, max_length=30, verbose_name='first name')),
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
(
"last_name",
models.CharField(
blank=True, max_length=150, verbose_name="last name"
),
),
(
"email",
models.EmailField(
blank=True, max_length=254, verbose_name="email address"
),
),
(
"is_staff",
'is_staff',
models.BooleanField(
default=False,
help_text="Designates whether the user can log into this admin site.",
verbose_name="staff status",
help_text='Designates whether the user can log into this admin site.',
verbose_name='staff status',
),
),
(
"is_active",
'is_active',
models.BooleanField(
default=True,
help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.",
verbose_name="active",
),
),
(
"date_joined",
models.DateTimeField(
default=django.utils.timezone.now, verbose_name="date joined"
help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.',
verbose_name='active',
),
),
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
(
"groups",
'groups',
models.ManyToManyField(
blank=True,
help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.",
related_name="user_set",
related_query_name="user",
to="auth.Group",
verbose_name="groups",
help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.',
related_name='user_set',
related_query_name='user',
to='auth.Group',
verbose_name='groups',
),
),
(
"user_permissions",
'user_permissions',
models.ManyToManyField(
blank=True,
help_text="Specific permissions for this user.",
related_name="user_set",
related_query_name="user",
to="auth.Permission",
verbose_name="user permissions",
help_text='Specific permissions for this user.',
related_name='user_set',
related_query_name='user',
to='auth.Permission',
verbose_name='user permissions',
),
),
],
options={
"verbose_name": "user",
"verbose_name_plural": "users",
"abstract": False,
},
managers=[("objects", django.contrib.auth.models.UserManager())],
options={'verbose_name': 'user', 'verbose_name_plural': 'users', 'abstract': False},
managers=[('objects', django.contrib.auth.models.UserManager())],
)
]
......@@ -5,77 +5,63 @@ from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [("accounts", "0001_initial")]
dependencies = [('accounts', '0001_initial')]
operations = [
migrations.AddField(
model_name="customuser",
name="centro_id_nks",
field=models.CharField(
blank=True, max_length=127, null=True, verbose_name="Cód. centros"
),
model_name='customuser',
name='centro_id_nks',
field=models.CharField(blank=True, max_length=127, null=True, verbose_name='Cód. centros'),
),
migrations.AddField(
model_name="customuser",
name="colectivos",
field=models.CharField(blank=True, max_length=127, null=True),
model_name='customuser', name='colectivos', field=models.CharField(blank=True, max_length=127, null=True)
),
migrations.AddField(
model_name="customuser",
name="departamento_id_nks",
field=models.CharField(
blank=True, max_length=127, null=True, verbose_name="Cód. departamentos"
),
model_name='customuser',
name='departamento_id_nks',
field=models.CharField(blank=True, max_length=127, null=True, verbose_name='Cód. departamentos'),
),
migrations.AddField(
model_name="customuser",
name="last_name_2",
field=models.CharField(
blank=True, max_length=150, null=True, verbose_name="segundo apellido"
),
model_name='customuser',
name='last_name_2',
field=models.CharField(blank=True, max_length=150, null=True, verbose_name='segundo apellido'),
),
migrations.AddField(
model_name="customuser",
name="nombre_oficial",
model_name='customuser',
name='nombre_oficial',
field=models.CharField(blank=True, max_length=50, null=True),
),
migrations.AddField(
model_name="customuser",
name="numero_documento",
model_name='customuser',
name='numero_documento',
field=models.CharField(
blank=True,
help_text="DNI, NIE o pasaporte.",
help_text='DNI, NIE o pasaporte.',
max_length=16,
null=True,
verbose_name="número de documento",
verbose_name='número de documento',
),
),
migrations.AddField(
model_name="customuser",
name="sexo",
field=models.CharField(blank=True, max_length=1, null=True),
model_name='customuser', name='sexo', field=models.CharField(blank=True, max_length=1, null=True)
),
migrations.AddField(
model_name="customuser",
name="sexo_oficial",
field=models.CharField(blank=True, max_length=1, null=True),
model_name='customuser', name='sexo_oficial', field=models.CharField(blank=True, max_length=1, null=True)
),
migrations.AddField(
model_name="customuser",
name="tipo_documento",
model_name='customuser',
name='tipo_documento',
field=models.CharField(
blank=True,
help_text="DNI, NIE o pasaporte.",
help_text='DNI, NIE o pasaporte.',
max_length=3,
null=True,
verbose_name="tipo de documento",
verbose_name='tipo de documento',
),
),
migrations.AlterField(
model_name="customuser",
name="first_name",
field=models.CharField(
blank=True, max_length=50, verbose_name="first name"
),
model_name='customuser',
name='first_name',
field=models.CharField(blank=True, max_length=50, verbose_name='first name'),
),
]
......@@ -6,11 +6,8 @@ from django.db import migrations
class Migration(migrations.Migration):
dependencies = [("accounts", "0002_auto_20190314_0819")]
dependencies = [('accounts', '0002_auto_20190314_0819')]
operations = [
migrations.AlterModelManagers(
name="customuser",
managers=[("objects", accounts.models.CustomUserManager())],
)
migrations.AlterModelManagers(name='customuser', managers=[('objects', accounts.models.CustomUserManager())])
]
......@@ -7,10 +7,10 @@ from django.utils.translation import gettext_lazy as _
class CustomUserManager(UserManager):
def get_or_none(self, **kwargs):
"""Devuelve el usuario con las propiedades indicadas.
'''Devuelve el usuario con las propiedades indicadas.
Si no se encuentra, devuelve `None`.
"""
'''
try:
return self.get(**kwargs)
except CustomUser.DoesNotExist:
......@@ -19,42 +19,26 @@ class CustomUserManager(UserManager):
class CustomUser(AbstractUser):
# Campos sobrescritos
first_name = models.CharField(
_("first name"), max_length=50, blank=True # era: max_length=30
)
first_name = models.CharField(_('first name'), max_length=50, blank=True) # era: max_length=30
# Campos adicionales
numero_documento = models.CharField(
_("número de documento"),
max_length=16,
blank=True,
null=True,
help_text=_("DNI, NIE o pasaporte."),
_('número de documento'), max_length=16, blank=True, null=True, help_text=_('DNI, NIE o pasaporte.')
)
tipo_documento = models.CharField(
_("tipo de documento"),
max_length=3,
blank=True,
null=True,
help_text=_("DNI, NIE o pasaporte."),
)
last_name_2 = models.CharField(
_("segundo apellido"), max_length=150, blank=True, null=True
_('tipo de documento'), max_length=3, blank=True, null=True, help_text=_('DNI, NIE o pasaporte.')
)
last_name_2 = models.CharField(_('segundo apellido'), max_length=150, blank=True, null=True)
sexo = models.CharField(max_length=1, blank=True, null=True)
sexo_oficial = models.CharField(max_length=1, blank=True, null=True)
nombre_oficial = models.CharField(max_length=50, blank=True, null=True)
centro_id_nks = models.CharField(
_("Cód. centros"), max_length=127, blank=True, null=True
)
departamento_id_nks = models.CharField(
_("Cód. departamentos"), max_length=127, blank=True, null=True
)
centro_id_nks = models.CharField(_('Cód. centros'), max_length=127, blank=True, null=True)
departamento_id_nks = models.CharField(_('Cód. departamentos'), max_length=127, blank=True, null=True)
colectivos = models.CharField(max_length=127, blank=True, null=True)
# Metodos sobrescritos
def get_full_name(self):
"""Devuelve el nombre completo (nombre y los dos apellidos)."""
full_name = "%s %s %s" % (self.first_name, self.last_name, self.last_name_2)
'''Devuelve el nombre completo (nombre y los dos apellidos).'''
full_name = '%s %s %s' % (self.first_name, self.last_name, self.last_name_2)
return full_name.strip()
# Métodos adicionales
......@@ -62,25 +46,25 @@ class CustomUser(AbstractUser):
return self.username
def get_colectivo_principal(self):
"""Devuelve el colectivo principal del usuario.
'''Devuelve el colectivo principal del usuario.
Se determina usando el orden de prelación PDI > ADS > PAS > EST.
"""
'''
colectivos_del_usuario = json.loads(self.colectivos) if self.colectivos else []
for col in ("PDI", "ADS", "PAS", "EST"):
for col in ('PDI', 'ADS', 'PAS', 'EST'):
if col in colectivos_del_usuario:
return col
return None
def get_num_equipos(self, anyo):
"""Devuelve el número de equipos de trabajo en los que participa el usuario."""
'''Devuelve el número de equipos de trabajo en los que participa el usuario.'''
num_como_participante = self.vinculaciones.filter(
tipo_participacion="participante", proyecto__convocatoria_id=anyo
tipo_participacion='participante', proyecto__convocatoria_id=anyo
).count()
num_como_coordinador = self.vinculaciones.filter(
tipo_participacion__in=["coordinador", "coordinador_principal"],
tipo_participacion__in=['coordinador', 'coordinador_principal'],
proyecto__convocatoria_id=anyo,
proyecto__estado="SOLICITADO",
proyecto__estado='SOLICITADO',
).count()
num_equipos = num_como_participante + num_como_coordinador
return num_equipos
......
......@@ -14,18 +14,14 @@ from django.core.validators import ValidationError, validate_email
def get_identidad(strategy, response, user, *args, **kwargs):
"""Actualiza el usuario con los datos obtenidos de Gestión de Identidades."""
'''Actualiza el usuario con los datos obtenidos de Gestión de Identidades.'''
wsdl = get_config('WSDL_IDENTIDAD')
session = Session()
session.auth = HTTPBasicAuth(
get_config("USER_IDENTIDAD"), get_config("PASS_IDENTIDAD")
)
session.auth = HTTPBasicAuth(get_config('USER_IDENTIDAD'), get_config('PASS_IDENTIDAD'))
try:
client = zeep.Client(
wsdl=wsdl, transport=zeep.transports.Transport(session=session)
)
client = zeep.Client(wsdl=wsdl, transport=zeep.transports.Transport(session=session))
except RequestConnectionError:
raise RequestConnectionError('No fue posible conectarse al WS de Identidades.')
except: # noqa: E722
......@@ -44,12 +40,8 @@ def get_identidad(strategy, response, user, *args, **kwargs):
user.first_name = identidad.nombre
user.last_name = identidad.primerApellido
user.last_name_2 = identidad.segundoApellido
correo_personal = (
identidad.correoPersonal if is_email_valid(identidad.correoPersonal) else None
)
correo_principal = (
identidad.correoPrincipal if is_email_valid(identidad.correoPrincipal) else None
)
correo_personal = identidad.correoPersonal if is_email_valid(identidad.correoPersonal) else None
correo_principal = identidad.correoPrincipal if is_email_valid(identidad.correoPrincipal) else None
# El email es un campo NOT NULL en el modelo.
user.email = correo_personal or correo_principal or ''
user.is_active = identidad.activo != 'N'
......@@ -71,7 +63,7 @@ def get_identidad(strategy, response, user, *args, **kwargs):
def is_email_valid(email):
"""Validate email address"""
'''Validate email address'''
try:
validate_email(email)
except ValidationError:
......
......@@ -6,17 +6,13 @@ from . import views
urlpatterns = [
# Evita que un usuario ya autenticado pueda volver a la página de inicio de sesión
path(
"login/",
auth_views.LoginView.as_view(redirect_authenticated_user=True),
name="login",
),
path('login/', auth_views.LoginView.as_view(redirect_authenticated_user=True), name='login'),
# Logout personalizado para solicitar el fin de la sesión SAML.
path("logout/", views.LogoutView.as_view(), name="logout"),
path('logout/', views.LogoutView.as_view(), name='logout'),
# Finalizar la sesión SAML.
path("sls/", views.SlsView.as_view(), name="sls"),
path('sls/', views.SlsView.as_view(), name='sls'),
# Muestra los metadatos para el Proveedor de Identidad (IdP) de SAML.
path("metadata", views.metadata_xml, name="metadata"),
path('metadata', views.metadata_xml, name='metadata'),
# Muestra los datos del usuario.
path("userdata", views.UserdataView.as_view(), name="userdata"),
path('userdata', views.UserdataView.as_view(), name='userdata'),
]
......@@ -11,74 +11,67 @@ from social_django.utils import load_backend, load_strategy
def metadata_xml(request):
"""Muestra los metadatos para el Proveedor de Identidad (IdP) de SAML."""
complete_url = reverse("social:complete", args=("saml",))
saml_backend = load_backend(
load_strategy(request), "saml", redirect_uri=complete_url
)
'''Muestra los metadatos para el Proveedor de Identidad (IdP) de SAML.'''
complete_url = reverse('social:complete', args=('saml',))
saml_backend = load_backend(load_strategy(request), 'saml', redirect_uri=complete_url)
metadata, errors = saml_backend.generate_metadata_xml()
if errors:
raise Exception("\n".join(errors))
return HttpResponse(content=metadata, content_type="text/xml")
raise Exception('\n'.join(errors))
return HttpResponse(content=metadata, content_type='text/xml')
class UserdataView(LoginRequiredMixin, View):
"""Muestra los datos del usuario."""
'''Muestra los datos del usuario.'''
def get(self, request, *args, **kwargs):
context = {}
context["datos_usuario"] = {
field.name: field.value_to_string(request.user)
for field in request.user._meta.fields
context['datos_usuario'] = {
field.name: field.value_to_string(request.user) for field in request.user._meta.fields
}
return render(request, "registration/userdata.html", context=context)
return render(request, 'registration/userdata.html', context=context)
@method_decorator(never_cache, name="dispatch")
@method_decorator(never_cache, name='dispatch')
class LogoutView(LoginRequiredMixin, RedirectView):
"""Log out the current user.
'''Log out the current user.
This view logs out a locally authenticated user,
or sends a Logout request to the SAML2 Identity Provider.
"""
'''
url = reverse_lazy(get_config("LOGOUT_REDIRECT_URL"))
url = reverse_lazy(get_config('LOGOUT_REDIRECT_URL'))
def get(self, request, *args, **kwargs):
saml_backend = load_backend(
load_strategy(request), "saml", redirect_uri="{% url 'sls' %}"
)
saml_backend = load_backend(load_strategy(request), 'saml', redirect_uri="{% url 'sls' %}")
# As of now, this code only handles the first association.
association = request.user.social_auth.first()
if association:
idp_name = association.uid.split(":")[0]
idp_name = association.uid.split(':')[0]
sls_url = saml_backend.request_logout(idp_name, association)
return redirect(sls_url)
logout(request)
return super().get(request, *args, **kwargs)
@method_decorator(never_cache, name="dispatch")
@method_decorator(never_cache, name='dispatch')
class SlsView(View):
"""
'''
The Single Logout Service processes the Logout response from the Identity Provider.
The logout request may have been generated by this Service Provider, or other SP.
"""
'''
url = reverse_lazy(get_config("LOGOUT_REDIRECT_URL"))
url = reverse_lazy(get_config('LOGOUT_REDIRECT_URL'))
def get(self, request, *args, **kwargs):
saml_backend = load_backend(
load_strategy(request), "saml", redirect_uri=str(self.url)
)
saml_backend = load_backend(load_strategy(request), 'saml', redirect_uri=str(self.url))
# As of now, this code only handles the first association.
association = request.user.social_auth.first()
idp_name = association.uid.split(":")[0]
idp_name = association.uid.split(':')[0]
_, errors = saml_backend.process_logout(idp_name, None)
if errors:
messages = "\n".join(errors)
messages = '\n'.join(errors)
raise Exception(messages)
logout(request)
......
......@@ -2,4 +2,4 @@ from django.apps import AppConfig
class IndoConfig(AppConfig):
name = "indo"
name = 'indo'
......@@ -13,21 +13,19 @@ from .models import Linea, ParticipanteProyecto, Programa, Proyecto, TipoPartici
class InvitacionForm(forms.ModelForm):
nip = forms.IntegerField(
label=_("NIP"),
help_text=_(
"Número de Identificación Personal en la Universidad de Zaragoza de la persona a invitar."
),
label=_('NIP'),
help_text=_('Número de Identificación Personal en la Universidad de Zaragoza de la persona a invitar.'),
)
def __init__(self, *args, **kwargs):
# Override __init__ to make the "self" object have the proyecto instance
# designated by the proyecto_id sent by the view, taken from the URL parameter.
self.proyecto = Proyecto.objects.get(id=kwargs.pop("proyecto_id"))
self.request = kwargs.pop("request")
self.proyecto = Proyecto.objects.get(id=kwargs.pop('proyecto_id'))
self.request = kwargs.pop('request')
super().__init__(*args, **kwargs)
def _crear_usuario(self, nip):
"""Crea un registro de usuario con el nip indicado y los datos de G.I."""
'''Crea un registro de usuario con el nip indicado y los datos de G.I.'''
usuario = CustomUser.objects.create_user(username=nip)
try:
......@@ -36,19 +34,17 @@ class InvitacionForm(forms.ModelForm):
# Si Gestión de Identidades devuelve un error, borramos el usuario
# y finalizamos mostrando el mensaje de error.
usuario.delete()
raise forms.ValidationError("ERROR: " + str(ex))
raise forms.ValidationError('ERROR: ' + str(ex))
# HACK - Indicamos que la autenticación es vía Single Sign On con SAML.
usuario_social = UserSocialAuth(
uid=f"lord:{usuario.username}", provider="saml", user=usuario
)
usuario_social = UserSocialAuth(uid=f'lord:{usuario.username}', provider='saml', user=usuario)
usuario_social.save()
return usuario
def clean(self):
cleaned_data = super().clean()
nip = cleaned_data.get("nip")
nip = cleaned_data.get('nip')
# Comprobamos si el usuario ya existe en el sistema.
usuario = CustomUser.objects.get_or_none(username=nip)
......@@ -65,112 +61,83 @@ class InvitacionForm(forms.ModelForm):
# Si el usuario no está activo, finalizamos explicando esta circunstancia.
if not usuario.is_active:
raise forms.ValidationError(
_("Usuario inactivo en el sistema de Gestión de Identidades")
)
raise forms.ValidationError(_('Usuario inactivo en el sistema de Gestión de Identidades'))
# Si el usuario no tiene un email válido, finalizamos explicando esta circunstancia.
if not usuario.email:
raise forms.ValidationError(
_(
f"No fue posible invitar al usuario «{nip}» porque no tiene "
"establecida ninguna dirección de correo electrónico en el sistema "
"de Gestión de Identidades."
f'No fue posible invitar al usuario «{nip}» porque no tiene '
'establecida ninguna dirección de correo electrónico en el sistema '
'de Gestión de Identidades.'
)
)
cleaned_data["usuario"] = usuario
cleaned_data['usuario'] = usuario
# Si un usuario ya está vinculado al proyecto, no se le puede invitar.
vinculados = self.proyecto.get_usuarios_vinculados()
if usuario in vinculados:
raise forms.ValidationError(
_(
f"No puede invitar a {usuario.get_full_name()} "
"porque ya está vinculado a este proyecto."
)
_(f'No puede invitar a {usuario.get_full_name()} ' 'porque ya está vinculado a este proyecto.')
)
# La participación de los estudiantes estará limitada a dos por proyecto
# (excepto en los PIPOUZ).
if (
self.proyecto.programa.nombre_corto != "PIPOUZ"
and usuario.get_colectivo_principal() == "EST"
):
estudiantes = [
vinculado
for vinculado in vinculados
if vinculado.get_colectivo_principal() == "EST"
]
if self.proyecto.programa.nombre_corto != 'PIPOUZ' and usuario.get_colectivo_principal() == 'EST':
estudiantes = [vinculado for vinculado in vinculados if vinculado.get_colectivo_principal() == 'EST']
if len(estudiantes) >= self.proyecto.programa.max_estudiantes:
nombres_estudiantes = ", ".join(
list(map(lambda e: e.get_full_name(), estudiantes))
)
nombres_estudiantes = ', '.join(list(map(lambda e: e.get_full_name(), estudiantes)))
raise forms.ValidationError(
_(
"Ya se ha alcanzado el máximo de participación de "
f"{self.proyecto.programa.max_estudiantes} estudiantes "
f"por proyecto: {nombres_estudiantes}."
'Ya se ha alcanzado el máximo de participación de '
f'{self.proyecto.programa.max_estudiantes} estudiantes '
f'por proyecto: {nombres_estudiantes}.'
)
)
def save(self, commit=True):
invitado = super().save(commit=False)
invitado.proyecto = self.proyecto
invitado.tipo_participacion = TipoParticipacion("invitado")
invitado.usuario = self.cleaned_data["usuario"]
invitado.tipo_participacion = TipoParticipacion('invitado')
invitado.usuario = self.cleaned_data['usuario']
return invitado.save()