Skip to content

Commit

Permalink
prototype v1
Browse files Browse the repository at this point in the history
  • Loading branch information
shrouxm committed Sep 26, 2024
1 parent 6906be2 commit 9bf5a17
Show file tree
Hide file tree
Showing 5 changed files with 348 additions and 46 deletions.
84 changes: 82 additions & 2 deletions terraso_backend/apps/graphql/schema/schema.graphql
Original file line number Diff line number Diff line change
Expand Up @@ -1712,6 +1712,7 @@ type Mutations {
markProjectSeen(input: ProjectMarkSeenMutationInput!): ProjectMarkSeenMutationPayload!
updateSoilData(input: SoilDataUpdateMutationInput!): SoilDataUpdateMutationPayload!
updateDepthDependentSoilData(input: DepthDependentSoilDataUpdateMutationInput!): DepthDependentSoilDataUpdateMutationPayload!
bulkUpdateSoilData(input: SoilDataBulkUpdateInput!): SoilDataBulkUpdatePayload!
updateSoilDataDepthInterval(input: SoilDataUpdateDepthIntervalMutationInput!): SoilDataUpdateDepthIntervalMutationPayload!
deleteSoilDataDepthInterval(input: SoilDataDeleteDepthIntervalMutationInput!): SoilDataDeleteDepthIntervalMutationPayload!
updateProjectSoilSettings(input: ProjectSoilSettingsUpdateMutationInput!): ProjectSoilSettingsUpdateMutationPayload!
Expand Down Expand Up @@ -2281,7 +2282,6 @@ type SoilDataUpdateMutationPayload {
}

input SoilDataUpdateMutationInput {
siteId: ID!
downSlope: SoilIdSoilDataDownSlopeChoices
crossSlope: SoilIdSoilDataCrossSlopeChoices
bedrock: Int
Expand All @@ -2299,6 +2299,7 @@ input SoilDataUpdateMutationInput {
soilDepthSelect: SoilIdSoilDataSoilDepthSelectChoices
landCoverSelect: SoilIdSoilDataLandCoverSelectChoices
grazingSelect: SoilIdSoilDataGrazingSelectChoices
siteId: ID!
depthIntervalPreset: SoilIdSoilDataDepthIntervalPresetChoices
clientMutationId: String
}
Expand All @@ -2310,7 +2311,6 @@ type DepthDependentSoilDataUpdateMutationPayload {
}

input DepthDependentSoilDataUpdateMutationInput {
siteId: ID!
depthInterval: DepthIntervalInput!
texture: SoilIdDepthDependentSoilDataTextureChoices
clayPercent: Int
Expand All @@ -2334,9 +2334,89 @@ input DepthDependentSoilDataUpdateMutationInput {
soilOrganicMatterTesting: SoilIdDepthDependentSoilDataSoilOrganicMatterTestingChoices
sodiumAbsorptionRatio: Decimal
carbonates: SoilIdDepthDependentSoilDataCarbonatesChoices
siteId: ID!
clientMutationId: String
}

type SoilDataBulkUpdatePayload {
errors: GenericScalar
results: [SoilDataBulkUpdateResultEntry!]!
clientMutationId: String
}

type SoilDataBulkUpdateResultEntry {
siteId: ID!
result: SoilDataBulkUpdateResult!
}

union SoilDataBulkUpdateResult = SoilDataBulkUpdateSuccess | SoilDataBulkUpdateFailure

type SoilDataBulkUpdateSuccess {
soilData: SoilDataNode!
}

type SoilDataBulkUpdateFailure {
reason: SoilDataBulkUpdateFailureReason!
}

enum SoilDataBulkUpdateFailureReason {
SITE_DELETED
LOST_PERMISSION
}

input SoilDataBulkUpdateInput {
soilData: [SoilDataBulkUpdateEntry!]!
clientMutationId: String
}

input SoilDataBulkUpdateEntry {
downSlope: SoilIdSoilDataDownSlopeChoices
crossSlope: SoilIdSoilDataCrossSlopeChoices
bedrock: Int
slopeLandscapePosition: SoilIdSoilDataSlopeLandscapePositionChoices
slopeAspect: Int
slopeSteepnessSelect: SoilIdSoilDataSlopeSteepnessSelectChoices
slopeSteepnessPercent: Int
slopeSteepnessDegree: Int
surfaceCracksSelect: SoilIdSoilDataSurfaceCracksSelectChoices
surfaceSaltSelect: SoilIdSoilDataSurfaceSaltSelectChoices
floodingSelect: SoilIdSoilDataFloodingSelectChoices
limeRequirementsSelect: SoilIdSoilDataLimeRequirementsSelectChoices
surfaceStoninessSelect: SoilIdSoilDataSurfaceStoninessSelectChoices
waterTableDepthSelect: SoilIdSoilDataWaterTableDepthSelectChoices
soilDepthSelect: SoilIdSoilDataSoilDepthSelectChoices
landCoverSelect: SoilIdSoilDataLandCoverSelectChoices
grazingSelect: SoilIdSoilDataGrazingSelectChoices
siteId: ID!
depthIntervals: [SoilDataBulkUpdateDepthDependentEntry!]!
}

input SoilDataBulkUpdateDepthDependentEntry {
depthInterval: DepthIntervalInput!
texture: SoilIdDepthDependentSoilDataTextureChoices
clayPercent: Int
rockFragmentVolume: SoilIdDepthDependentSoilDataRockFragmentVolumeChoices
colorHue: Float
colorValue: Float
colorChroma: Float
colorPhotoUsed: Boolean
colorPhotoSoilCondition: SoilIdDepthDependentSoilDataColorPhotoSoilConditionChoices
colorPhotoLightingCondition: SoilIdDepthDependentSoilDataColorPhotoLightingConditionChoices
conductivity: Decimal
conductivityTest: SoilIdDepthDependentSoilDataConductivityTestChoices
conductivityUnit: SoilIdDepthDependentSoilDataConductivityUnitChoices
structure: SoilIdDepthDependentSoilDataStructureChoices
ph: Decimal
phTestingSolution: SoilIdDepthDependentSoilDataPhTestingSolutionChoices
phTestingMethod: SoilIdDepthDependentSoilDataPhTestingMethodChoices
soilOrganicCarbon: Decimal
soilOrganicMatter: Decimal
soilOrganicCarbonTesting: SoilIdDepthDependentSoilDataSoilOrganicCarbonTestingChoices
soilOrganicMatterTesting: SoilIdDepthDependentSoilDataSoilOrganicMatterTestingChoices
sodiumAbsorptionRatio: Decimal
carbonates: SoilIdDepthDependentSoilDataCarbonatesChoices
}

type SoilDataUpdateDepthIntervalMutationPayload {
errors: GenericScalar
soilData: SoilDataNode
Expand Down
2 changes: 2 additions & 0 deletions terraso_backend/apps/graphql/schema/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
SoilDataUpdateMutation,
)
from apps.soil_id.graphql.soil_id.endpoints import soil_id
from apps.soil_id.graphql.soil_id.soil_data.types import SoilDataBulkUpdate

from .audit_logs import AuditLogNode
from .commons import TerrasoRelayNode
Expand Down Expand Up @@ -191,6 +192,7 @@ class Mutations(graphene.ObjectType):
mark_project_seen = ProjectMarkSeenMutation.Field()
update_soil_data = SoilDataUpdateMutation.Field()
update_depth_dependent_soil_data = DepthDependentSoilDataUpdateMutation.Field()
bulk_update_soil_data = SoilDataBulkUpdate.Field()
update_soil_data_depth_interval = SoilDataUpdateDepthIntervalMutation.Field()
delete_soil_data_depth_interval = SoilDataDeleteDepthIntervalMutation.Field()
update_project_soil_settings = ProjectSoilSettingsUpdateMutation.Field()
Expand Down
94 changes: 50 additions & 44 deletions terraso_backend/apps/soil_id/graphql/soil_data.py
Original file line number Diff line number Diff line change
Expand Up @@ -325,29 +325,32 @@ def mutate_and_get_payload(cls, root, info, site_id, depth_interval, **kwargs):
return SoilDataDeleteDepthIntervalMutation(soil_data=site.soil_data)


class SoilDataInputs:
down_slope = SoilDataNode.down_slope_enum()
cross_slope = SoilDataNode.cross_slope_enum()
bedrock = graphene.Int()
slope_landscape_position = SoilDataNode.slope_landscape_position_enum()
slope_aspect = graphene.Int()
slope_steepness_select = SoilDataNode.slope_steepness_enum()
slope_steepness_percent = graphene.Int()
slope_steepness_degree = graphene.Int()
surface_cracks_select = SoilDataNode.surface_cracks_enum()
surface_salt_select = SoilDataNode.surface_salt_enum()
flooding_select = SoilDataNode.flooding_enum()
lime_requirements_select = SoilDataNode.lime_requirements_enum()
surface_stoniness_select = SoilDataNode.surface_stoniness_enum()
water_table_depth_select = SoilDataNode.water_table_depth_enum()
soil_depth_select = SoilDataNode.soil_depth_enum()
land_cover_select = SoilDataNode.land_cover_enum()
grazing_select = SoilDataNode.grazing_enum()


class SoilDataUpdateMutation(BaseWriteMutation):
soil_data = graphene.Field(SoilDataNode)
model_class = SoilData

class Input:
class Input(SoilDataInputs):
site_id = graphene.ID(required=True)
down_slope = SoilDataNode.down_slope_enum()
cross_slope = SoilDataNode.cross_slope_enum()
bedrock = graphene.Int()
slope_landscape_position = SoilDataNode.slope_landscape_position_enum()
slope_aspect = graphene.Int()
slope_steepness_select = SoilDataNode.slope_steepness_enum()
slope_steepness_percent = graphene.Int()
slope_steepness_degree = graphene.Int()
surface_cracks_select = SoilDataNode.surface_cracks_enum()
surface_salt_select = SoilDataNode.surface_salt_enum()
flooding_select = SoilDataNode.flooding_enum()
lime_requirements_select = SoilDataNode.lime_requirements_enum()
surface_stoniness_select = SoilDataNode.surface_stoniness_enum()
water_table_depth_select = SoilDataNode.water_table_depth_enum()
soil_depth_select = SoilDataNode.soil_depth_enum()
land_cover_select = SoilDataNode.land_cover_enum()
grazing_select = SoilDataNode.grazing_enum()
depth_interval_preset = SoilDataNode.depth_interval_preset_enum()

@classmethod
Expand All @@ -373,38 +376,41 @@ def mutate_and_get_payload(cls, root, info, site_id, **kwargs):
return result


class SoilDataDepthDependentInputs:
depth_interval = graphene.Field(DepthIntervalInput, required=True)
texture = DepthDependentSoilDataNode.texture_enum()
clay_percent = graphene.Int()
rock_fragment_volume = DepthDependentSoilDataNode.rock_fragment_volume_enum()
color_hue = graphene.Float()
color_value = graphene.Float()
color_chroma = graphene.Float()
color_photo_used = graphene.Boolean()
color_photo_soil_condition = DepthDependentSoilDataNode.color_photo_soil_condition_enum()
color_photo_lighting_condition = (
DepthDependentSoilDataNode.color_photo_lighting_condition_enum()
)
conductivity = graphene.Decimal()
conductivity_test = DepthDependentSoilDataNode.conductivity_test_enum()
conductivity_unit = DepthDependentSoilDataNode.conductivity_unit_enum()
structure = DepthDependentSoilDataNode.structure_enum()
ph = graphene.Decimal()
ph_testing_solution = DepthDependentSoilDataNode.ph_testing_solution_enum()
ph_testing_method = DepthDependentSoilDataNode.ph_testing_method_enum()
soil_organic_carbon = graphene.Decimal()
soil_organic_matter = graphene.Decimal()
soil_organic_carbon_testing = DepthDependentSoilDataNode.soil_organic_carbon_testing_enum()
soil_organic_matter_testing = DepthDependentSoilDataNode.soil_organic_matter_testing_enum()
sodium_absorption_ratio = graphene.Decimal()
carbonates = DepthDependentSoilDataNode.carbonates_enum()


class DepthDependentSoilDataUpdateMutation(BaseWriteMutation):
soil_data = graphene.Field(SoilDataNode)
model_class = DepthDependentSoilData
result_class = SoilData

class Input:
class Input(SoilDataDepthDependentInputs):
site_id = graphene.ID(required=True)
depth_interval = graphene.Field(DepthIntervalInput, required=True)
texture = DepthDependentSoilDataNode.texture_enum()
clay_percent = graphene.Int()
rock_fragment_volume = DepthDependentSoilDataNode.rock_fragment_volume_enum()
color_hue = graphene.Float()
color_value = graphene.Float()
color_chroma = graphene.Float()
color_photo_used = graphene.Boolean()
color_photo_soil_condition = DepthDependentSoilDataNode.color_photo_soil_condition_enum()
color_photo_lighting_condition = (
DepthDependentSoilDataNode.color_photo_lighting_condition_enum()
)
conductivity = graphene.Decimal()
conductivity_test = DepthDependentSoilDataNode.conductivity_test_enum()
conductivity_unit = DepthDependentSoilDataNode.conductivity_unit_enum()
structure = DepthDependentSoilDataNode.structure_enum()
ph = graphene.Decimal()
ph_testing_solution = DepthDependentSoilDataNode.ph_testing_solution_enum()
ph_testing_method = DepthDependentSoilDataNode.ph_testing_method_enum()
soil_organic_carbon = graphene.Decimal()
soil_organic_matter = graphene.Decimal()
soil_organic_carbon_testing = DepthDependentSoilDataNode.soil_organic_carbon_testing_enum()
soil_organic_matter_testing = DepthDependentSoilDataNode.soil_organic_matter_testing_enum()
sodium_absorption_ratio = graphene.Decimal()
carbonates = DepthDependentSoilDataNode.carbonates_enum()

@classmethod
def mutate_and_get_payload(cls, root, info, site_id, depth_interval, **kwargs):
Expand Down
137 changes: 137 additions & 0 deletions terraso_backend/apps/soil_id/graphql/soil_id/soil_data/types.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
import enum
import traceback

import graphene
import structlog
from django.db import transaction

from apps.graphql.schema.commons import BaseWriteMutation
from apps.project_management.models.sites import Site
from apps.project_management.permission_rules import Context
from apps.project_management.permission_table import SiteAction, check_site_permission
from apps.soil_id.graphql.soil_data import (
SoilDataDepthDependentInputs,
SoilDataInputs,
SoilDataNode,
)
from apps.soil_id.models.soil_data import SoilData

logger = structlog.get_logger(__name__)


class SoilDataBulkUpdateSuccess(graphene.ObjectType):
soil_data = graphene.Field(SoilDataNode, required=True)


# TODO: just a generic "can't access" result?
class SoilDataBulkUpdateFailureReason(graphene.Enum):
DOES_NOT_EXIST = "DOES_NOT_EXIST"
NOT_ALLOWED = "NOT_ALLOWED"


class SoilDataBulkUpdateFailure(graphene.ObjectType):
reason = graphene.Field(SoilDataBulkUpdateFailureReason, required=True)


class SoilDataBulkUpdateResult(graphene.Union):
class Meta:
types = (SoilDataBulkUpdateSuccess, SoilDataBulkUpdateFailure)


class SoilDataBulkUpdateResultEntry(graphene.ObjectType):
site_id = graphene.ID(required=True)
result = graphene.Field(SoilDataBulkUpdateResult, required=True)


class SoilDataBulkUpdateDepthDependentEntry(SoilDataDepthDependentInputs, graphene.InputObjectType):
pass


class SoilDataBulkUpdateEntry(SoilDataInputs, graphene.InputObjectType):
site_id = graphene.ID(required=True)
depth_intervals = graphene.Field(
graphene.List(graphene.NonNull(SoilDataBulkUpdateDepthDependentEntry)), required=True
)


class SoilDataBulkUpdate(BaseWriteMutation):
results = graphene.Field(
graphene.List(graphene.NonNull(SoilDataBulkUpdateResultEntry)), required=True
)

class Input:
soil_data = graphene.Field(
graphene.List(graphene.NonNull(SoilDataBulkUpdateEntry)), required=True
)

@classmethod
def mutate_and_get_payload(cls, root, info, soil_data):
try:
results = []

with transaction.atomic():
for entry in soil_data:
site_id = entry.pop("site_id")
depth_intervals = entry.pop("depth_intervals")

site = Site.objects.filter(id=site_id).first()

if site is None:
results.append(
SoilDataBulkUpdateResultEntry(
site_id=site_id,
result=SoilDataBulkUpdateFailure(
reason=SoilDataBulkUpdateFailureReason.DOES_NOT_EXIST
),
)
)
continue

user = info.context.user
if not check_site_permission(user, SiteAction.ENTER_DATA, Context(site=site)):
results.append(
SoilDataBulkUpdateResultEntry(
site_id=entry["site_id"],
result=SoilDataBulkUpdateFailure(
reason=SoilDataBulkUpdateFailureReason.NOT_ALLOWED
),
)
)
continue

if not hasattr(site, "soil_data"):
site.soil_data = SoilData()

for attr, value in entry.items():
if isinstance(value, enum.Enum):
value = value.value
setattr(site.soil_data, attr, value)

site.soil_data.save()

for depth_interval_input in depth_intervals:
interval = depth_interval_input.pop("depth_interval")
depth_interval, _ = site.soil_data.depth_dependent_data.get_or_create(
depth_interval_start=interval["start"],
depth_interval_end=interval["end"],
)

for attr, value in depth_interval_input.items():
if isinstance(value, enum.Enum):
value = value.value
setattr(depth_interval, attr, value)

depth_interval.save()

results.append(
SoilDataBulkUpdateResultEntry(
site_id=site_id,
result=SoilDataBulkUpdateSuccess(soil_data=site.soil_data),
)
)

logger.info(results)

return cls(results=results)
except Exception as exc:
logger.info(traceback.format_exc(exc))
Loading

0 comments on commit 9bf5a17

Please sign in to comment.