nansencenter/django-geo-spaas

new base for viewer

Closed this issue · 8 comments

viewer application should be simplified and then divided into abstract classes and the child case. Creating abstract class and child class helps us to write code in DRY style of coding for further development.

For this reason, the viewer app will be replace by viewer_standalone for safety and code repository. In the next step, viewer_standalone will be split in two apps that contain the abstract classes and the child (usage) classes separately.

Models

Search - OK, remove str
Remove Dataset
Remove VisualizationParameter
Remove Visualization

Forms

SearchForm
Meta.fields = ['polygon', 'date0', 'date1', 'source']
should not be hardcoded.
It should be dynamically set in a method of SearchForm. For example:

# in base_viewer
class SearchForm(forms.ModelForm):
    search_model = Search
    search_fields = ['polygon', 'date0', 'date1', 'source']
    def __init__(self, *args, **kwargs):
         self.Meta.model = self.search_model
         self.Meta.fields = self.search_fields
         super().__init__(self, *args, **kwargs)
    class Meta:
        model = None
        fields = None
        ...

# in adas_viewer
class AdasSearchForm(SearchForm):
    search_model = AdasSearch
    search_fields = ['polygon', 'date0', 'date1', 'source', 'parameter']

Views

Keep IndexView
remove IndexView. set_params
Keep set_form_defaults, post, get
Split render into:
filter_datasets()

# base viewer
class IndexView(View):
    def get...
    def post...
    ...
    def filter_datasets(self, form):
        # filter by date
        # filter by source
        # filter by geometry

# adas_viewer
class AdasView(IndexView):
    def filter_datasets(self, form):
       super().filter_datasets(self, form)
       # filter by parameter name

Remove pagination of output
Remove params
Remove visualisation

def render(self, request)
    datasets = self.filter(self.form)
    datasets = self.paginate(datasets)
    context = self.set_context()
    render(context)

Template

Start from scratch:
https://django-leaflet.readthedocs.io/en/latest/widget.html
Bare minimum template should show a form (with a map) and a list of found files.

b6bc967. @akorosov
Dear Anton, this is the commit before starting the new branch with the correct approach of programming(starting from the models and ends in templates).

So reviewing the change of the files is for knowing the differences not the working code and approving it.

f94f919

is my draft of templates for possible usage in the future as snippet codes.

@akorosov Dear anton,

The first working version is on 1e57839.

  1. Let's move some functionality to ModelForms:

default values can be set a ModelForm method set_defaults()
Input and output is form

filtering of datasets can be done a ModelForm filter()
Input and output is dataset

class SearchForm1:
    def set_defaults(self, form):
        form['time_coverage_start'] = 10
        return form
    def filter(self, form, datasets):
        return datasets.objects.filter(time_coverage_start=form['time_coverage_start'])

class SearchForm2:
    def set_defaults(self, form):
        return form
    def filter(self, form, datasets):
        return datasets.objects.filter(geometry__intersects=form['geometry'])

class BaseView:
    class_form = [SearchForm1, SearchForm2]

    def set_defaults(self):
        self.forms = [form.set_defaults() for form in self.class_form]

    def filter(self, form):
        ds = Dataset.objects.all()
        for form in self.class_form:
            ds = form.filter(ds)
        return ds
  1. Let's make get() and post() methods flat (read Zen of Python)
    def get(self):
        self.set_defaults()
        self.validate()
        self.filter()
        self.set_context()
        render()
    
    def post(self, request):
        self.set_defaults()

        forms = [form(request) for form in self.class_form]

        self.validate()
        self.filter()
        self.set_context()
        render()
    
class AdasView(BaseView):
    class_form = [SearchForm1, SearchForm2, SearchForm3]

Add self.initial to forms:

class BaseForm(forms.ModelForm):
    initial = dict()
        
    def __init__(self, *args, **kwargs):
        uper().__init__(initial=self.initial, *args, **kwargs)
    

class TimeAndSourceForm(BaseForm):
    initial = dict(
        time_coverage_end = timezone.now(),
        time_coverage_start = timezone.datetime(2000, 1, 1),
        )
        
    class Meta:
        model = CatalogDataset
        fields = ['time_coverage_start', 'time_coverage_end', 'source']

    def filter(self, ds):
        t0 = self.cleaned_data['time_coverage_start']
        t1 = self.cleaned_data['time_coverage_end'] + \
            timezone.timedelta(hours=24)
        ds = ds.filter(Q(time_coverage_end__lt=t1) &
                       Q(time_coverage_start__gt=t0))
        src = [self.cleaned_data['source']]
        ds = ds.filter(source__in=src)
        return ds

class SpatialSearchForm(BaseForm):
    ...

form = TimeAndSourceForm()
form = TimeAndSourceForm(post.REQUEST)

Add parameter to create_forms()

And return forms instead of setting self.forms

    def create_forms(self, request_post=None):
        ''' Set default values for the form by instantiating them '''
        if request_post is None:
            request_post = dict()
        forms = [i(request_post) for i in self.form_class]
        return forms

    def get(self, request, *args, **kwargs):
        ''' Render page if no data is given '''
        forms = self.create_forms()
        forms = self.validate_forms(forms)
        self.filtering_the_datasets(request, forms)
        self.set_context(forms)
        return render(request, self.main_template, self.context)

    def post(self, request, *args, **kwargs):
        ''' all sections needed for POST requests'''
        forms = self.create_forms(request.POST)
        forms = self.validate_forms(forms)
        # modify attributes based on the forms cleaned data
        self.filtering_the_datasets(request, forms)
        # return self.final_rendering(request)
        self.set_context(forms)
        return render(request, self.main_template, self.context)

Change logics of the filtering function

    def get():
        ds = self.get_all_datasets()

    def post():
        ds = self.get_filtered_datasets(forms)

    def get_all_datasets(self):
        return CatalogDataset.objects.all()

    def get_filtered_datasets(self, forms):
        ds = self.get_all_datasets()
        for form in forms:
            ds = form.filter(ds)
        return ds