import
This commit is contained in:
@@ -243,10 +243,16 @@ class IndustryAdmin(admin.ModelAdmin):
|
||||
search_fields = ['name', 'slug', 'description']
|
||||
readonly_fields = ['created_at', 'updated_at']
|
||||
inlines = [IndustrySectorInline]
|
||||
actions = ['delete_selected'] # Enable bulk delete
|
||||
change_list_template = 'admin/igny8_core_auth/industry/change_list.html'
|
||||
|
||||
def get_sectors_count(self, obj):
|
||||
return obj.sectors.filter(is_active=True).count()
|
||||
get_sectors_count.short_description = 'Active Sectors'
|
||||
|
||||
def has_delete_permission(self, request, obj=None):
|
||||
"""Allow deletion for superusers and developers"""
|
||||
return request.user.is_superuser or (hasattr(request.user, 'is_developer') and request.user.is_developer())
|
||||
|
||||
|
||||
@admin.register(IndustrySector)
|
||||
@@ -255,6 +261,12 @@ class IndustrySectorAdmin(admin.ModelAdmin):
|
||||
list_filter = ['is_active', 'industry']
|
||||
search_fields = ['name', 'slug', 'description']
|
||||
readonly_fields = ['created_at', 'updated_at']
|
||||
actions = ['delete_selected'] # Enable bulk delete
|
||||
change_list_template = 'admin/igny8_core_auth/industrysector/change_list.html'
|
||||
|
||||
def has_delete_permission(self, request, obj=None):
|
||||
"""Allow deletion for superusers and developers"""
|
||||
return request.user.is_superuser or (hasattr(request.user, 'is_developer') and request.user.is_developer())
|
||||
|
||||
|
||||
@admin.register(SeedKeyword)
|
||||
@@ -264,6 +276,8 @@ class SeedKeywordAdmin(admin.ModelAdmin):
|
||||
list_filter = ['is_active', 'industry', 'sector', 'intent']
|
||||
search_fields = ['keyword']
|
||||
readonly_fields = ['created_at', 'updated_at']
|
||||
actions = ['delete_selected'] # Enable bulk delete
|
||||
change_list_template = 'admin/igny8_core_auth/seedkeyword/change_list.html'
|
||||
|
||||
fieldsets = (
|
||||
('Keyword Info', {
|
||||
@@ -276,6 +290,10 @@ class SeedKeywordAdmin(admin.ModelAdmin):
|
||||
'fields': ('created_at', 'updated_at')
|
||||
}),
|
||||
)
|
||||
|
||||
def has_delete_permission(self, request, obj=None):
|
||||
"""Allow deletion for superusers and developers"""
|
||||
return request.user.is_superuser or (hasattr(request.user, 'is_developer') and request.user.is_developer())
|
||||
|
||||
|
||||
@admin.register(User)
|
||||
|
||||
@@ -379,7 +379,7 @@ class SeedKeyword(models.Model):
|
||||
db_table = 'igny8_seed_keywords'
|
||||
unique_together = [['keyword', 'industry', 'sector']]
|
||||
verbose_name = 'Seed Keyword'
|
||||
verbose_name_plural = 'Seed Keywords'
|
||||
verbose_name_plural = 'Global Keywords Database'
|
||||
indexes = [
|
||||
models.Index(fields=['keyword']),
|
||||
models.Index(fields=['industry', 'sector']),
|
||||
|
||||
@@ -1316,3 +1316,219 @@ class AuthViewSet(viewsets.GenericViewSet):
|
||||
message='Password has been reset successfully',
|
||||
request=request
|
||||
)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# CSV Import/Export Views for Admin
|
||||
# ============================================================================
|
||||
|
||||
from django.http import HttpResponse, JsonResponse
|
||||
from django.contrib.admin.views.decorators import staff_member_required
|
||||
from django.views.decorators.http import require_http_methods
|
||||
import csv
|
||||
import io
|
||||
|
||||
|
||||
@staff_member_required
|
||||
@require_http_methods(["GET"])
|
||||
def industry_csv_template(request):
|
||||
"""Download CSV template for Industry import"""
|
||||
response = HttpResponse(content_type='text/csv')
|
||||
response['Content-Disposition'] = 'attachment; filename="industry_template.csv"'
|
||||
|
||||
writer = csv.writer(response)
|
||||
writer.writerow(['name', 'description', 'is_active'])
|
||||
writer.writerow(['Technology', 'Technology industry', 'true'])
|
||||
writer.writerow(['Healthcare', 'Healthcare and medical services', 'true'])
|
||||
|
||||
return response
|
||||
|
||||
|
||||
@staff_member_required
|
||||
@require_http_methods(["POST"])
|
||||
def industry_csv_import(request):
|
||||
"""Import industries from CSV"""
|
||||
if not request.FILES.get('csv_file'):
|
||||
return JsonResponse({'success': False, 'error': 'No CSV file provided'}, status=400)
|
||||
|
||||
csv_file = request.FILES['csv_file']
|
||||
decoded_file = csv_file.read().decode('utf-8')
|
||||
io_string = io.StringIO(decoded_file)
|
||||
reader = csv.DictReader(io_string)
|
||||
|
||||
created = 0
|
||||
updated = 0
|
||||
errors = []
|
||||
|
||||
from django.utils.text import slugify
|
||||
|
||||
for row_num, row in enumerate(reader, start=2):
|
||||
try:
|
||||
is_active = row.get('is_active', 'true').lower() in ['true', '1', 'yes']
|
||||
slug = slugify(row['name'])
|
||||
|
||||
industry, created_flag = Industry.objects.update_or_create(
|
||||
name=row['name'],
|
||||
defaults={
|
||||
'slug': slug,
|
||||
'description': row.get('description', ''),
|
||||
'is_active': is_active
|
||||
}
|
||||
)
|
||||
if created_flag:
|
||||
created += 1
|
||||
else:
|
||||
updated += 1
|
||||
except Exception as e:
|
||||
errors.append(f"Row {row_num}: {str(e)}")
|
||||
|
||||
return JsonResponse({
|
||||
'success': True,
|
||||
'created': created,
|
||||
'updated': updated,
|
||||
'errors': errors
|
||||
})
|
||||
|
||||
|
||||
@staff_member_required
|
||||
@require_http_methods(["GET"])
|
||||
def industrysector_csv_template(request):
|
||||
"""Download CSV template for IndustrySector import"""
|
||||
response = HttpResponse(content_type='text/csv')
|
||||
response['Content-Disposition'] = 'attachment; filename="industrysector_template.csv"'
|
||||
|
||||
writer = csv.writer(response)
|
||||
writer.writerow(['name', 'industry', 'description', 'is_active'])
|
||||
writer.writerow(['Software Development', 'Technology', 'Software and app development', 'true'])
|
||||
writer.writerow(['Healthcare IT', 'Healthcare', 'Healthcare information technology', 'true'])
|
||||
|
||||
return response
|
||||
|
||||
|
||||
@staff_member_required
|
||||
@require_http_methods(["POST"])
|
||||
def industrysector_csv_import(request):
|
||||
"""Import industry sectors from CSV"""
|
||||
if not request.FILES.get('csv_file'):
|
||||
return JsonResponse({'success': False, 'error': 'No CSV file provided'}, status=400)
|
||||
|
||||
csv_file = request.FILES['csv_file']
|
||||
decoded_file = csv_file.read().decode('utf-8')
|
||||
io_string = io.StringIO(decoded_file)
|
||||
reader = csv.DictReader(io_string)
|
||||
|
||||
created = 0
|
||||
updated = 0
|
||||
errors = []
|
||||
|
||||
from django.utils.text import slugify
|
||||
|
||||
for row_num, row in enumerate(reader, start=2):
|
||||
try:
|
||||
is_active = row.get('is_active', 'true').lower() in ['true', '1', 'yes']
|
||||
slug = slugify(row['name'])
|
||||
|
||||
# Find industry by name
|
||||
try:
|
||||
industry = Industry.objects.get(name=row['industry'])
|
||||
except Industry.DoesNotExist:
|
||||
errors.append(f"Row {row_num}: Industry '{row['industry']}' not found")
|
||||
continue
|
||||
|
||||
sector, created_flag = IndustrySector.objects.update_or_create(
|
||||
name=row['name'],
|
||||
industry=industry,
|
||||
defaults={
|
||||
'slug': slug,
|
||||
'description': row.get('description', ''),
|
||||
'is_active': is_active
|
||||
}
|
||||
)
|
||||
if created_flag:
|
||||
created += 1
|
||||
else:
|
||||
updated += 1
|
||||
except Exception as e:
|
||||
errors.append(f"Row {row_num}: {str(e)}")
|
||||
|
||||
return JsonResponse({
|
||||
'success': True,
|
||||
'created': created,
|
||||
'updated': updated,
|
||||
'errors': errors
|
||||
})
|
||||
|
||||
|
||||
@staff_member_required
|
||||
@require_http_methods(["GET"])
|
||||
def seedkeyword_csv_template(request):
|
||||
"""Download CSV template for SeedKeyword import"""
|
||||
response = HttpResponse(content_type='text/csv')
|
||||
response['Content-Disposition'] = 'attachment; filename="seedkeyword_template.csv"'
|
||||
|
||||
writer = csv.writer(response)
|
||||
writer.writerow(['keyword', 'industry', 'sector', 'volume', 'difficulty', 'intent', 'is_active'])
|
||||
writer.writerow(['python programming', 'Technology', 'Software Development', '10000', '45', 'Informational', 'true'])
|
||||
writer.writerow(['medical software', 'Healthcare', 'Healthcare IT', '5000', '60', 'Commercial', 'true'])
|
||||
|
||||
return response
|
||||
|
||||
|
||||
@staff_member_required
|
||||
@require_http_methods(["POST"])
|
||||
def seedkeyword_csv_import(request):
|
||||
"""Import seed keywords from CSV"""
|
||||
if not request.FILES.get('csv_file'):
|
||||
return JsonResponse({'success': False, 'error': 'No CSV file provided'}, status=400)
|
||||
|
||||
csv_file = request.FILES['csv_file']
|
||||
decoded_file = csv_file.read().decode('utf-8')
|
||||
io_string = io.StringIO(decoded_file)
|
||||
reader = csv.DictReader(io_string)
|
||||
|
||||
created = 0
|
||||
updated = 0
|
||||
errors = []
|
||||
|
||||
for row_num, row in enumerate(reader, start=2):
|
||||
try:
|
||||
is_active = row.get('is_active', 'true').lower() in ['true', '1', 'yes']
|
||||
|
||||
# Find industry and sector by name
|
||||
try:
|
||||
industry = Industry.objects.get(name=row['industry'])
|
||||
except Industry.DoesNotExist:
|
||||
errors.append(f"Row {row_num}: Industry '{row['industry']}' not found")
|
||||
continue
|
||||
|
||||
try:
|
||||
sector = IndustrySector.objects.get(name=row['sector'], industry=industry)
|
||||
except IndustrySector.DoesNotExist:
|
||||
errors.append(f"Row {row_num}: Sector '{row['sector']}' not found in industry '{row['industry']}'")
|
||||
continue
|
||||
|
||||
keyword, created_flag = SeedKeyword.objects.update_or_create(
|
||||
keyword=row['keyword'],
|
||||
industry=industry,
|
||||
sector=sector,
|
||||
defaults={
|
||||
'volume': int(row.get('volume', 0)),
|
||||
'difficulty': int(row.get('difficulty', 0)),
|
||||
'intent': row.get('intent', 'Informational'),
|
||||
'is_active': is_active
|
||||
}
|
||||
)
|
||||
if created_flag:
|
||||
created += 1
|
||||
else:
|
||||
updated += 1
|
||||
except Exception as e:
|
||||
errors.append(f"Row {row_num}: {str(e)}")
|
||||
|
||||
return JsonResponse({
|
||||
'success': True,
|
||||
'created': created,
|
||||
'updated': updated,
|
||||
'errors': errors
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user