summaryrefslogtreecommitdiff
path: root/ishtar_common
diff options
context:
space:
mode:
authorÉtienne Loks <etienne.loks@iggdrasil.net>2023-01-10 13:08:48 +0100
committerÉtienne Loks <etienne.loks@iggdrasil.net>2023-01-12 09:50:56 +0100
commit061edbaac5a37b8e717391c3b7d804a96c041979 (patch)
tree79a9647f9e8563d44d08123dd4844a249001e63a /ishtar_common
parentac839d9481b863c93a9c0f92011118bb581f7c2d (diff)
downloadIshtar-061edbaac5a37b8e717391c3b7d804a96c041979.tar.bz2
Ishtar-061edbaac5a37b8e717391c3b7d804a96c041979.zip
Syndication - export external sources
Diffstat (limited to 'ishtar_common')
-rw-r--r--ishtar_common/admin.py41
-rw-r--r--ishtar_common/migrations/0222_auto_20230111_1857.py94
-rw-r--r--ishtar_common/models_common.py16
-rw-r--r--ishtar_common/models_imports.py18
-rw-r--r--ishtar_common/models_rest.py61
-rw-r--r--ishtar_common/rest.py84
-rw-r--r--ishtar_common/static/js/ishtar.js10
-rw-r--r--ishtar_common/templates/blocks/DataTables-content.html21
-rw-r--r--ishtar_common/templates/blocks/DataTables-external-sources.html2
-rw-r--r--ishtar_common/templates/blocks/DataTables-stats.html2
-rw-r--r--ishtar_common/templates/blocks/DataTables-tabs.html4
-rw-r--r--ishtar_common/templates/blocks/DataTables.html84
-rw-r--r--ishtar_common/urls.py21
-rw-r--r--ishtar_common/utils.py48
-rw-r--r--ishtar_common/views.py69
-rw-r--r--ishtar_common/views_item.py51
-rw-r--r--ishtar_common/widgets.py20
-rw-r--r--ishtar_common/wizards.py24
18 files changed, 529 insertions, 141 deletions
diff --git a/ishtar_common/admin.py b/ishtar_common/admin.py
index 4da0a15ad..f0f795665 100644
--- a/ishtar_common/admin.py
+++ b/ishtar_common/admin.py
@@ -2257,13 +2257,25 @@ def get_api_choices():
class ApiSearchModelAdminForm(forms.ModelForm):
class Meta:
model = models_rest.ApiUser
- exclude = []
+ exclude = ["table_format"]
content_type = forms.ModelChoiceField(
label=_("Content type"), queryset=ContentType.objects,
limit_choices_to=get_api_choices
)
+ def __init__(self, *args, **kwargs):
+ super(ApiSearchModelAdminForm, self).__init__(*args, **kwargs)
+ if self.instance.id:
+ model_name = self.instance.content_type.model.replace("_", "")
+ model_name = f"{self.instance.content_type.app_label}.models."\
+ f"{model_name}"
+ q = models.ImporterType.objects.filter(
+ associated_models__klass__iexact=model_name
+ )
+ # self.fields['table_format'].queryset = q
+ self.fields['export'].queryset = q
+
class ApiSearchModelAdmin(admin.ModelAdmin):
form = ApiSearchModelAdminForm
@@ -2300,7 +2312,8 @@ def update_types_from_source(modeladmin, request, queryset):
source = queryset.all()[0]
created, updated, deleted = 0, 0, 0
missing_models, missing_types = [], []
- for item_type in ("operation",):
+ config = {}
+ for item_type in ("operation", "contextrecord", "file", "find", "warehouse"):
curl = source.url
if not curl.endswith("/"):
curl += "/"
@@ -2335,6 +2348,17 @@ def update_types_from_source(modeladmin, request, queryset):
str(_("Response of {} is not a valid JSON message.")).format(curl),
)
continue
+
+ if "config" in content:
+ for k in content["config"]:
+ if not content["config"][k]:
+ continue
+ if k not in config:
+ config[k] = ""
+ elif config[k]:
+ config[k] += "||"
+ config[k] += content["config"][k]
+
result = source.update_matches(content)
if result.get("created", None):
created += result['created']
@@ -2346,6 +2370,18 @@ def update_types_from_source(modeladmin, request, queryset):
missing_models += result["search_model do not exist"]
if result.get("type do not exist", None):
missing_types += result["type do not exist"]
+ modified = False
+ for k in config:
+ if getattr(source, k) != config[k]:
+ modified = True
+ setattr(source, k, config[k])
+ if modified:
+ source.save()
+ messages.add_message(
+ request,
+ messages.INFO,
+ str(_("Table/exports configuration updated")),
+ )
if created:
messages.add_message(
request,
@@ -2485,6 +2521,7 @@ class ApiExternalSourceAdmin(admin.ModelAdmin):
generate_match_document,
update_association_from_match_document,
]
+ exclude = ("search_columns", "search_columns_label", "exports", "exports_label")
autocomplete_fields = ["users"]
list_display = ("name", "url", "key")
diff --git a/ishtar_common/migrations/0222_auto_20230111_1857.py b/ishtar_common/migrations/0222_auto_20230111_1857.py
new file mode 100644
index 000000000..e03a4ccf0
--- /dev/null
+++ b/ishtar_common/migrations/0222_auto_20230111_1857.py
@@ -0,0 +1,94 @@
+# Generated by Django 2.2.24 on 2023-01-11 18:57
+
+from django.conf import settings
+from django.db import migrations, models
+import django.db.models.deletion
+
+
+class Migration(migrations.Migration):
+
+ dependencies = [
+ ('ishtar_common', '0221_auto_20220823_1935'),
+ ]
+
+ operations = [
+ migrations.AlterModelOptions(
+ name='spatialreferencesystem',
+ options={'ordering': ('order', 'label'), 'verbose_name': 'Geographic - Spatial reference system', 'verbose_name_plural': 'Geographic - Spatial reference systems'},
+ ),
+ migrations.AddField(
+ model_name='apiexternalsource',
+ name='exports',
+ field=models.TextField(default='', verbose_name='Exports slug'),
+ ),
+ migrations.AddField(
+ model_name='apiexternalsource',
+ name='exports_label',
+ field=models.TextField(default='', verbose_name='Exports label'),
+ ),
+ migrations.AddField(
+ model_name='apiexternalsource',
+ name='search_columns',
+ field=models.TextField(default='', verbose_name='Search columns'),
+ ),
+ migrations.AddField(
+ model_name='apiexternalsource',
+ name='search_columns_label',
+ field=models.TextField(default='', verbose_name='Search columns label'),
+ ),
+ migrations.AddField(
+ model_name='apisearchmodel',
+ name='export',
+ field=models.ManyToManyField(blank=True, related_name='search_model_exports', to='ishtar_common.ImporterType', verbose_name='Export'),
+ ),
+ migrations.AddField(
+ model_name='apisearchmodel',
+ name='table_format',
+ field=models.ForeignKey(help_text='Not used. Set it when table columns will be set by importer.', null=True, on_delete=django.db.models.deletion.PROTECT, related_name='search_model_table_format', to='ishtar_common.ImporterType', verbose_name='Table formats'),
+ ),
+ migrations.AlterField(
+ model_name='apiexternalsource',
+ name='users',
+ field=models.ManyToManyField(blank=True, to='ishtar_common.IshtarUser', verbose_name='Users'),
+ ),
+ migrations.AlterField(
+ model_name='apisearchmodel',
+ name='user',
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='ishtar_common.ApiUser', verbose_name='User'),
+ ),
+ migrations.AlterField(
+ model_name='apiuser',
+ name='user_ptr',
+ field=models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, primary_key=True, related_name='apiuser', serialize=False, to=settings.AUTH_USER_MODEL, verbose_name='User'),
+ ),
+ migrations.AlterField(
+ model_name='import',
+ name='skip_lines',
+ field=models.IntegerField(default=1, help_text='Number of header lines in your file (can be 0 and should be 0 for geopackage or Shapefile).', verbose_name='Skip lines'),
+ ),
+ migrations.AlterField(
+ model_name='import',
+ name='state',
+ field=models.CharField(choices=[('C', 'Created'), ('AP', 'Analyse in progress'), ('A', 'Analysed'), ('HQ', 'Check modified in queue'), ('IQ', 'Import in queue'), ('HP', 'Check modified in progress'), ('IP', 'Import in progress'), ('PP', 'Post-processing in progress'), ('PI', 'Partially imported'), ('FE', 'Finished with errors'), ('F', 'Finished'), ('AC', 'Archived')], default='C', max_length=2, verbose_name='State'),
+ ),
+ migrations.AlterField(
+ model_name='importertype',
+ name='unicity_keys',
+ field=models.CharField(blank=True, help_text='Mandatory for update importer. Set to key that identify items without ambiguity. Warning: __ is not supported, only use level 1 key.', max_length=500, null=True, verbose_name='Unicity keys (separator ";")'),
+ ),
+ migrations.AlterField(
+ model_name='town',
+ name='geodata',
+ field=models.ManyToManyField(blank=True, related_name='related_items_ishtar_common_town', to='ishtar_common.GeoVectorData', verbose_name='Geodata'),
+ ),
+ migrations.AlterField(
+ model_name='town',
+ name='main_geodata',
+ field=models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='main_related_items_ishtar_common_town', to='ishtar_common.GeoVectorData', verbose_name='Main geodata'),
+ ),
+ migrations.AlterField(
+ model_name='valueformater',
+ name='format_string',
+ field=models.CharField(help_text='A string used to format a value using the Python "format()" method. The site https://pyformat.info/ provide good examples of usage. Only one "{}" entry is managed. Instead you can use "{item}". The input is assumed to be a string.', max_length=100, verbose_name='Format string'),
+ ),
+ ]
diff --git a/ishtar_common/models_common.py b/ishtar_common/models_common.py
index 5e6e3af8f..151ab5650 100644
--- a/ishtar_common/models_common.py
+++ b/ishtar_common/models_common.py
@@ -37,7 +37,7 @@ from django.core.serializers import serialize
from django.urls import reverse, NoReverseMatch
from django.core.validators import validate_slug
from django.db import connection, transaction, OperationalError, IntegrityError
-from django.db.models import Q, Count, Max
+from django.db.models import Q, Count, Max, fields
from django.db.models.signals import post_save, post_delete, m2m_changed
from django.template import loader
from django.template.defaultfilters import slugify
@@ -47,8 +47,9 @@ from ishtar_common.utils import (
ugettext_lazy as _,
pgettext_lazy,
get_image_path,
+ get_columns_from_class,
human_date,
- reverse_list_coordinates
+ reverse_list_coordinates,
)
from simple_history.models import HistoricalRecords as BaseHistoricalRecords
from simple_history.signals import (
@@ -4249,6 +4250,17 @@ class MainItem(ShortMenuItem, SerializeItem):
def class_verbose_name(cls):
return cls._meta.verbose_name
+ @classmethod
+ def get_columns(cls, table_cols_attr="TABLE_COLS", dict_col_labels=True):
+ """
+ :param table_cols_attr: "TABLE_COLS" if not specified
+ :param dict_col_labels: (default: True) if set to False return list matching
+ with table_cols list
+ :return: (table_cols, table_col_labels)
+ """
+ return get_columns_from_class(cls, table_cols_attr=table_cols_attr,
+ dict_col_labels=dict_col_labels)
+
def get_search_url(self):
if self.SLUG:
return reverse(self.SLUG + "_search")
diff --git a/ishtar_common/models_imports.py b/ishtar_common/models_imports.py
index 6358f070a..d4f8b5698 100644
--- a/ishtar_common/models_imports.py
+++ b/ishtar_common/models_imports.py
@@ -353,6 +353,24 @@ class ImporterType(models.Model):
newclass = type(name, (Importer,), args)
return newclass
+ def get_columns(self, importer_class=None):
+ """
+ :param importer_class: importer class - if not provided get from self
+ :return: (columns: list, columns_names: list) - column attributes, column labels
+ """
+ if not importer_class:
+ importer_class = self.get_importer_class()
+ cols, col_names = [], []
+ for formater in importer_class.LINE_EXPORT_FORMAT:
+ if not formater:
+ cols.append("")
+ col_names.append("")
+ continue
+ cols.append(formater.export_field_name)
+ col_names.append(formater.label)
+ return cols, col_names
+
+
def save(self, *args, **kwargs):
if not self.slug:
self.slug = create_slug(ImporterType, self.name)
diff --git a/ishtar_common/models_rest.py b/ishtar_common/models_rest.py
index e0a0ba97e..7a6c79a54 100644
--- a/ishtar_common/models_rest.py
+++ b/ishtar_common/models_rest.py
@@ -65,6 +65,14 @@ class ApiSearchModel(models.Model):
null=True,
help_text=_("Search query add to each request"),
)
+ table_format = models.ForeignKey(
+ "ishtar_common.ImporterType", on_delete=models.PROTECT, null=True,
+ verbose_name=_("Table formats"), related_name=_("search_model_table_format"),
+ help_text=_("Not used. Set it when table columns will be set by importer.")
+ )
+ export = models.ManyToManyField(
+ "ishtar_common.ImporterType", blank=True, verbose_name=_("Export"),
+ related_name=_("search_model_exports"))
class Meta:
verbose_name = _("API - Remote access - Search model")
@@ -96,6 +104,10 @@ class ApiExternalSource(models.Model):
url = models.URLField(verbose_name=_("URL"))
name = models.CharField(verbose_name=_("Name"), max_length=200)
key = models.CharField(_("Key"), max_length=40)
+ search_columns = models.TextField(_("Search columns"), default="")
+ search_columns_label = models.TextField(_("Search columns label"), default="")
+ exports = models.TextField(_("Exports slug"), default="")
+ exports_label = models.TextField(_("Exports label"), default="")
users = models.ManyToManyField("IshtarUser", blank=True, verbose_name=_("Users"))
match_document = models.FileField(
_("Match document"),
@@ -117,6 +129,53 @@ class ApiExternalSource(models.Model):
def __str__(self):
return self.name
+ def get_columns(self, model_name):
+ """
+ Column keys for table display
+ :return: (key1:str, key2:str, ...) - list of column key
+ """
+ if not self.search_columns:
+ return []
+ model_name += "-"
+ return [
+ k[len(model_name):]
+ for k in self.search_columns.split("||") if k.startswith(model_name)
+ ]
+
+ def get_column_labels(self, model_name):
+ """
+ Column label for table display
+ :return: (label1:str, label2:str, ...) - list of column labels
+ """
+ if not self.search_columns_label:
+ return []
+ model_name += "-"
+ return [
+ k[len(model_name):]
+ for k in self.search_columns_label.split("||") if k.startswith(model_name)
+ ]
+
+ def get_exports(self, model_name):
+ """
+ Get export list
+ :return: [(slug:slug, label:str)] - list of export slug and labels
+ """
+ if not self.exports or not self.exports_label:
+ return []
+ model_name += "-"
+ exports = [
+ k[len(model_name):]
+ for k in self.exports.split("||") if k.startswith(model_name)
+ ]
+ exports_label = [
+ k[len(model_name):]
+ for k in self.exports_label.split("||") if k.startswith(model_name)
+ ]
+ result = []
+ for idx in range(min([len(exports), len(exports_label)])):
+ result.append((exports[idx], exports_label[idx]))
+ return result
+
def save(self, force_insert=False, force_update=False, using=None,
update_fields=None):
super().save(force_insert, force_update, using, update_fields)
@@ -137,6 +196,8 @@ class ApiExternalSource(models.Model):
"type do not exist": [],
}
for search_model in content:
+ if search_model == "config":
+ continue
app, model_name = search_model.split(".")
try:
ct = ContentType.objects.get(app_label=app, model=model_name)
diff --git a/ishtar_common/rest.py b/ishtar_common/rest.py
index f85061b69..e489e5f80 100644
--- a/ishtar_common/rest.py
+++ b/ishtar_common/rest.py
@@ -1,7 +1,10 @@
-import json
+import datetime
+import requests
from django.conf import settings
from django.db.models import Q
+from django.http import HttpResponse
+from django.shortcuts import reverse
from django.utils.translation import activate, deactivate
from rest_framework import authentication, permissions, generics
@@ -10,8 +13,8 @@ from rest_framework.views import APIView
from ishtar_common import models_rest
from ishtar_common.models_common import GeneralType
+from ishtar_common.models_imports import Importer
from ishtar_common.views_item import get_item
-from ishtar_common.serializers_utils import generic_get_results
class IpModelPermission(permissions.BasePermission):
@@ -39,15 +42,24 @@ class SearchAPIView(APIView):
content_type__model=self.model._meta.model_name,
)
- def get(self, request, format=None):
+ def get(self, request, slug=None, format=None, data_type="json"):
+ search_model = self.search_model_query(request).all()[0]
+ # get default importer
+ if slug:
+ q = search_model.export.filter(slug=slug)
+ if not q.count():
+ return HttpResponse('Page not found', status=404)
+ table_cols, col_names = q.all()[0].get_columns()
+ else:
+ table_cols, col_names = self.model.get_columns(dict_col_labels=False)
+
_get_item = get_item(
self.model,
"get_" + self.model.SLUG,
self.model.SLUG,
- no_permission_check=True
- # TODO: own_table_cols=get_table_cols_for_ope() - adapt columns
+ no_permission_check=True,
+ own_table_cols=table_cols
)
- search_model = self.search_model_query(request).all()[0]
if search_model.limit_query:
query = search_model.limit_query
if request.GET.get("search_vector", None):
@@ -55,13 +67,30 @@ class SearchAPIView(APIView):
request.GET._mutable = True
request.GET["search_vector"] = query
request.GET._mutable = False
- data_type = "json"
+
+ # source-1-689-source-1-1853 -> 689-1853
+ if "selected_ids" in request.GET:
+ value = request.GET["selected_ids"]
+ values = []
+ for idx, k in enumerate(value.split("-")):
+ if idx % 3 == 2:
+ values.append(k)
+ request.GET._mutable = True
+ request.GET["selected_ids"] = "-".join(values)
+ request.GET._mutable = False
+
if request.GET.get("data_type", None):
data_type = request.GET.get("data_type")
- response = _get_item(request, data_type=data_type)
+ response = _get_item(request, col_names=col_names, data_type=data_type,
+ type=data_type)
return response
+class ExportAPIView(SearchAPIView):
+ def get(self, request, slug=None, format=None, data_type="csv"):
+ return super().get(request, slug=slug, format=format, data_type=data_type)
+
+
class FacetAPIView(APIView):
authentication_classes = (authentication.TokenAuthentication,)
permission_classes = (permissions.IsAuthenticated, IpModelPermission)
@@ -78,6 +107,45 @@ class FacetAPIView(APIView):
def get(self, request, format=None):
values = {}
base_queries = self._get_base_search_model_queries()
+
+ # config
+ values["config"] = {
+ "search_columns": "",
+ "search_columns_label": "",
+ "exports": "",
+ "exports_label": ""
+ }
+ for model in self.models:
+ model_name = f"{model._meta.app_label}-{model._meta.model_name}-"
+ q = models_rest.ApiSearchModel.objects.filter(
+ user=request.user.apiuser,
+ content_type__app_label=model._meta.app_label,
+ content_type__model=model._meta.model_name
+ )
+ if not q.count():
+ continue
+ search_model = q.all()[0]
+ # cols, col_names = search_model.table_format.get_columns()
+ cols, col_names = model.get_columns(dict_col_labels=False)
+ cols, col_names = [c for c in cols if c], [c for c in col_names if c]
+ if cols and col_names:
+ if values["config"]["search_columns"]:
+ values["config"]["search_columns"] += "||"
+ values["config"]["search_columns_label"] += "||"
+ values["config"]["search_columns"] += "||".join([
+ model_name + (col[0] if isinstance(col, list) else col)
+ for col in cols
+ ])
+ values["config"]["search_columns_label"] += "||".join([
+ model_name + col for col in col_names
+ ])
+ for export in search_model.export.all():
+ if values["config"]["exports"]:
+ values["config"]["exports"] += "||"
+ values["config"]["exports_label"] += "||"
+ values["config"]["exports"] += model_name + export.slug
+ values["config"]["exports_label"] += model_name + export.name
+
for idx, select_form in enumerate(self.select_forms):
# only send types matching permissions
model, q = base_queries[idx]
diff --git a/ishtar_common/static/js/ishtar.js b/ishtar_common/static/js/ishtar.js
index 3df8fe8f0..56f4e157d 100644
--- a/ishtar_common/static/js/ishtar.js
+++ b/ishtar_common/static/js/ishtar.js
@@ -1128,7 +1128,7 @@ var qa_action_register = function(url, slug) {
};
-var update_export_urls = function(dt, sname, source, source_full, extra_sources, extra_tpl){
+var update_export_urls = function(dt, source_cls, sname, source, source_full, extra_sources, extra_tpl){
let rows = dt.rows( { selected: true } ).data();
let data = "selected_ids=";
for (k in rows){
@@ -1136,11 +1136,17 @@ var update_export_urls = function(dt, sname, source, source_full, extra_sources,
if (k > 0) data += "-";
data += rows[k]['id'];
}
- let csv_url = source + "csv?submited=1&" + data;
+ let extra = "?submited=1&" + data;
+ let csv_url = source + "csv" + extra;
$("." + sname + "-csv").attr("href", csv_url);
let csv_full_url = source_full + "csv?submited=1&" + data;
$("." + sname + "-csv-full").attr("href", csv_full_url);
+ $("." + source_cls + " ." + sname + "-csv-external").each(function(){
+ let url = $(this).attr("href").split('?')[0] + extra;
+ $(this).attr("href", url);
+ });
+
for (k in extra_sources){
let src = extra_sources[k];
let slug = src[0];
diff --git a/ishtar_common/templates/blocks/DataTables-content.html b/ishtar_common/templates/blocks/DataTables-content.html
index 029ebd84c..0665e7a4e 100644
--- a/ishtar_common/templates/blocks/DataTables-content.html
+++ b/ishtar_common/templates/blocks/DataTables-content.html
@@ -22,11 +22,11 @@
</div>
<div class="col">
<div class="btn-group btn-group-sm" role="group">
- <button id="export-{{name}}" type="button"
- class="btn btn-secondary dropdown-toggle table-export" data-toggle="dropdown"
- aria-haspopup="true" aria-expanded="false">
+ <button id="export-{{name}}" type="button"
+ class="btn btn-secondary dropdown-toggle table-export" data-toggle="dropdown"
+ aria-haspopup="true" aria-expanded="false">
{% trans 'Export'%}
- </button>
+ </button>
<div class="dropdown-menu" aria-labelledby="export-{{name}}">
{% if source_full or extra_sources %}
<a class="dropdown-item {{sname}}-csv" href='{{source}}csv' target='_blank'
@@ -40,7 +40,7 @@
{% endif %}
</div>
<div class="input-group-append">
- <div class="selected-lines input-group-text" id="btnGroupAddon">
+ <div class="selected-lines input-group-text">
<span class="sl-whole">{% trans "whole table" %}</span>
<span class="sl-selected"><span class="sl-number"></span>&nbsp;{% trans "selected item(s)" %}</span>
</div>
@@ -70,13 +70,13 @@
<input type="hidden" id="hidden_{{name}}" name="{{name}}"/>
- {% for source_id, source_label, source_url in external_sources %}
+ {% for source_id, source_label, source_url, columns, column_labels, export_urls in external_sources %}
<div id="grid_{{name}}_{{source_id}}_meta_wrapper" class="sources sources-{{source_id}}">
<table id='grid_{{name}}_{{source_id}}' class="display" width="100%">
<thead>
<tr>
<th></th>
- <th></th>{% for col in col_names %}
+ <th></th>{% for col in column_labels %}
<th>{{col}}</th>
{% endfor %}</tr>
</thead>
@@ -92,6 +92,7 @@
</button>
</div>
</div>
+ {% if export_urls %}
<div class="col">
<div class="btn-group btn-group-sm" role="group">
<button id="export-{{name}}-" type="button"
@@ -100,16 +101,18 @@
{% trans 'Export'%}
</button>
<div class="dropdown-menu" aria-labelledby="export-{{name}}">
- <a class="dropdown-item {{sname}}-csv" href="{{source}}csv" target="_blank" title="{% trans 'Export as CSV' %}">{% trans "CSV" %}</a>
+ {% for lbl_export, export_url in export_urls %}
+ <a class="dropdown-item {{sname}}-csv-external" href="{{export_url}}" target="_blank" title="{% trans 'Export as CSV - ' %}{{lbl_export}}">{{lbl_export}}</a>{% endfor %}
</div>
<div class="input-group-append">
- <div class="selected-lines input-group-text" id="btnGroupAddon">
+ <div class="selected-lines input-group-text">
<span class="sl-whole">{% trans "whole table" %}</span>
<span class="sl-selected"><span class="sl-number"></span>&nbsp;{% trans "selected item(s)" %}</span>
</div>
</div>
</div>
</div>
+ {% endif %}
</div>
diff --git a/ishtar_common/templates/blocks/DataTables-external-sources.html b/ishtar_common/templates/blocks/DataTables-external-sources.html
index c317e0c6f..af8774b82 100644
--- a/ishtar_common/templates/blocks/DataTables-external-sources.html
+++ b/ishtar_common/templates/blocks/DataTables-external-sources.html
@@ -6,7 +6,7 @@
<input type="radio" name="_sources" autocomplete="off" checked>
{% trans "Local" %}&nbsp;&nbsp;<span class="badge badge-light" id="source_badge_default">-</span>
</label>
- {% for source_id, source_label, source_url in external_sources %}
+ {% for source_id, source_label, source_url, columns, column_labels, export_urls in external_sources %}
<label id="source_button_{{source_id}}" class="btn btn-secondary">
<input type="radio" name="_sources" autocomplete="off">
{{source_label}}&nbsp;&nbsp;<span class="badge badge-light" id="source_badge_{{source_id}}">-</span>
diff --git a/ishtar_common/templates/blocks/DataTables-stats.html b/ishtar_common/templates/blocks/DataTables-stats.html
index d443a7721..8c6d387ac 100644
--- a/ishtar_common/templates/blocks/DataTables-stats.html
+++ b/ishtar_common/templates/blocks/DataTables-stats.html
@@ -48,7 +48,7 @@
{% with stats_name=name %}{% with extra="default" %}
{% include "blocks/DataTables-stats-detail.html" %}
{% endwith %}{% endwith %}
- {% if external_sources %}{% for source_id, source_label, source_url in external_sources %}
+ {% if external_sources %}{% for source_id, source_label, source_url, columns, column_labels, export_urls in external_sources %}
{% with stats_name=name %}{% with extra=source_id %}
{% include "blocks/DataTables-stats-detail.html" %}
{% endwith %}{% endwith %}
diff --git a/ishtar_common/templates/blocks/DataTables-tabs.html b/ishtar_common/templates/blocks/DataTables-tabs.html
index 8b6d4b807..2576f4df5 100644
--- a/ishtar_common/templates/blocks/DataTables-tabs.html
+++ b/ishtar_common/templates/blocks/DataTables-tabs.html
@@ -42,7 +42,7 @@
id="tab-content-gallery-{{name}}" role="tabpanel"
aria-labelledby="tab-gallery-{{name}}">
<div id="content-gallery-{{name}}" class="sources sources-default"></div>
- {% if external_sources %}{% for source_id, source_label, source_url in external_sources %}
+ {% if external_sources %}{% for source_id, source_label, source_url, columns, column_labels, export_urls in external_sources %}
<div id="content-gallery-{{name}}-{{source_id}}" class="sources sources-{{source_id}}"></div>
{% endfor %}{% endif %}
</div>{% endif %}
@@ -50,7 +50,7 @@
id="tab-content-map-{{name}}" role="tabpanel"
aria-labelledby="tab-map-{{name}}">
<div id="map-{{name}}-default" class="sources sources-default"></div>
- {% if external_sources %}{% for source_id, source_label, source_url in external_sources %}
+ {% if external_sources %}{% for source_id, source_label, source_url, columns, column_labels, export_urls in external_sources %}
<div id="map-{{name}}-{{source_id}}" class="sources sources-{{source_id}}"></div>{% endfor %}{% endif %}
</div>{% endif %}
{% if current_model.STATISTIC_MODALITIES %}
diff --git a/ishtar_common/templates/blocks/DataTables.html b/ishtar_common/templates/blocks/DataTables.html
index 8ef1a5403..19ffd948e 100644
--- a/ishtar_common/templates/blocks/DataTables.html
+++ b/ishtar_common/templates/blocks/DataTables.html
@@ -25,7 +25,7 @@
</div>
</div>
-{% for source_id, source_label, source_url in external_sources %}
+{% for source_id, source_label, source_url, columns, column_labels, export_urls in external_sources %}
<div class="modal fade table-modal-lg" tabindex="-1" role="dialog"
aria-hidden="true" id="modal_grid_{{name}}_{{source_id}}">
<div class="modal-dialog full modal-lg">
@@ -62,7 +62,7 @@ $('#modal_grid_{{name}}').on('hide.bs.modal', function (e) {
bs_hide_table("{{name}}");
});
-{% for source_id, source_label, source_url in external_sources %}
+{% for source_id, source_label, source_url, columns, column_labels, export_urls in external_sources %}
$('#modal_grid_{{name}}_{{source_id}}').on('show.bs.modal', function (e) {
bs_expand_table("{{name}}_{{source_id}}");
});
@@ -103,7 +103,7 @@ var selItems_{{sname}} = new Array();
{% if gallery %}
gallery_submit_search = function(image_page){
- {% if external_sources %}{% for source_id, source_label, source_url in external_sources %}
+ {% if external_sources %}{% for source_id, source_label, source_url, columns, column_labels, export_urls in external_sources %}
_gallery_submit_search(image_page, query_vars, "{{name}}", "{{source_url}}", "-{{source_id}}");
{% endfor %}{% endif %}
return _gallery_submit_search(image_page, query_vars, "{{name}}", "{{source}}");
@@ -114,7 +114,7 @@ map_submit_search = function(){
if (current_source == "default"){
return _map_submit_search(query_vars, "{{name}}", "{{source}}");
}
- {% if external_sources %}{% for source_id, source_label, source_url in external_sources %}
+ {% if external_sources %}{% for source_id, source_label, source_url, columns, column_labels, export_urls in external_sources %}
else if (current_source == "{{source_id}}"){
return _map_submit_search(query_vars, "{{name}}", "{{source_url}}", "{{source_id}}");
}
@@ -123,17 +123,17 @@ map_submit_search = function(){
{% endif %}
extra_list = [
- "default"{% for source_id, source_label, source_url in external_sources %},
+ "default"{% for source_id, source_label, source_url, columns, column_labels, export_urls in external_sources %},
"{{source_id}}"{% endfor %}
];
sources = [
- "{{source}}"{% for source_id, source_label, source_url in external_sources %},
+ "{{source}}"{% for source_id, source_label, source_url, columns, column_labels, export_urls in external_sources %},
"{{source_url}}"{% endfor %}
];
stats_submit_search = function(){
- {% if external_sources %}{% for source_id, source_label, source_url in external_sources %}
+ {% if external_sources %}{% for source_id, source_label, source_url, columns, column_labels, export_urls in external_sources %}
_stats_submit_search(query_vars, "{{name}}", "{{source_url}}", "{{source_id}}");
{% endfor %}{% endif %}
return _stats_submit_search(query_vars, "{{name}}", "{{source}}");
@@ -151,7 +151,7 @@ datatable_submit_search = function(not_submited){
datatable_{{sname}}.ajax.url(url);
datatable_{{sname}}.draw();
-{% if external_sources %}{% for source_id, source_label, source_url in external_sources %}
+{% if external_sources %}{% for source_id, source_label, source_url, columns, column_labels, export_urls in external_sources %}
if (!not_submited){
url = "{{source_url}}?submited=1&" + data;
} else {
@@ -170,16 +170,23 @@ datatable_submit_search = function(not_submited){
return false;
};
-update_submit_args = function(){
- var data = search_get_query_data(query_vars, "{{name}}");
- var csv_url = "{{source}}csv?submited=1&" + data;
- $(".{{sname}}-csv").attr("href", csv_url);
- var csv_full_url = "{{source_full}}csv?submited=1&" + data;
- $(".{{sname}}-csv-full").attr("href", csv_full_url);
+update_submit_args = function(source_cls){
+ if (!source_cls) source_cls = "sources-default";
+ let data = search_get_query_data(query_vars, "{{name}}");
+ let extra = "?submited=1&" + data;
+ let csv_url = "{{source}}csv" + extra;
+ $("." + source_cls + " .{{sname}}-csv").attr("href", csv_url);
+ let csv_full_url = "{{source_full}}csv?submited=1&" + data;
+ $("." + source_cls + " .{{sname}}-csv-full").attr("href", csv_full_url);
{% for slug, name, extra_source in extra_sources %}
- $(".{{slug}}-csv-full").attr("href", '{{extra_source}}csv?submited=1&' + data);{% endfor %}
+ $("." + source_cls + " .{{slug}}-csv-full").attr("href", '{{extra_source}}csv?submited=1&' + data);{% endfor %}
{% for template in current_model.label_templates %}
- $(".{{template.slug}}-labels").attr("href", '{{template.get_baselink_for_labels}}?submited=1&' + data);{% endfor %}
+ $("." + source_cls + " .{{template.slug}}-labels").attr("href", '{{template.get_baselink_for_labels}}?submited=1&' + data);{% endfor %}
+
+ $("." + source_cls + " .{{sname}}-csv-external").each(function(){
+ let url = $(this).attr("href").split('?')[0] + extra;
+ $(this).attr("href", url);
+ });
if ($('.modal-progress').length > 0){
$('.modal-progress').modal('hide');
@@ -187,24 +194,24 @@ update_submit_args = function(){
return false;
};
-update_select_args = function(dt){
+update_select_args = function(dt, source_cls){
let nb_row = dt.rows( { selected: true } ).count();
- $(".sl-number").html(nb_row);
+ $("." + source_cls + " .sl-number").html(nb_row);
if (nb_row == 0){
- $(".selected-lines .sl-whole").show();
- $(".selected-lines .sl-selected").hide();
- update_submit_args();
+ $("." + source_cls + " .selected-lines .sl-whole").show();
+ $("." + source_cls + " .selected-lines .sl-selected").hide();
+ update_submit_args(source_cls);
return;
}
- $(".selected-lines .sl-selected").show();
- $(".selected-lines .sl-whole").hide();
+ $("." + source_cls + " .selected-lines .sl-selected").show();
+ $("." + source_cls + " .selected-lines .sl-whole").hide();
let extra_sources = [{% for slug, name, extra_source in extra_sources %}
["{{slug}}", "{{name}}", "{{extra_source}}"]{% if not forloop.last %},{% endif %}
{% endfor %}];
let extra_tpl = [{% for template in current_model.label_templates %}
["{{template.slug}}", "{{template.get_baselink_for_labels}}"]{% if not forloop.last %},{% endif %}
{% endfor %}];
- update_export_urls(dt, "{{sname}}", "{{source}}", "{{source_full}}", extra_sources, extra_tpl);
+ update_export_urls(dt, source_cls, "{{sname}}", "{{source}}", "{{source_full}}", extra_sources, extra_tpl);
}
var current_source = "default";
@@ -227,7 +234,7 @@ jQuery(document).ready(function(){
return false;
}
);
- {% for source_id, source_label, source_url in external_sources %}
+ {% for source_id, source_label, source_url, columns, column_labels, export_urls in external_sources %}
$("#source_button_{{source_id}}").click(
function(){
current_source = "{{source_id}}";
@@ -327,14 +334,14 @@ jQuery(document).ready(function(){
datatable_{{sname}}.on(
'select',
function(e, dt, type, indexes){
- update_select_args(dt);
+ update_select_args(dt, 'sources-default');
dt_single_enable_disable_submit_button(e, dt, type, indexes);
}
);
datatable_{{sname}}.on(
'deselect',
function(e, dt, type, indexes){
- update_select_args(dt);
+ update_select_args(dt, 'sources-default');
dt_single_enable_disable_submit_button(e, dt, type, indexes);
}
);
@@ -342,20 +349,20 @@ jQuery(document).ready(function(){
datatable_{{sname}}.on(
'select',
function(e, dt, type, indexes){
- update_select_args(dt);
+ update_select_args(dt, 'sources-default');
dt_multi_enable_disable_submit_button(e, dt, type, indexes);
}
);
datatable_{{sname}}.on(
'deselect',
function(e, dt, type, indexes){
- update_select_args(dt);
+ update_select_args(dt, 'sources-default');
dt_multi_enable_disable_submit_button(e, dt, type, indexes);
}
);
{% endif %}
-{% if external_sources %}{% for source_id, source_label, source_url in external_sources %}
+{% if external_sources %}{% for source_id, source_label, source_url, columns, column_labels, export_urls in external_sources %}
var base_external_source = "{{source_url}}";
if (default_search_vector){
@@ -375,10 +382,13 @@ jQuery(document).ready(function(){
}
},
"deferLoading": 0,
+ "select": {
+ "style": 'multi'
+ },
"dom": 'litp',
"columns": [
{ "data": "id", "visible": false },
- { "data": "link", "orderable": false },{% for col in extra_cols %}
+ { "data": "link", "orderable": false },{% for col in columns %}
{ "data": "{{col}}", "defaultContent": "-",
"render": $.fn.dataTable.render.ellipsis( 70, true ) }{% if not forloop.last %},{% endif %}{% endfor %}
]
@@ -386,6 +396,18 @@ jQuery(document).ready(function(){
$.extend(datatable_options_{{source_id}}, datatables_default);
if (datatables_i18n) datatable_options_{{source_id}}['language'] = datatables_i18n;
datatable_{{sname}}_{{source_id}} = jQuery("#grid_{{name}}_{{source_id}}").DataTable(datatable_options_{{source_id}});
+ datatable_{{sname}}_{{source_id}}.on(
+ 'select',
+ function(e, dt, type, indexes){
+ update_select_args(dt, 'sources-{{source_id}}');
+ }
+ );
+ datatable_{{sname}}_{{source_id}}.on(
+ 'deselect',
+ function(e, dt, type, indexes){
+ update_select_args(dt, 'sources-{{source_id}}');
+ }
+ );
{% endfor %}{% endif %}
diff --git a/ishtar_common/urls.py b/ishtar_common/urls.py
index 8a0886551..974ad061f 100644
--- a/ishtar_common/urls.py
+++ b/ishtar_common/urls.py
@@ -20,6 +20,7 @@
from django.conf import settings
from django.conf.urls import include, url
from django.conf.urls.static import static
+from django.urls import path
from django.views.generic import TemplateView
from .menus import Menu
@@ -313,11 +314,6 @@ urlpatterns += [
name="get-by-importer",
),
url(
- r"search-external/(?P<model>[a-z-]+)/(?P<external_source_id>\d+)/(?P<data_type>[a-z-]+)?",
- views_item.get_distant_item,
- name="search-external"
- ),
- url(
r"new-person/(?:(?P<parent_name>[^/]+)/)?(?:(?P<limits>[^/]+)/)?$",
views.new_person,
name="new-person",
@@ -601,6 +597,21 @@ urlpatterns += [
views.QANotAvailable.as_view(),
name="qa-not-available",
),
+ path(
+ "external-search/<slug:model>/<int:external_source_id>/",
+ views_item.get_distant_item,
+ name="external-search"
+ ),
+ path(
+ "external-search/<slug:model>/<int:external_source_id>/<slug:data_type>",
+ views_item.get_distant_item,
+ name="external-search"
+ ),
+ path(
+ "external-export/<int:source_id>/<slug:model_name>/<slug:slug>/",
+ views_item.external_export,
+ name="external-export"
+ ),
]
urlpatterns += get_urls_for_model(models.Document, views, own=True)
diff --git a/ishtar_common/utils.py b/ishtar_common/utils.py
index 1d3c4d143..3abc0ddd1 100644
--- a/ishtar_common/utils.py
+++ b/ishtar_common/utils.py
@@ -1026,6 +1026,54 @@ def get_field_labels_from_path(model, path):
return labels
+def get_columns_from_class(cls, table_cols_attr="TABLE_COLS", dict_col_labels=True):
+ """
+ Get table columns and table label from a model
+ :param table_cols_attr: "TABLE_COLS" if not specified
+ :param dict_col_labels: (default: True) if set to False return list matching
+ with table_cols list
+ :return: (table_cols, table_col_labels)
+ """
+ col_labels = {}
+ slug = getattr(cls, "SLUG", None)
+ if hasattr(cls, "COL_LABELS"):
+ col_labels = cls.COL_LABELS
+ if slug in settings.COL_LABELS:
+ col_labels.update(settings.COL_LABELS[slug])
+ tb_key = (slug, table_cols_attr)
+ if tb_key in settings.TABLE_COLS:
+ table_cols = settings.TABLE_COLS[tb_key]
+ else:
+ table_cols = getattr(cls, table_cols_attr)
+ if callable(table_cols):
+ table_cols = table_cols()
+ if dict_col_labels:
+ return table_cols, col_labels
+ table_cols_label = []
+ for c in table_cols:
+ if c in col_labels:
+ table_cols_label.append(str(col_labels[c]))
+ else:
+ field_verbose_name, field_name = "", ""
+ field = cls
+ keys = c.split("__")
+ if "." in c:
+ keys = c.split(".")
+ for key in keys:
+ if hasattr(field, "remote_field") and field.remote_field:
+ field = field.remote_field.model
+ try:
+ field = field._meta.get_field(key)
+ field_verbose_name = field.verbose_name
+ except (fields.FieldDoesNotExist, AttributeError):
+ if hasattr(field, key + "_lbl"):
+ field_verbose_name = getattr(field, key + "_lbl")
+ else:
+ continue
+ table_cols_label.append(str(field_verbose_name))
+ return table_cols, table_cols_label
+
+
def create_default_areas(models=None, verbose=False):
# can be used on migrations if models are provided
if not models:
diff --git a/ishtar_common/views.py b/ishtar_common/views.py
index 3ae686f99..98bded3ec 100644
--- a/ishtar_common/views.py
+++ b/ishtar_common/views.py
@@ -650,17 +650,15 @@ def get_by_importer(
if data_type == "json":
res = "{}"
return HttpResponse(res, content_type="text/plain")
- imp = q.all()[0].get_importer_class()
- cols, col_names = [], []
- for formater in imp.LINE_EXPORT_FORMAT:
- if not formater:
- cols.append("")
- col_names.append("")
- continue
- cols.append(formater.export_field_name)
- col_names.append(formater.label)
- obj_name = imp.OBJECT_CLS.__name__.lower()
- return get_item(imp.OBJECT_CLS, "get_" + obj_name, obj_name, own_table_cols=cols)(
+ importer = q.all()[0]
+ importer_class = importer.get_importer_class()
+ cols, col_names = importer.get_columns(importer_class=importer_class)
+ if data_type == "csv" or dct.get("type", "") == "csv":
+ obj_name = importer.name
+ else:
+ obj_name = importer_class.OBJECT_CLS.__name__.lower()
+ return get_item(importer_class.OBJECT_CLS, "get_" + obj_name, obj_name,
+ own_table_cols=cols)(
request, data_type, full, force_own, col_names=col_names, **dct
)
@@ -2691,55 +2689,6 @@ class GeoFormMixin(IshtarMixin, LoginRequiredMixin):
return kwargs
-class GeoEditView(GeoFormMixin, UpdateView):
- page_name = _("Geo item modification")
-
- def get_form_kwargs(self):
- kwargs = super(GeoEditView, self).get_form_kwargs()
- try:
- geo = models.GeoVectorData.objects.get(pk=self.kwargs.get("pk"))
- assert check_permission(self.request, "geo/edit", geo.pk)
- except (AssertionError, models.GeoVectorData.DoesNotExist):
- raise Http404()
- initial = {}
-
- for k in (
- list(self.form_class.base_fields.keys()) +
- models.GeoVectorData.RELATED_MODELS
- ):
- value = getattr(geo, k)
- if hasattr(value, "all"):
- value = ",".join([str(v.pk) for v in value.all().order_by("pk")])
- if hasattr(value, "pk"):
- value = value.pk
- initial[k] = value
-
- kwargs["main_items_fields"] = {}
- kwargs["too_many"] = {}
- LIMIT = 10
- for k in models.GeoVectorData.RELATED_MODELS:
- kwargs["main_items_fields"][k] = []
- values = []
- for idx, related_item in enumerate(getattr(geo, k).all()):
- if idx >= LIMIT:
- if k not in kwargs["too_many"]:
- kwargs["too_many"][k] = []
- kwargs["too_many"][k].append(related_item.pk)
- continue
- pk = str(related_item.pk)
- values.append(pk)
- key = "{}_{}_main_item".format(k, pk)
- kwargs["main_items_fields"][k].append(
- (key, "{} - {}".format(_("Main geo item for"), related_item))
- )
- if related_item.main_geodata == geo:
- initial[key] = True
- initial[k] = ",".join(values)
- kwargs["initial"] = initial
- kwargs["user"] = self.request.user
- return kwargs
-
-
class GeoCreateView(GeoFormMixin, CreateView):
page_name = _("Geo item creation")
diff --git a/ishtar_common/views_item.py b/ishtar_common/views_item.py
index ce70559f9..673ffb46a 100644
--- a/ishtar_common/views_item.py
+++ b/ishtar_common/views_item.py
@@ -1428,6 +1428,7 @@ def _get_data_from_query(items, query_table_cols, extra_request_keys,
if hasattr(items.model, "locked"):
values.append("locked")
values.append("lock_user_id")
+ values = [v for v in values if v] # filter empty values
return items.values_list(*values)
@@ -2591,3 +2592,53 @@ def get_distant_item(request, model, external_source_id, data_type=None):
if "link" in row:
row["link"] = row["link"].replace(str(idx), source_id)
return HttpResponse(json.dumps(dct), content_type="application/json")
+
+
+def external_export(request, source_id, model_name, slug):
+ external_sources = request.session.get("EXTERNAL_SOURCES", {})
+ url = None
+ for source in external_sources:
+ try:
+ src_id, __ = source.split("||")
+ src_id = int(src_id)
+ except ValueError:
+ continue
+ if src_id != source_id:
+ continue
+ for model in external_sources[source]:
+ if model_name == model.split("-")[-1]:
+ url = reverse("api-export-" + model_name, args=[slug])
+
+ if not url:
+ return HttpResponse('Unauthorized', status=401)
+
+ try:
+ src = models_rest.ApiExternalSource.objects.get(pk=source_id)
+ except (models_rest.ApiExternalSource.DoesNotExist, ValueError):
+ return HttpResponse('Unauthorized', status=401)
+
+ url = src.url + url
+ try:
+ response = requests.get(
+ url,
+ params=request.GET,
+ timeout=20,
+ headers={"Authorization": f"Token {src.key}"},
+ )
+ except requests.exceptions.Timeout:
+ return HttpResponse('Gateway Timeout', status=504)
+ if response.status_code != 200:
+ lbl = {
+ 401: "Unauthorized",
+ 404: "Page not found",
+ 500: "Server error",
+ }
+ lbl = lbl[response.status_code] \
+ if response.status_code in lbl else "Unknown error"
+ return HttpResponse(lbl, status=response.status_code)
+
+ response = HttpResponse(response.text, content_type="text/csv")
+ n = datetime.datetime.now()
+ filename = f"{model_name}-{n.strftime('%Y%m%d-%H%M%S')}.csv"
+ response["Content-Disposition"] = "attachment; filename=%s" % filename
+ return response
diff --git a/ishtar_common/widgets.py b/ishtar_common/widgets.py
index a6a621cbe..57dc1a730 100644
--- a/ishtar_common/widgets.py
+++ b/ishtar_common/widgets.py
@@ -44,7 +44,7 @@ from json import JSONEncoder
from django.utils.translation import ugettext_lazy as _
from ishtar_common import models
-from ishtar_common.utils import reverse_coordinates
+from ishtar_common.utils import get_columns_from_class, reverse_coordinates
logger = logging.getLogger(__name__)
@@ -1204,20 +1204,10 @@ class DataTable(Select2Media, forms.RadioSelect):
def get_cols(self, python=False):
jq_col_names, extra_cols = [], []
- col_labels = {}
- slug = getattr(self.associated_model, "SLUG", None)
- if hasattr(self.associated_model, "COL_LABELS"):
- col_labels = self.associated_model.COL_LABELS
- if slug in settings.COL_LABELS:
- col_labels.update(settings.COL_LABELS[slug])
- tb_key = (slug, self.table_cols)
- if tb_key in settings.TABLE_COLS:
- table_cols = settings.TABLE_COLS[tb_key]
- else:
- table_cols = getattr(self.associated_model, self.table_cols)
- if callable(table_cols):
- table_cols = table_cols()
-
+ table_cols, col_labels = get_columns_from_class(
+ self.associated_model,
+ table_cols_attr=self.table_cols
+ )
for col_names in table_cols:
field_verbose_names = []
field_verbose_name, field_name = "", ""
diff --git a/ishtar_common/wizards.py b/ishtar_common/wizards.py
index 8acf1a3ed..d8c789018 100644
--- a/ishtar_common/wizards.py
+++ b/ishtar_common/wizards.py
@@ -48,7 +48,7 @@ from django.template import loader
from django.utils.translation import ugettext_lazy as _
from django.utils.safestring import mark_safe
-from ishtar_common import models
+from ishtar_common import models, models_rest
from ishtar_common.forms import CustomForm, reverse_lazy
from ishtar_common.utils import get_all_field_names, MultiValueDict, put_session_message
@@ -134,8 +134,26 @@ class IshtarWizard(NamedUrlWizardView):
src_id, lbl = source.split("||")
except ValueError:
continue
- url = reverse("search-external", args=[base_model_name, src_id])
- context["external_sources"].append((src_id, lbl, url))
+ q = models_rest.ApiExternalSource.objects.filter(
+ profiles=self.request.user.ishtaruser.current_profile,
+ pk=src_id
+ )
+ if not q.count():
+ continue
+ external_source = q.all()[0]
+ url = reverse("external-search", args=[base_model_name, src_id])
+ export_urls = []
+ for slug, label in external_source.get_exports(model_name):
+ export_urls.append(
+ (label,
+ reverse("external-export",
+ args=[src_id, base_model_name, slug]))
+ )
+ context["external_sources"].append(
+ (src_id, lbl, url, external_source.get_columns(model_name),
+ external_source.get_column_labels(model_name),
+ export_urls)
+ )
form.fields["pk"].widget.external_sources = context["external_sources"]