From 49c589af90b0e35a8949f4c325b6e2c49b50e932 Mon Sep 17 00:00:00 2001 From: tim738745 <98717409+tim738745@users.noreply.github.com> Date: Thu, 21 Nov 2024 11:33:54 -0800 Subject: [PATCH] feat: 402 - file requirements (#410) --- .../api/migrations/0039_filerequirements.py | 31 +++++++++ ...0040_add_datasets_and_file_requirements.py | 63 +++++++++++++++++++ django/api/models/__init__.py | 1 + django/api/models/file_requirements.py | 20 ++++++ django/api/serializers/file_requirements.py | 9 +++ django/api/services/file_requirements.py | 5 ++ django/api/viewsets/upload.py | 11 ++++ .../uploads/components/FileRequirements.js | 42 +++++++++++++ frontend/src/uploads/components/UploadPage.js | 6 ++ frontend/src/uploads/routes.js | 1 + 10 files changed, 189 insertions(+) create mode 100644 django/api/migrations/0039_filerequirements.py create mode 100644 django/api/migrations/0040_add_datasets_and_file_requirements.py create mode 100644 django/api/models/file_requirements.py create mode 100644 django/api/serializers/file_requirements.py create mode 100644 django/api/services/file_requirements.py create mode 100644 frontend/src/uploads/components/FileRequirements.js diff --git a/django/api/migrations/0039_filerequirements.py b/django/api/migrations/0039_filerequirements.py new file mode 100644 index 00000000..d38af6f6 --- /dev/null +++ b/django/api/migrations/0039_filerequirements.py @@ -0,0 +1,31 @@ +# Generated by Django 3.2.25 on 2024-11-07 22:15 + +from django.db import migrations, models +import django.db.models.deletion + + +class Migration(migrations.Migration): + + dependencies = [ + ('api', '0038_addregions'), + ] + + operations = [ + migrations.CreateModel( + name='FileRequirements', + fields=[ + ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('create_timestamp', models.DateTimeField(auto_now_add=True, null=True)), + ('create_user', models.CharField(default='SYSTEM', max_length=130)), + ('update_timestamp', models.DateTimeField(auto_now=True, null=True)), + ('update_user', models.CharField(max_length=130, null=True)), + ('sheet', models.TextField(blank=True, null=True)), + ('columns', models.TextField(blank=True, null=True)), + ('formats', models.TextField(blank=True, null=True)), + ('dataset', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, related_name='file_requirements', to='api.datasets')), + ], + options={ + 'db_table': 'file_requirements', + }, + ), + ] diff --git a/django/api/migrations/0040_add_datasets_and_file_requirements.py b/django/api/migrations/0040_add_datasets_and_file_requirements.py new file mode 100644 index 00000000..b2ec042a --- /dev/null +++ b/django/api/migrations/0040_add_datasets_and_file_requirements.py @@ -0,0 +1,63 @@ +# Generated by Django 3.2.25 on 2024-11-07 22:17 + +from django.db import migrations + + +def add_datasets_and_file_requirements(apps, schema_editor): + Datasets = apps.get_model("api", "Datasets") + FileRequirements = apps.get_model("api", "FileRequirements") + columns_default_text = "All required columns must match the column names exactly as they appear in the provided template" + formats_default_text = "File format should be xlsx. xlsm. only" + + ger_obj, ger_created = Datasets.objects.get_or_create( + name="Go Electric Rebates Program", defaults={"update_user": "SYSTEM"} + ) + FileRequirements.objects.get_or_create( + dataset=ger_obj, + defaults={ + "sheet": """ + The sheet name must be "Distribution List - Master" + """, + "columns": columns_default_text, + "formats": formats_default_text, + }, + ) + + arc_obj, arc_created = Datasets.objects.get_or_create( + name="ARC Project Tracking", defaults={"update_user": "SYSTEM"} + ) + FileRequirements.objects.get_or_create( + dataset=arc_obj, + defaults={ + "sheet": """ + The sheet name must be "ARC Data" + """, + "columns": columns_default_text, + "formats": formats_default_text, + }, + ) + + cvp_obj, cvp_created = Datasets.objects.get_or_create( + name="CVP Data", defaults={"update_user": "SYSTEM"} + ) + FileRequirements.objects.get_or_create( + dataset=cvp_obj, + defaults={ + "sheet": """ + The sheet name must be "Data" + """, + "columns": columns_default_text, + "formats": formats_default_text, + }, + ) + + +class Migration(migrations.Migration): + + dependencies = [ + ("api", "0039_filerequirements"), + ] + + operations = [ + migrations.RunPython(add_datasets_and_file_requirements), + ] diff --git a/django/api/models/__init__.py b/django/api/models/__init__.py index d1fc14cc..c163f3f8 100644 --- a/django/api/models/__init__.py +++ b/django/api/models/__init__.py @@ -27,3 +27,4 @@ from . import decoded_vin_record from . import regions from . import cvp_data +from . import file_requirements diff --git a/django/api/models/file_requirements.py b/django/api/models/file_requirements.py new file mode 100644 index 00000000..d9b3cbe5 --- /dev/null +++ b/django/api/models/file_requirements.py @@ -0,0 +1,20 @@ +from django.db import models +from auditable.models import Auditable +from api.models.datasets import Datasets + + +class FileRequirements(Auditable): + dataset = models.OneToOneField( + Datasets, + related_name="file_requirements", + on_delete=models.CASCADE, + ) + + sheet = models.TextField(blank=True, null=True) + + columns = models.TextField(blank=True, null=True) + + formats = models.TextField(blank=True, null=True) + + class Meta: + db_table = "file_requirements" diff --git a/django/api/serializers/file_requirements.py b/django/api/serializers/file_requirements.py new file mode 100644 index 00000000..f53b9718 --- /dev/null +++ b/django/api/serializers/file_requirements.py @@ -0,0 +1,9 @@ +from rest_framework.serializers import ModelSerializer +from api.models.file_requirements import FileRequirements + + +class FileRequirementsSerializer(ModelSerializer): + + class Meta: + model = FileRequirements + fields = ("sheet", "columns", "formats") diff --git a/django/api/services/file_requirements.py b/django/api/services/file_requirements.py new file mode 100644 index 00000000..06b029ae --- /dev/null +++ b/django/api/services/file_requirements.py @@ -0,0 +1,5 @@ +from api.models.file_requirements import FileRequirements + + +def get_file_requirements(dataset_name): + return FileRequirements.objects.filter(dataset__name=dataset_name).first() diff --git a/django/api/viewsets/upload.py b/django/api/viewsets/upload.py index 5f6d16ff..16132204 100644 --- a/django/api/viewsets/upload.py +++ b/django/api/viewsets/upload.py @@ -18,6 +18,8 @@ import api.constants.constants as constants from api.services.spreadsheet_uploader_prep import * from api.services.uploaded_vins_file import create_vins_file +from api.services.file_requirements import get_file_requirements +from api.serializers.file_requirements import FileRequirementsSerializer class UploadViewset(GenericViewSet): @@ -131,3 +133,12 @@ def download_dataset(self, request): return response except ValueError as e: return HttpResponse(str(e), status=400) + + @action(detail=False, methods=["get"]) + def file_requirements(self, request): + dataset_name = request.query_params.get("dataset") + file_requirements = get_file_requirements(dataset_name) + if file_requirements is None: + return Response({}) + serializer = FileRequirementsSerializer(file_requirements) + return Response(serializer.data) diff --git a/frontend/src/uploads/components/FileRequirements.js b/frontend/src/uploads/components/FileRequirements.js new file mode 100644 index 00000000..aa8e0834 --- /dev/null +++ b/frontend/src/uploads/components/FileRequirements.js @@ -0,0 +1,42 @@ +import React, { useState, useEffect } from "react"; +import useAxios from "../../app/utilities/useAxios"; +import ROUTES_UPLOAD from "../routes"; + +const FileRequirements = ({ datasetSelected }) => { + const axios = useAxios(); + const [requirements, setRequirements] = useState([]); + + useEffect(() => { + if (datasetSelected) { + axios + .get( + ROUTES_UPLOAD.FILE_REQUIREMENTS.replace(":dataset", datasetSelected), + ) + .then((response) => { + const list = []; + for (const [key, value] of Object.entries(response.data)) { + list.push(
  • {value}
  • ); + } + setRequirements(list); + }) + .catch((error) => { + //do something here? + }); + } + }, [datasetSelected]); + + if (requirements.length > 0) { + return ( +
    +

    File Requirements

    +
    + Ensure your file meets the following conditions before uploading: +
    + +
    + ); + } + return null; +}; + +export default FileRequirements; diff --git a/frontend/src/uploads/components/UploadPage.js b/frontend/src/uploads/components/UploadPage.js index 3094add1..fc1b1bca 100644 --- a/frontend/src/uploads/components/UploadPage.js +++ b/frontend/src/uploads/components/UploadPage.js @@ -14,6 +14,7 @@ import UploadIcon from "@mui/icons-material/Upload"; import DownloadIcon from "@mui/icons-material/Download"; import FileDropArea from "./FileDropArea"; import Loading from "../../app/components/Loading"; +import FileRequirements from "./FileRequirements"; const UploadPage = (props) => { const { @@ -100,6 +101,11 @@ const UploadPage = (props) => { uploadFiles={uploadFiles} /> + + +