Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dynamic Dataset creation form #210

Closed
wants to merge 70 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
70 commits
Select commit Hold shift + click to select a range
0a5e352
Fix fast-debug in dataservice image entrypoint.
robertbartel Oct 3, 2022
82639df
Fix imports in AllocationParadigm unit tests.
robertbartel Oct 12, 2022
187dad2
Add package client name vars to example.env.
robertbartel Sep 13, 2022
c7754f7
Add new QueryType for dataset management messages.
robertbartel Sep 13, 2022
fd5c834
Add GET_SERIALIZED_FORM query dataservice support.
robertbartel Oct 3, 2022
34b1528
Refactor ExternalRequestClient async_make_request.
robertbartel Oct 3, 2022
a8df363
Adding client methods for data/metadata retrieval.
robertbartel Oct 12, 2022
98ab36a
Refactor dataset request handler to use try block.
robertbartel Sep 13, 2022
6e2cf9a
Add get_serialized_datasets to external client.
robertbartel Sep 13, 2022
6c7535c
Improve DatasetExternalClient.
robertbartel Oct 3, 2022
c8a0569
Bump dataservice dependencies.
robertbartel Sep 13, 2022
839f70e
Fix LIST_FILES query response bug in dataservice.
robertbartel Oct 3, 2022
83387f9
Update dataset manager abstract interface.
robertbartel Oct 12, 2022
d06837c
More QueryType elements and update DatasetQuery.
robertbartel Oct 12, 2022
736d3c1
Update dataset message to support start and size.
robertbartel Oct 12, 2022
4c5e1e4
Bump requestservice dependencies.
robertbartel Sep 13, 2022
62a2998
Bump requestservice to 0.6.0.
robertbartel Sep 13, 2022
a6bce6a
Bump communication package version to 0.9.1.
robertbartel Oct 12, 2022
ea99c83
Optimize object store dataset reloading.
robertbartel Oct 12, 2022
bb629a4
Update object store manager for interface changes.
robertbartel Oct 12, 2022
39c3625
Update modeldata dependency versions.
robertbartel Oct 12, 2022
4ea065b
Bump modeldata package version to 0.9.0.
robertbartel Oct 12, 2022
5f6e389
Update client unit tests for interface changes.
robertbartel Oct 12, 2022
c25d828
Bump client package version to 0.2.0.
robertbartel Oct 12, 2022
128b6cd
Update dataservice response handling.
robertbartel Oct 12, 2022
791b1c2
Update dataservice package dependency versions.
robertbartel Oct 12, 2022
39f369a
Bump dataservice version to 0.4.0.
robertbartel Oct 12, 2022
cb54879
Add dataset view class and static page for GUI.
robertbartel Sep 13, 2022
a6238f2
Refactor static dir usage in DMODProxy.py.
robertbartel Sep 13, 2022
0758a38
Add Django GUI url def for dataset view.
robertbartel Sep 13, 2022
28215fc
Add client package build arg for GUI Docker build.
robertbartel Sep 13, 2022
39d6c51
Add second view and navigation.
robertbartel Sep 29, 2022
45f0b4f
Have DatasetManagementView.py send DS as list.
robertbartel Sep 30, 2022
59743c5
Update dataset management GUI with details view.
robertbartel Sep 30, 2022
60cb548
Cleanup after changing implementation approach.
robertbartel Sep 30, 2022
4aeab61
Create AbstractDatasetView GUI Django view class.
robertbartel Oct 3, 2022
af45047
Add Django view for direct dataservice API calls.
robertbartel Oct 3, 2022
5a5f3a8
Add GUI Django url for Dataset API AJAX calls.
robertbartel Oct 3, 2022
cf9de3b
Add commented-out helper debug volume for GUI.
robertbartel Oct 3, 2022
da982c4
Refactor DatasetManagementView inheritance.
robertbartel Oct 3, 2022
edbfd8d
More progress on dataset management template.
robertbartel Oct 3, 2022
5477e8f
Add dataset delete support to Django API view.
robertbartel Oct 3, 2022
af89068
Implement dataset delete in management view.
robertbartel Oct 3, 2022
5e9a59f
Adding Javascript component helper classes.
robertbartel Oct 12, 2022
a2b5c5c
Update dataset API view.
robertbartel Oct 12, 2022
11aef62
Update dataset management view template.
robertbartel Oct 12, 2022
b52cfd7
Add incomplete DatasetFileWebsocketFilelike.
robertbartel Oct 13, 2022
dafdf6a
fix dynamic form field node removal in CreateDatasetForm
aaraney Oct 28, 2022
9fd1736
add django forms for DatasetManagementView.
aaraney Oct 28, 2022
5a7127d
add utility method to for retrieving dynamic form from enum using its…
aaraney Oct 28, 2022
e8898c1
move DatasetFormatForm to bottom of DatasetManagementForms mod to mak…
aaraney Oct 28, 2022
3512ad4
date time field accepts partial ISO8061 format. this is a work around…
aaraney Oct 28, 2022
f3ab626
render forms in template
aaraney Oct 28, 2022
159404e
add dataset form css
aaraney Oct 28, 2022
05c8a9f
add custom id and class to form fields
aaraney Oct 28, 2022
09d03f3
wire-up labels in template and add comments about how form input's an…
aaraney Oct 28, 2022
4008016
add dynamic form mix in. sets custom id, class, and style to form fields
aaraney Oct 28, 2022
e543465
hide dynamic form labels by default
aaraney Oct 28, 2022
21779d6
disable all dynamic fields by default. fields must be disabled to sub…
aaraney Oct 28, 2022
38c7f7e
add onchange for toggling on / off dynamic form fields
aaraney Oct 28, 2022
55b0c13
render form and dynamic forms in DatasetManagementView
aaraney Oct 28, 2022
d8e1835
add trailing colon to labels
aaraney Oct 28, 2022
e373bfd
forgot to instantiate some form fields
aaraney Oct 28, 2022
cd8adcd
add slight gap between items
aaraney Oct 28, 2022
c166fbb
add default noop data format choice
aaraney Oct 28, 2022
ebda7a9
add file upload form field
aaraney Oct 31, 2022
fbb3e66
add filename validation, cannot include '_'. Minio does not allow '_'
aaraney Oct 31, 2022
0a0b21f
update file fields' to use input validation
aaraney Oct 31, 2022
a640f9c
add js utility module
aaraney Oct 31, 2022
085dab4
add start time end time field validation
aaraney Oct 31, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion docker/main/dataservice/entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ if [ -d ${UPDATED_PACKAGES_DIR:=/updated_packages} ]; then
for srv in $(pip -qq freeze | grep dmod | awk -F= '{print $1}' | awk -F- '{print $2}'); do
if [ $(ls ${UPDATED_PACKAGES_DIR} | grep dmod.${srv}- | wc -l) -eq 1 ]; then
pip uninstall -y --no-input $(pip -qq freeze | grep dmod.${srv} | awk -F= '{print $1}')
pip install $(ls ${UPDATED_PACKAGES_DIR}/*.whl | grep dmod.${srv}-)
pip install --no-deps $(ls ${UPDATED_PACKAGES_DIR}/*.whl | grep dmod.${srv}-)
fi
done
#pip install ${UPDATED_PACKAGES_DIR}/*.whl
Expand Down
3 changes: 3 additions & 0 deletions docker/nwm_gui/docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ services:
args:
docker_internal_registry: ${DOCKER_INTERNAL_REGISTRY:?Missing DOCKER_INTERNAL_REGISTRY value (see 'Private Docker Registry ' section in example.env)}
comms_package_name: ${PYTHON_PACKAGE_DIST_NAME_COMMS:?}
client_package_name: ${PYTHON_PACKAGE_DIST_NAME_CLIENT:?}
networks:
- request-listener-net
# Call this when starting the container
Expand Down Expand Up @@ -62,6 +63,8 @@ services:
volumes:
- ${DMOD_APP_STATIC:?}:/usr/maas_portal/static
- ${DMOD_SSL_DIR}/requestservice:/usr/maas_portal/ssl
# Needed only for speeding debugging
#- ${DOCKER_GUI_HOST_SRC:?GUI sources path not configured in environment}/MaaS:/usr/maas_portal/MaaS
#- ${DOCKER_GUI_HOST_VENV_DIR:-/tmp/blah}:${DOCKER_GUI_CONTAINER_VENV_DIR:-/tmp/blah}
# Expose Django's port to the internal network so that the web server may access it
expose:
Expand Down
5 changes: 5 additions & 0 deletions example.env
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,11 @@ NGEN_BRANCH=master
## Python Packages Settings ##
########################################################################

## The "name" of the built client Python distribution package, for purposes of installing (e.g., via pip)
PYTHON_PACKAGE_DIST_NAME_CLIENT=dmod-client
## The name of the actual Python communication package (i.e., for importing or specifying as a module on the command line)
PYTHON_PACKAGE_NAME_CLIENT=dmod.client

## The "name" of the built communication Python distribution package, for purposes of installing (e.g., via pip)
PYTHON_PACKAGE_DIST_NAME_COMMS=dmod-communication
## The name of the actual Python communication package (i.e., for importing or specifying as a module on the command line)
Expand Down
29 changes: 29 additions & 0 deletions python/gui/MaaS/cbv/AbstractDatasetView.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
from abc import ABC
from django.views.generic.base import View
from dmod.client.request_clients import DatasetExternalClient
import logging
logger = logging.getLogger("gui_log")
from .DMODProxy import DMODMixin, GUI_STATIC_SSL_DIR
from typing import Dict


class AbstractDatasetView(View, DMODMixin, ABC):

def __init__(self, *args, **kwargs):
super(AbstractDatasetView, self).__init__(*args, **kwargs)
self._dataset_client = None

async def get_dataset(self, dataset_name: str) -> Dict[str, dict]:
serial_dataset = await self.dataset_client.get_serialized_datasets(dataset_name=dataset_name)
return serial_dataset

async def get_datasets(self) -> Dict[str, dict]:
serial_datasets = await self.dataset_client.get_serialized_datasets()
return serial_datasets

@property
def dataset_client(self) -> DatasetExternalClient:
if self._dataset_client is None:
self._dataset_client = DatasetExternalClient(endpoint_uri=self.maas_endpoint_uri,
ssl_directory=GUI_STATIC_SSL_DIR)
return self._dataset_client
5 changes: 4 additions & 1 deletion python/gui/MaaS/cbv/DMODProxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
from pathlib import Path
from typing import List, Optional, Tuple, Type

GUI_STATIC_SSL_DIR = Path('/usr/maas_portal/ssl')


class RequestFormProcessor(ABC):

Expand Down Expand Up @@ -209,7 +211,7 @@ class PostFormRequestClient(ModelExecRequestClient):
def _bootstrap_ssl_dir(cls, ssl_dir: Optional[Path] = None):
if ssl_dir is None:
ssl_dir = Path(__file__).resolve().parent.parent.parent.joinpath('ssl')
ssl_dir = Path('/usr/maas_portal/ssl') #Fixme
ssl_dir = GUI_STATIC_SSL_DIR #Fixme
return ssl_dir

def __init__(self, endpoint_uri: str, http_request: HttpRequest, ssl_dir: Optional[Path] = None):
Expand Down Expand Up @@ -315,6 +317,7 @@ def forward_request(self, request: HttpRequest, event_type: MessageEventType) ->
client = PostFormRequestClient(endpoint_uri=self.maas_endpoint_uri, http_request=request)
if event_type == MessageEventType.MODEL_EXEC_REQUEST:
form_processor_type = ModelExecRequestFormProcessor
# TODO: need a new type of form processor here (or 3 more, for management, uploading, and downloading)
else:
raise RuntimeError("{} got unsupported event type: {}".format(self.__class__.__name__, str(event_type)))

Expand Down
62 changes: 62 additions & 0 deletions python/gui/MaaS/cbv/DatasetApiView.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import asyncio
from django.http import JsonResponse
from wsgiref.util import FileWrapper
from django.http.response import StreamingHttpResponse
from .AbstractDatasetView import AbstractDatasetView
from .DatasetFileWebsocketFilelike import DatasetFileWebsocketFilelike
import logging
logger = logging.getLogger("gui_log")


class DatasetApiView(AbstractDatasetView):

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)

def _get_dataset_content_details(self, dataset_name: str):
result = asyncio.get_event_loop().run_until_complete(self.dataset_client.get_dataset_content_details(name=dataset_name))
logger.info(result)
return JsonResponse({"contents": result}, status=200)

def _delete_dataset(self, dataset_name: str) -> JsonResponse:
result = asyncio.get_event_loop().run_until_complete(self.dataset_client.delete_dataset(name=dataset_name))
return JsonResponse({"successful": result}, status=200)

def _get_datasets_json(self) -> JsonResponse:
serial_dataset_map = asyncio.get_event_loop().run_until_complete(self.get_datasets())
return JsonResponse({"datasets": serial_dataset_map}, status=200)

def _get_dataset_json(self, dataset_name: str) -> JsonResponse:
serial_dataset = asyncio.get_event_loop().run_until_complete(self.get_dataset(dataset_name=dataset_name))
return JsonResponse({"dataset": serial_dataset[dataset_name]}, status=200)

def _get_download(self, request, *args, **kwargs):
dataset_name = request.GET.get("dataset_name", None)
item_name = request.GET.get("item_name", None)
chunk_size = 8192

custom_filelike = DatasetFileWebsocketFilelike(self.dataset_client, dataset_name, item_name)

response = StreamingHttpResponse(
FileWrapper(custom_filelike, chunk_size),
content_type="application/octet-stream"
)
response['Content-Length'] = asyncio.get_event_loop().run_until_complete(self.dataset_client.get_item_size(dataset_name, item_name))
response['Content-Disposition'] = "attachment; filename=%s" % item_name
return response

def get(self, request, *args, **kwargs):
request_type = request.GET.get("request_type", None)
if request_type == 'download_file':
return self._get_download(request)
elif request_type == 'datasets':
return self._get_datasets_json()
elif request_type == 'dataset':
return self._get_dataset_json(dataset_name=request.GET.get("name", None))
elif request_type == 'contents':
return self._get_dataset_content_details(dataset_name=request.GET.get("name", None))
if request_type == 'delete':
return self._delete_dataset(dataset_name=request.GET.get("name", None))

# TODO: finish
return JsonResponse({}, status=400)
20 changes: 20 additions & 0 deletions python/gui/MaaS/cbv/DatasetFileWebsocketFilelike.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import asyncio
from typing import AnyStr
from dmod.client.request_clients import DatasetExternalClient


class DatasetFileWebsocketFilelike:

def __init__(self, client: DatasetExternalClient, dataset_name: str, file_name: str):
self._client = client
self._dataset_name = dataset_name
self._file_name = file_name
self._read_index: int = 0

def read(self, blksize: int) -> AnyStr:

result = asyncio.get_event_loop().run_until_complete(
self._client.download_item_block(dataset_name=self._dataset_name, item_name=self._file_name,
blk_start=self._read_index, blk_size=blksize))
self._read_index += blksize
return result
Loading