diff --git a/Templates/identify/register.html b/Templates/identify/register.html
index ebdca10..0dc0d2a 100644
--- a/Templates/identify/register.html
+++ b/Templates/identify/register.html
@@ -101,6 +101,51 @@
{% trans "Create Account" %}
{% endfor %}
+
+
+ {% for error in form.gender.errors %}
+ {{ error }}
+ {% endfor %}
+
+
+
+
+ {% for error in form.age_range.errors %}
+ {{ error }}
+ {% endfor %}
+
+
+
+
+ {% for error in form.province.errors %}
+ {{ error }}
+ {% endfor %}
+
+
+
+
+ {% for error in form.municipality.errors %}
+ {{ error }}
+ {% endfor %}
+
+
diff --git a/users/templates/users/parts/dashboard_cross_tab.html b/users/templates/users/parts/dashboard_cross_tab.html
new file mode 100644
index 0000000..55ec2ef
--- /dev/null
+++ b/users/templates/users/parts/dashboard_cross_tab.html
@@ -0,0 +1,97 @@
+{% extends 'users/types/user_admin.html' %}
+{% load i18n %}
+
+{% block parts_content %}
+
+ {% include 'users/parts/filter_form.html' %}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ | {% trans 'Gender' %} |
+ {% for header in cross_table_headers %}
+ {{ header }} |
+ {% endfor %}
+
+
+
+ {% for row in cross_table_rows %}
+
+ {% for cell in row %}
+ | {{ cell }} |
+ {% endfor %}
+
+ {% endfor %}
+
+
+
+
+
+
+
+
+
+
+{% endblock %}
diff --git a/users/templates/users/parts/dashboard_demographics.html b/users/templates/users/parts/dashboard_demographics.html
new file mode 100644
index 0000000..f6bca3c
--- /dev/null
+++ b/users/templates/users/parts/dashboard_demographics.html
@@ -0,0 +1,122 @@
+{% extends 'users/types/user_admin.html' %}
+{% load i18n %}
+
+{% block parts_content %}
+
+ {% include 'users/parts/filter_form.html' %}
+
+
+
+
+
+
+
+
+
+{% endblock %}
diff --git a/users/templates/users/parts/dashboard_overview.html b/users/templates/users/parts/dashboard_overview.html
index 5148afd..237ce03 100644
--- a/users/templates/users/parts/dashboard_overview.html
+++ b/users/templates/users/parts/dashboard_overview.html
@@ -6,7 +6,7 @@
-
- {{ form.location }}
+
+ {{ form.gender }}
+
+
+
+
+ {{ form.age_range }}
+
+
+
+
+
+
+
+
+
+
+
+
{% endblock %}
\ No newline at end of file
diff --git a/users/templates/users/types/user_admin.html b/users/templates/users/types/user_admin.html
index 5774e99..0463fd8 100644
--- a/users/templates/users/types/user_admin.html
+++ b/users/templates/users/types/user_admin.html
@@ -23,6 +23,10 @@
{% trans 'Directors' %}
+
+ {% trans 'Demographics' %}
+
diff --git a/users/templates/users/types/user_client.html b/users/templates/users/types/user_client.html
index e4e2aa9..11ddd18 100644
--- a/users/templates/users/types/user_client.html
+++ b/users/templates/users/types/user_client.html
@@ -28,13 +28,35 @@
{{ user.last_login|date:"d/m/Y" }}
+
+
{% trans "Gender" %}
+
+ {% if user.gender %}
+ {{ user.get_gender_display }}
+ {% else %}
+ {% trans "Not specified" %}
+ {% endif %}
+
+
+
+
+
{% trans "Age range" %}
+
+ {% if user.age_range %}
+ {{ user.get_age_range_display }}
+ {% else %}
+ {% trans "Not specified" %}
+ {% endif %}
+
+
+
{% trans "Location" %}
- {% if user.location %}
- {{ user.location }}
+ {% if user.municipality %}
+ {{ user.municipality.name }} ({{ user.municipality.province.name }})
{% else %}
- {% trans "Add your location" %}
+ {% trans "Not specified" %}
{% endif %}
diff --git a/users/urls.py b/users/urls.py
index d435512..5d84b71 100644
--- a/users/urls.py
+++ b/users/urls.py
@@ -24,4 +24,9 @@
path('admin/dashboard/genres/', views.admin_dashboard_genres, name='admin_dashboard_genres'),
path('admin/dashboard/age-ratings/', views.admin_dashboard_age_ratings, name='admin_dashboard_age_ratings'),
path('admin/dashboard/directors/', views.admin_dashboard_directors, name='admin_dashboard_directors'),
+ path('admin/dashboard/demographics/', views.admin_dashboard_demographics, name='admin_dashboard_demographics'),
+ path('admin/dashboard/demographics/gender/', views.admin_dashboard_views_by_gender, name='admin_dashboard_views_by_gender'),
+ path('admin/dashboard/demographics/age/', views.admin_dashboard_views_by_age, name='admin_dashboard_views_by_age'),
+ path('admin/dashboard/demographics/province/', views.admin_dashboard_views_by_province, name='admin_dashboard_views_by_province'),
+ path('admin/dashboard/demographics/cross-tab/', views.admin_dashboard_cross_tab, name='admin_dashboard_cross_tab'),
]
\ No newline at end of file
diff --git a/users/views.py b/users/views.py
index 849f821..551bfaf 100644
--- a/users/views.py
+++ b/users/views.py
@@ -22,13 +22,17 @@
_ = gettext.gettext
from web_app.forms import CustomUserChangeForm, CustomUserAdminCreationForm
-from web_app.models import CustomUser, Movie, Series, UserProfile, API, Visualitzacio, UserType, RoleAuditLog
+from web_app.models import CustomUser, Movie, Series, UserProfile, API, Visualitzacio, UserType, RoleAuditLog, Province, Municipality
from .services import (
get_content_analytics,
format_analytics_for_csv,
get_genre_distribution,
get_age_rating_distribution,
get_director_top_list,
+ get_views_by_gender,
+ get_views_by_age_range,
+ get_views_by_province,
+ get_views_by_gender_age,
PII_COLUMNS,
validate_gdpr_compliance,
)
@@ -81,7 +85,16 @@ def user_profile(request):
else:
form = CustomUserChangeForm(instance=request.user)
- return render(request, 'users/profile/user_profile.html', {'form': form})
+ current_province_id = None
+ if request.user.municipality:
+ current_province_id = request.user.municipality.province_id
+
+ return render(request, 'users/profile/user_profile.html', {
+ 'form': form,
+ 'provinces': Province.objects.all(),
+ 'current_province_id': current_province_id,
+ 'current_municipality_id': request.user.municipality_id,
+ })
@login_required(login_url='login')
@user_passes_test(is_consumer)
@@ -277,14 +290,17 @@ def crear_usuario_admin(request):
def export_analytics_csv(request):
start_date = request.GET.get('start')
end_date = request.GET.get('end')
- report_type = request.GET.get('type', 'internal')
+ report_type = request.GET.get('type', 'internal')
+
+ if start_date == 'None': start_date = None
+ if end_date == 'None': end_date = None
platform_ids = get_active_platform_ids(request.user)
raw_data = get_content_analytics(
start_date=start_date,
end_date=end_date,
- plataform_id=platform_ids # Ahora pasamos una lista de IDs segura
+ platform_ids=platform_ids
)
is_b2b = (report_type == 'b2b')
@@ -386,6 +402,110 @@ def admin_dashboard_directors(request):
'platforms': API.objects.all()
})
+@login_required(login_url='login')
+@user_passes_test(is_admin_or_staff)
+def admin_dashboard_demographics(request):
+ start_date = request.GET.get('start')
+ end_date = request.GET.get('end')
+ platform_ids = get_active_platform_ids(request.user)
+
+ gender_stats = get_views_by_gender(start_date, end_date, platform_ids)
+ age_stats = get_views_by_age_range(start_date, end_date, platform_ids)
+ province_stats = get_views_by_province(start_date, end_date, platform_ids)
+ cross_stats = get_views_by_gender_age(start_date, end_date, platform_ids)
+
+ gender_table = list(zip(gender_stats['labels'], gender_stats['values']))
+ age_table = list(zip(age_stats['labels'], age_stats['values']))
+ province_table = list(zip(province_stats['labels'], province_stats['values']))
+
+ return render(request, 'users/parts/dashboard_demographics.html', {
+ 'gender_stats': gender_stats,
+ 'age_stats': age_stats,
+ 'province_stats': province_stats,
+ 'cross_stats': cross_stats,
+ 'gender_table': gender_table,
+ 'age_table': age_table,
+ 'province_table': province_table,
+ 'filters': {'start': start_date, 'end': end_date},
+ 'platforms': API.objects.all(),
+ })
+
+@login_required(login_url='login')
+@user_passes_test(is_admin_or_staff)
+def admin_dashboard_views_by_gender(request):
+ start_date = request.GET.get('start')
+ end_date = request.GET.get('end')
+ platform_ids = get_active_platform_ids(request.user)
+
+ gender_stats = get_views_by_gender(start_date, end_date, platform_ids)
+ gender_table = list(zip(gender_stats['labels'], gender_stats['values']))
+
+ return render(request, 'users/parts/dashboard_views_by_gender.html', {
+ 'gender_stats': gender_stats,
+ 'gender_table': gender_table,
+ 'filters': {'start': start_date, 'end': end_date},
+ 'platforms': API.objects.all(),
+ })
+
+@login_required(login_url='login')
+@user_passes_test(is_admin_or_staff)
+def admin_dashboard_views_by_age(request):
+ start_date = request.GET.get('start')
+ end_date = request.GET.get('end')
+ platform_ids = get_active_platform_ids(request.user)
+
+ age_stats = get_views_by_age_range(start_date, end_date, platform_ids)
+ age_table = list(zip(age_stats['labels'], age_stats['values']))
+
+ return render(request, 'users/parts/dashboard_views_by_age.html', {
+ 'age_stats': age_stats,
+ 'age_table': age_table,
+ 'filters': {'start': start_date, 'end': end_date},
+ 'platforms': API.objects.all(),
+ })
+
+@login_required(login_url='login')
+@user_passes_test(is_admin_or_staff)
+def admin_dashboard_views_by_province(request):
+ start_date = request.GET.get('start')
+ end_date = request.GET.get('end')
+ platform_ids = get_active_platform_ids(request.user)
+
+ province_stats = get_views_by_province(start_date, end_date, platform_ids)
+ province_table = list(zip(province_stats['labels'], province_stats['values']))
+
+ return render(request, 'users/parts/dashboard_views_by_province.html', {
+ 'province_stats': province_stats,
+ 'province_table': province_table,
+ 'filters': {'start': start_date, 'end': end_date},
+ 'platforms': API.objects.all(),
+ })
+
+@login_required(login_url='login')
+@user_passes_test(is_admin_or_staff)
+def admin_dashboard_cross_tab(request):
+ start_date = request.GET.get('start')
+ end_date = request.GET.get('end')
+ platform_ids = get_active_platform_ids(request.user)
+
+ cross_stats = get_views_by_gender_age(start_date, end_date, platform_ids)
+
+ cross_table_headers = [d['label'] for d in cross_stats['datasets']]
+ cross_table_rows = []
+ for i, label in enumerate(cross_stats['labels']):
+ row = [label]
+ for dataset in cross_stats['datasets']:
+ row.append(dataset['data'][i] if i < len(dataset['data']) else 0)
+ cross_table_rows.append(row)
+
+ return render(request, 'users/parts/dashboard_cross_tab.html', {
+ 'cross_stats': cross_stats,
+ 'cross_table_headers': cross_table_headers,
+ 'cross_table_rows': cross_table_rows,
+ 'filters': {'start': start_date, 'end': end_date},
+ 'platforms': API.objects.all(),
+ })
+
@user_passes_test(lambda u: u.is_superuser)
def update_user_role(request):
if request.method == 'POST':
diff --git a/web_app/admin.py b/web_app/admin.py
index 9203990..4d6c700 100644
--- a/web_app/admin.py
+++ b/web_app/admin.py
@@ -1,6 +1,6 @@
from django.contrib import admin
from django.utils.safestring import mark_safe
-from .models import SyncLog, API, Contingut, Movie, Series, Director, Genre, AgeRating, CustomUser, UserProfile, Valoracio, Preferits, RoleAuditLog
+from .models import SyncLog, API, Contingut, Movie, Series, Director, Genre, AgeRating, CustomUser, UserProfile, Valoracio, Preferits, RoleAuditLog, Province, Municipality
@admin.register(API)
@@ -136,4 +136,15 @@ class RoleAuditLogAdmin(admin.ModelAdmin):
list_display = ('timestamp', 'admin', 'affected_user', 'old_role', 'new_role')
list_filter = ('timestamp', 'old_role', 'new_role')
search_fields = ('admin__username', 'affected_user__username')
- readonly_fields = ('admin', 'affected_user', 'old_role', 'new_role', 'timestamp')
\ No newline at end of file
+ readonly_fields = ('admin', 'affected_user', 'old_role', 'new_role', 'timestamp')
+
+@admin.register(Province)
+class ProvinceAdmin(admin.ModelAdmin):
+ list_display = ('ine_code', 'name')
+ search_fields = ('name',)
+
+@admin.register(Municipality)
+class MunicipalityAdmin(admin.ModelAdmin):
+ list_display = ('ine_code', 'name', 'province')
+ list_filter = ('province',)
+ search_fields = ('name',)
\ No newline at end of file
diff --git a/web_app/forms.py b/web_app/forms.py
index 5a49b38..a2f4bbb 100644
--- a/web_app/forms.py
+++ b/web_app/forms.py
@@ -4,7 +4,7 @@
from django.core.exceptions import ValidationError
from django.core.validators import MaxLengthValidator
-from .models import CustomUser, API, UserType
+from .models import CustomUser, API, UserType, Gender, AgeRange, Province, Municipality
class CustomUserCreationForm(forms.ModelForm):
"""
@@ -45,9 +45,29 @@ class CustomUserCreationForm(forms.ModelForm):
label=_("Select your platforms")
)
+ gender = forms.ChoiceField(
+ choices=Gender.choices,
+ label=_("Gender"),
+ required=True,
+ )
+
+ age_range = forms.ChoiceField(
+ choices=AgeRange.choices,
+ label=_("Age Range"),
+ required=True,
+ )
+
+ province = forms.ModelChoiceField(
+ queryset=Province.objects.all(),
+ label=_("Province"),
+ required=True,
+ )
+
+
class Meta:
model = CustomUser
- fields = ('username', 'email', 'password', 'password2', 'platforms')
+ fields = ('username', 'email', 'password', 'password2', 'platforms',
+ 'gender', 'age_range', 'province', 'municipality')
def clean_password2(self):
"""
@@ -59,20 +79,72 @@ def clean_password2(self):
if password is not None and password != password2:
raise forms.ValidationError("Las contraseñas no coinciden")
return password2
+
+ def clean(self):
+ cleaned_data = super().clean()
+ province = cleaned_data.get('province')
+ municipality_id = self.data.get('municipality')
+
+ if province and municipality_id:
+ try:
+ municipality = Municipality.objects.get(id=municipality_id, province=province)
+ cleaned_data['municipality'] = municipality
+ except Municipality.DoesNotExist:
+ self.add_error('municipality', _("The selected municipality does not belong to the selected province."))
+ elif province:
+ self.add_error('municipality', _("You must select a municipality."))
+
+ return cleaned_data
+
def save(self, commit=True):
user = super().save(commit=False)
user.set_password(self.cleaned_data['password'])
+ user.gender = self.cleaned_data.get('gender')
+ user.age_range = self.cleaned_data.get('age_range')
+ user.municipality = self.cleaned_data.get('municipality')
+
if commit:
user.save()
return user
class CustomUserChangeForm(UserChangeForm):
+ province = forms.ModelChoiceField(
+ queryset=Province.objects.all(),
+ label=_("Province"),
+ required=False,
+ )
+
class Meta:
model = CustomUser
- fields = ['username', 'first_name', 'last_name', 'email', 'avatar', 'bio', 'location', 'type']
+ fields = [
+ 'username', 'first_name', 'last_name', 'email', 'avatar', 'bio',
+ 'type', 'gender', 'age_range', 'municipality',
+ ]
help_texts = {field: '' for field in fields}
+ def __init__(self, *args, **kwargs):
+ super().__init__(*args, **kwargs)
+ if self.instance and self.instance.municipality:
+ self.fields['province'].initial = self.instance.municipality.province
+ self.fields['province'].widget.attrs['data-current-province'] = self.instance.municipality.province_id
+
+ def clean(self):
+ cleaned_data = super().clean()
+ province = cleaned_data.get('province')
+ municipality_id = self.data.get('municipality')
+
+ if province and municipality_id:
+ try:
+ municipality = Municipality.objects.get(id=municipality_id, province=province)
+ cleaned_data['municipality'] = municipality
+ except Municipality.DoesNotExist:
+ self.add_error('municipality', _("The selected municipality does not belong to the selected province."))
+ elif province:
+ self.add_error('municipality', _("You must select a municipality."))
+
+ return cleaned_data
+
def clean_avatar(self):
"""
Performs custom validation on the uploaded avatar file to strictly enforce
diff --git a/web_app/management/__init__.py b/web_app/management/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/web_app/management/commands/__init__.py b/web_app/management/commands/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/web_app/management/commands/load_municipalities.py b/web_app/management/commands/load_municipalities.py
new file mode 100644
index 0000000..eefe039
--- /dev/null
+++ b/web_app/management/commands/load_municipalities.py
@@ -0,0 +1,59 @@
+import json
+from pathlib import Path
+
+from django.core.management.base import BaseCommand
+
+from web_app.models import Province, Municipality
+
+
+class Command(BaseCommand):
+ help = "Loads provinces and municipalities from a JSON file into the database."
+
+ def add_arguments(self, parser):
+ parser.add_argument(
+ '--source',
+ type=str,
+ default=None,
+ help='Path to the JSON data file. Defaults to data/municipalities_spain.json',
+ )
+
+ def handle(self, *args, **options):
+ source = options.get('source')
+ if not source:
+ source = Path(__file__).resolve().parents[3] / 'data' / 'municipalities_spain.json'
+
+ source_path = Path(source)
+
+ if not source_path.exists():
+ self.stderr.write(self.style.ERROR(f"File not found: {source_path}"))
+ return
+
+ with open(source_path, encoding='utf-8') as f:
+ data = json.load(f)
+
+ provinces_created = 0
+ municipalities_created = 0
+
+ for province_data in data:
+ province, created = Province.objects.update_or_create(
+ ine_code=province_data['ine_code'],
+ defaults={'name': province_data['name']},
+ )
+ if created:
+ provinces_created += 1
+ self.stdout.write(f" Created province: {province.name}")
+
+ for municipality_data in province_data.get('municipalities', []):
+ municipality, created = Municipality.objects.update_or_create(
+ ine_code=municipality_data['ine_code'],
+ defaults={
+ 'name': municipality_data['name'],
+ 'province': province,
+ },
+ )
+ if created:
+ municipalities_created += 1
+
+ self.stdout.write(self.style.SUCCESS(
+ f"Done. {provinces_created} provinces and {municipalities_created} municipalities loaded."
+ ))
diff --git a/web_app/migrations/0004_municipality_province_remove_customuser_location_and_more.py b/web_app/migrations/0004_municipality_province_remove_customuser_location_and_more.py
new file mode 100644
index 0000000..52b41c2
--- /dev/null
+++ b/web_app/migrations/0004_municipality_province_remove_customuser_location_and_more.py
@@ -0,0 +1,64 @@
+# Generated by Django 6.0.3 on 2026-06-09 09:06
+
+import django.db.models.deletion
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('web_app', '0003_roleauditlog'),
+ ]
+
+ operations = [
+ migrations.CreateModel(
+ name='Municipality',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('ine_code', models.CharField(max_length=5, unique=True)),
+ ('name', models.CharField(max_length=255)),
+ ],
+ options={
+ 'verbose_name': 'Municipio',
+ 'verbose_name_plural': 'Municipios',
+ 'ordering': ['name'],
+ },
+ ),
+ migrations.CreateModel(
+ name='Province',
+ fields=[
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
+ ('ine_code', models.CharField(max_length=2, unique=True)),
+ ('name', models.CharField(max_length=255)),
+ ],
+ options={
+ 'verbose_name': 'Provincia',
+ 'verbose_name_plural': 'Provincias',
+ 'ordering': ['name'],
+ },
+ ),
+ migrations.RemoveField(
+ model_name='customuser',
+ name='location',
+ ),
+ migrations.AddField(
+ model_name='customuser',
+ name='age_range',
+ field=models.CharField(blank=True, choices=[('<18', 'Under 18'), ('18-30', '18-30'), ('31-50', '31-50'), ('>50', 'Over 50')], max_length=10, null=True),
+ ),
+ migrations.AddField(
+ model_name='customuser',
+ name='gender',
+ field=models.CharField(blank=True, choices=[('Male', 'Male'), ('Female', 'Female'), ('Non-binary', 'Non-binary'), ('Other', 'Other')], max_length=50, null=True),
+ ),
+ migrations.AddField(
+ model_name='customuser',
+ name='municipality',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='users', to='web_app.municipality'),
+ ),
+ migrations.AddField(
+ model_name='municipality',
+ name='province',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='municipalities', to='web_app.province'),
+ ),
+ ]
diff --git a/web_app/models.py b/web_app/models.py
index caa3cdb..83776c3 100644
--- a/web_app/models.py
+++ b/web_app/models.py
@@ -9,6 +9,44 @@ class UserType(models.TextChoices):
ADMIN = 'Admin', 'Admin'
CONSUMER = 'Consumer', 'Consumer'
+
+class Gender(models.TextChoices):
+ MALE = 'Male', 'Male'
+ FEMALE = 'Female', 'Female'
+ NON_BINARY = 'Non-binary', 'Non-binary'
+ OTHER = 'Other', 'Other'
+
+class AgeRange(models.TextChoices):
+ UNDER_18 = '<18', 'Under 18'
+ BETWEEN_18_30 = '18-30', '18-30'
+ BETWEEN_31_50 = '31-50', '31-50'
+ OVER_50 = '>50', 'Over 50'
+
+class Province(models.Model):
+ ine_code = models.CharField(max_length=2, unique=True)
+ name = models.CharField(max_length=255)
+
+ class Meta:
+ verbose_name = 'Provincia'
+ verbose_name_plural = 'Provincias'
+ ordering = ['name']
+ def __str__(self):
+ return self.name
+
+class Municipality(models.Model):
+ ine_code = models.CharField(max_length=5, unique=True)
+ name = models.CharField(max_length=255)
+ province = models.ForeignKey(Province, on_delete=models.CASCADE, related_name='municipalities')
+
+ class Meta:
+ verbose_name = 'Municipio'
+ verbose_name_plural = 'Municipios'
+ ordering = ['name']
+
+ def __str__(self):
+ return self.name
+
+
class API(models.Model):
port = models.IntegerField(unique=True)
name = models.CharField(max_length=100, blank=True, default='')
@@ -203,11 +241,29 @@ class CustomUser(AbstractUser):
"""
avatar = models.ImageField(upload_to='avatars/', null=True, blank=True)
bio = models.TextField(blank=True, null=True)
- location = models.CharField(max_length=255, blank=True, null=True)
type = models.CharField(
choices=UserType.choices,
default=UserType.CONSUMER,
)
+ gender = models.CharField(
+ choices=Gender.choices,
+ max_length=50,
+ blank=True,
+ null=True
+ )
+ age_range = models.CharField(
+ choices=AgeRange.choices,
+ max_length=10,
+ blank=True,
+ null=True
+ )
+ municipality = models.ForeignKey(
+ Municipality,
+ on_delete=models.SET_NULL,
+ blank=True,
+ null=True,
+ related_name='users'
+ )
subscriptions = models.ManyToManyField('API', blank=True, related_name='subscribed_users')
def __str__(self):
diff --git a/web_app/urls.py b/web_app/urls.py
index 193488c..2fe6d87 100644
--- a/web_app/urls.py
+++ b/web_app/urls.py
@@ -27,6 +27,8 @@
path('terms-use/', views.terms_use, name='terms_use'),
path('privacy-policy/', views.privacy_policy, name='privacy_policy'),
+ path('api/municipalities//', views.municipalities_by_province, name='municipalities_api'),
+
#path('user/profile', views.profile, name='profile'),
#path('user/history', views.history, name='history'),
diff --git a/web_app/views.py b/web_app/views.py
index b4a32b8..9363e8e 100644
--- a/web_app/views.py
+++ b/web_app/views.py
@@ -3,8 +3,14 @@
from django.utils.crypto import get_random_string
from django.shortcuts import render, redirect, get_object_or_404
from web_app.forms import CustomUserAdminCreationForm, CustomUserCreationForm
-from web_app.models import AgeRating, API, CustomUser, Director, Genre, Movie, UserProfile, Series, Contingut, \
- AgeRating, Valoracio, Visualitzacio, SyncLog
+from web_app.models import (
+ API, Director, Genre, AgeRating, Contingut,
+ Movie, Series, CustomUser, UserProfile,
+ Preferits, Valoracio, Visualitzacio, SyncLog,
+ RoleAuditLog,
+ Province, Municipality,
+)
+
from web_app import utils
import json
from itertools import chain
@@ -89,7 +95,7 @@ def register_view(request):
user.subscriptions.set(platforms)
login(request, user)
return redirect('home')
- return render(request, 'identify/register.html', {'form': form})
+ return render(request, 'identify/register.html', {'form': form, 'provinces': Province.objects.all()})
@login_required(login_url='login')
def redirect_by_role(request):
@@ -252,7 +258,7 @@ def register_platform_click(request):
# MAGIA DE DJANGO: Si existe, actualiza la fecha. Si no, lo crea.
vis, created = Visualitzacio.objects.update_or_create(
user=request.user,
- contingut_id=contingut_id,
+ contingut_id=contingut_id,
api_id=api_id,
defaults={'data_visualitzacio': timezone.now()}
)
@@ -266,6 +272,9 @@ def register_platform_click(request):
return JsonResponse({'status': 'error', 'message': 'Método no permitido'}, status=405)
+def municipalities_by_province(request, province_id):
+ municipalities = Municipality.objects.filter(province_id=province_id).values('id', 'name')
+ return JsonResponse(list(municipalities), safe=False)
@user_passes_test(lambda u: u.is_superuser)
def system_logs(request):
@@ -278,4 +287,4 @@ def system_logs(request):
return render(request, 'users/parts/system_logs.html', {
'logs': page_obj
- })
\ No newline at end of file
+ })