Skip to content

Commit

Permalink
Added initial API endpoints for HydroShare archival
Browse files Browse the repository at this point in the history
  • Loading branch information
Ken Lippold authored and Ken Lippold committed Dec 4, 2023
1 parent c5a6466 commit de07d57
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 5 deletions.
8 changes: 8 additions & 0 deletions core/endpoints/thing/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,3 +104,11 @@ class ThingMetadataGetResponse(Schema):

class Config:
allow_population_by_field_name = True


class ThingArchiveBody(Schema):
resource_title: str = Field(..., alias='resourceTitle')
resource_abstract: str = Field(..., alias='resourceAbstract')
resource_keywords: List[str] = Field(None, alias='resourceKeywords')
public_resource: bool = Field(False, alias='publicResource')
datastreams: List[UUID] = Field(None, alias='datastreams')
97 changes: 92 additions & 5 deletions core/endpoints/thing/views.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import os
import hsclient
import tempfile
from ninja import Path
from typing import List, Optional
from uuid import UUID
from datetime import datetime
from django.db import transaction, IntegrityError
from django.db.models import Q
from hsmodels.schemas.fields import PointCoverage
from accounts.auth.jwt import JWTAuth
from accounts.auth.basic import BasicAuth
from accounts.auth.anonymous import anonymous_auth
Expand All @@ -14,12 +18,13 @@
transfer_observed_property_ownership
from core.endpoints.processinglevel.utils import query_processing_levels, build_processing_level_response, \
transfer_processing_level_ownership
from core.endpoints.datastream.utils import query_datastreams, build_datastream_response
from core.endpoints.datastream.utils import query_datastreams, build_datastream_response, generate_csv
from core.endpoints.datastream.schemas import DatastreamGetResponse
from core.endpoints.sensor.utils import query_sensors, build_sensor_response, transfer_sensor_ownership
from .schemas import ThingGetResponse, ThingPostBody, ThingPatchBody, ThingOwnershipPatchBody, ThingPrivacyPatchBody, \
ThingMetadataGetResponse, LocationFields, ThingFields
ThingMetadataGetResponse, LocationFields, ThingFields, ThingArchiveBody
from .utils import query_things, get_thing_by_id, build_thing_response, check_thing_by_id
from hydroserver import settings


router = DataManagementRouter(tags=['Things'])
Expand Down Expand Up @@ -414,10 +419,92 @@ def get_datastreams(request, thing_id: UUID = Path(...)):
'{thing_id}/archive',
auth=[JWTAuth(), BasicAuth()],
response={
201: None
201: str,
401: str,
403: str,
404: str
}
)
def archive_thing(request, thing_id: UUID = Path(...)):
def archive_thing(request, data: ThingArchiveBody, thing_id: UUID = Path(...)):
""""""

return None
authenticated_user = request.authenticated_user

thing = get_thing_by_id(
user=authenticated_user,
thing_id=thing_id,
require_ownership=True,
raise_http_errors=True
)

if thing.hydroshare_archive_link is not None:
return 403, 'This site has already been archived to HydroShare.'

if authenticated_user.hydroshare_token is None:
return 403, 'You have not linked a HydroShare account to your HydroServer account.'

datastream_query, _ = query_datastreams(
user=request.authenticated_user,
thing_ids=[thing_id],
)

datastreams = datastream_query.all()

if data.datastreams:
datastreams = [
datastream for datastream in datastreams if datastream.id in datastreams
]

hydroshare_service = hsclient.HydroShare(
client_id=settings.AUTHLIB_OAUTH_CLIENTS['hydroshare']['client_id'],
token={
'access_token': authenticated_user.hydroshare_token['access_token'],
'token_type': authenticated_user.hydroshare_token['token_type'],
'scope': authenticated_user.hydroshare_token['scope'],
'state': '',
'expires_in': authenticated_user.hydroshare_token['expires_in'],
'refresh_token': authenticated_user.hydroshare_token['refresh_token']
}
)

archive_resource = hydroshare_service.create()
archive_resource.metadata.title = data.resource_title
archive_resource.metadata.abstract = data.resource_abstract
archive_resource.metadata.subjects = data.resource_keywords
archive_resource.set_sharing_status(data.public_resource)
archive_resource.metadata.spatial_coverage = PointCoverage(
name=thing.location.name,
north=thing.location.latitude,
east=thing.location.longitude,
projection='WGS 84 EPSG:4326',
type='point',
units='Decimal degrees'
)
archive_resource.metadata.additional_metadata = {
'Sampling Feature Type': thing.sampling_feature_type,
'Sampling Feature Code': thing.sampling_feature_code,
'Site Type': thing.site_type
}

if thing.data_disclaimer:
archive_resource.metadata.additional_metadata['Data Disclaimer'] = thing.data_disclaimer

archive_resource.save()

datastream_file_names = []

with tempfile.TemporaryDirectory() as temp_dir:
for datastream in datastreams:
temp_file_name = f'{datastream.description}.csv'
temp_file_index = 2
while temp_file_name in datastream_file_names:
temp_file_name = f'{datastream.description} - {str(temp_file_index)}.csv'
temp_file_index += 1
datastream_file_names.append(temp_file_name)
temp_file_path = os.path.join(temp_dir, temp_file_name)
with open(temp_file_path, 'w') as csv_file:
for line in generate_csv(datastream):
csv_file.write(line)
archive_resource.file_upload(temp_file_path)

return 201, archive_resource.resource_id
23 changes: 23 additions & 0 deletions core/migrations/0004_thing_hydroshare_archive_link_and_more.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
# Generated by Django 4.2.4 on 2023-12-01 18:35

from django.db import migrations, models


class Migration(migrations.Migration):

dependencies = [
('core', '0003_unitchangelog_thingchangelog_and_more'),
]

operations = [
migrations.AddField(
model_name='thing',
name='hydroshare_archive_link',
field=models.CharField(blank=True, db_column='hydroshareArchiveLink', max_length=500, null=True),
),
migrations.AddField(
model_name='thingchangelog',
name='hydroshare_archive_link',
field=models.CharField(blank=True, db_column='hydroshareArchiveLink', max_length=500, null=True),
),
]
1 change: 1 addition & 0 deletions core/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class Thing(models.Model):
site_type = models.CharField(max_length=200, db_column='siteType')
is_private = models.BooleanField(default=False, db_column='isPrivate')
data_disclaimer = models.TextField(null=True, blank=True, db_column='dataDisclaimer')
hydroshare_archive_link = models.CharField(max_length=500, blank=True, null=True, db_column='hydroshareArchiveLink')
location = models.OneToOneField(Location, related_name='thing', on_delete=models.CASCADE, db_column='locationId')
history = HistoricalRecords(custom_model_name='ThingChangeLog', related_name='log')

Expand Down
2 changes: 2 additions & 0 deletions environment.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,5 @@ dependencies:
- django_ses==2.5.0
- djangorestframework-simplejwt==5.3.0
- django-simple-history==3.4.0
- hsclient==0.3.3
- hsmodels==0.5.8
2 changes: 2 additions & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,5 @@ Authlib==1.2.1
django-storages==1.14.2
djangorestframework-simplejwt==5.3.0
django-simple-history==3.4.0
hsclient==0.3.3
hsmodels==0.5.8

0 comments on commit de07d57

Please sign in to comment.