Skip to content

Commit

Permalink
temp commit
Browse files Browse the repository at this point in the history
  • Loading branch information
kiancm committed Jan 16, 2021
1 parent 968a421 commit 65b346f
Show file tree
Hide file tree
Showing 2 changed files with 257 additions and 19 deletions.
213 changes: 197 additions & 16 deletions api/serializers.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
from rest_framework import serializers
from drf_writable_nested.serializers import NestedCreateMixin
from rest_framework.fields import empty
from django.db import transaction
from drf_writable_nested.serializers import NestedCreateMixin, WritableNestedModelSerializer
from drf_writable_nested.mixins import UniqueFieldsMixin

from database import models
from database.scripts.import_rmg_models import get_species_hash, get_reaction_hash
from database.models.kinetic_data import validate_kinetics_data
from api.models import Revision


class NestedModelSerializer(NestedCreateMixin, serializers.ModelSerializer):
class ThroughModelSerializer(NestedCreateMixin, serializers.ModelSerializer):
def update(self, instance, validated_data):
for model in self.models:
new_objects = []
Expand All @@ -22,39 +25,218 @@ def update(self, instance, validated_data):
return super().update(instance, validated_data)


class OptionalNestedCreateSerializer(WritableNestedModelSerializer):
def update_relations(self, instance, relations):
for name, data in relations.items():
if isinstance(data, list):
if isinstance(data[0], dict):
continue
getattr(instance, name).add(*data)
else:
setattr(instance, name, data)
instance.save()

return instance

def split_relations_data(self, validated_data):
relations = {}
for name in self.create_names:
data = self.validated_data.pop(name)
if data is not None:
relations[name] = data

return relations, validated_data

def update(self, instance, validated_data):
instance = super().update(instance, validated_data)
relations, validated_data = self.split_relations_data(validated_data)
instance = self.update_relations(instance, relations)

return instance

def create(self, validated_data):
relations, validated_data = self.split_relations_data(validated_data)
instance = super().create(validated_data)

return self.update_relations(instance, relations)


class NestedCreateUpdateSerializer(WritableNestedModelSerializer):
def prepare_nested_data(self, data, serializer):
nested_data = {k: v for k, v in data.items() if k in serializer.nested_names}
for name, items in nested_data.items():
for i, item in enumerate(items):
child_serializer = serializer.fields.get(name).child
lookup_data = {k: v for k, v in item.items() if k in child_serializer.lookup_fields}
model = child_serializer.Meta.model
if hasattr(child_serializer, "nested_names"):
updated_item = self.prepare_nested_data(item, child_serializer)
else:
updated_item = {}
try:
id = model.objects.get(**lookup_data).id
nested_data[name][i].update({"id": id, **updated_item})
except model.DoesNotExist:
pass

data = {**data, **nested_data}
return data

def run_validation(self, data=empty):
return super().run_validation(data=self.prepare_nested_data(data, self))


class ValidTogetherMixin:
pass


class NestedGetCreateSerializer(WritableNestedModelSerializer):
def run_validation(self, data):
model = self.Meta.model
try:
obj = model.objects.get(pk=data)
data = self.__class__(obj).data
except model.DoesNotExist:
raise ValueError(f"ID {data} does not exist")
except (TypeError, ValueError):
raise

return super().run_validation(data=data)


class FormulaSerializer(serializers.ModelSerializer):
class Meta:
model = models.Formula
fields = "__all__"


class IsomerSerializer(serializers.ModelSerializer):
class StructureSerializer(UniqueFieldsMixin, serializers.ModelSerializer):
lookup_fields = ["adjacency_list"]

class Meta:
model = models.Structure
exclude = ["isomer"]


class IsomerSerializer(NestedCreateMixin, serializers.ModelSerializer):
structure_set = StructureSerializer(many=True)
# formula = serializers.CharField(source="formula.formula")

class Meta:
model = models.Isomer
fields = "__all__"

# def validate_structure_set(self, value):
# serializer = StructureSerializer(many=True, data=value)
# serializer.is_valid(raise_exception=True)

def create(self, validated_data):
structure_serializer = StructureSerializer(
many=True, data=validated_data.pop("structure_set")
)
instance = models.Isomer.objects.create(**validated_data)
structure_serializer.is_valid()
structures = structure_serializer.save()
for structure in structures:
structure.isomer = instance
structure.save()

return instance

def update(self, instance, validated_data):
with transaction.atomic():
# formula = validated_data.pop("formula")["formula"]
structure_serializer = StructureSerializer(
many=True, data=validated_data.pop("structure_set")
)
structure_serializer.is_valid()
structures = structure_serializer.save()
for structure in structures:
structure.isomer = instance
structure.save()
# instance.formula = models.Formula.objects.get_or_create(formula=formula)[0]

return super().update(instance, validated_data)


class SpeciesSerializer(serializers.ModelSerializer):
new_isomers = IsomerSerializer(many=True, required=False, write_only=True)

class Meta:
model = models.Species
exclude = ["hash"]
fields = "__all__"
# exclude = ["hash"]
extra_kwargs = {"isomers": {"required": False}, "hash": {"read_only": True}}

def to_internal_value(self, data):
internal_value = super().to_internal_value(data)
new_isomers = data.get("new_isomers")
if new_isomers:
internal_value["new_isomers"] = new_isomers

return internal_value

# def validate_new_isomers(self, value):
# serializer = IsomerSerializer(many=True, data=value)
# serializer.is_valid(raise_exception=True)

def validate(self, attrs):
if not attrs.get("isomers") and not attrs.get("new_isomers"):
raise serializers.ValidationError(r"Must have either 'isomers' or 'new_isomers' field")

return super().validate(attrs)

def update_isomers(self, instance, isomers_data, new_isomers_data):
with transaction.atomic():
instance.isomers.clear()
isomers = isomers_data
if new_isomers_data:
serializer = IsomerSerializer(many=True, data=new_isomers_data)
serializer.is_valid()
new_isomers = serializer.save()
isomers.extend(isomer.id for isomer in new_isomers)

instance.isomers.add(*isomers)

return instance

def update(self, instance, validated_data):
instance = self.update_isomers(
instance, validated_data.pop("isomers", []), validated_data.pop("new_isomers", None)
)

return super().update(instance, validated_data)

def create(self, validated_data):
isomers = models.Isomer.objects.filter(pk__in=validated_data["isomers"])
validated_data["hash"] = get_species_hash(isomers)
isomers_data = validated_data.pop("isomers", [])
new_isomers_data = validated_data.pop("new_isomers", None)

with transaction.atomic():
instance = super().create(validated_data)

return self.update_isomers(instance, isomers_data, new_isomers_data)


return super().update(validated_data)
class StoichiometrySerializer(WritableNestedModelSerializer):
species = serializers.PrimaryKeyRelatedField(
required=False, many=False, queryset=models.Species.objects.all()
)
new_species = SpeciesSerializer(source="species", write_only=True, required=False)

update_names = ["species"]

class StoichiometrySerializer(serializers.ModelSerializer):
class Meta:
model = models.Stoichiometry
exclude = ["reaction"]


class ReactionSerializer(NestedModelSerializer):
class ReactionSerializer(WritableNestedModelSerializer):
stoichiometry_set = StoichiometrySerializer(many=True)
models = [models.Stoichiometry]

update_names = ["stoichiometry_set"]

# def update(self, instance, validated_data):
# pass

class Meta:
model = models.Reaction
Expand Down Expand Up @@ -87,10 +269,12 @@ class Meta:
exclude = ["kinetics"]


class KineticsSerializer(NestedModelSerializer):
class KineticsSerializer(ThroughModelSerializer):
efficiency_set = EfficiencySerializer(many=True)
models = [models.Efficiency]
data = serializers.JSONField(source="raw_data", validators=[validate_kinetics_data])

def validate_diff(self, value):
return validate_kinetics_data(value)

class Meta:
model = models.Kinetics
Expand All @@ -102,9 +286,6 @@ class Meta:
model = models.SpeciesName
exclude = ["kinetic_model"]

def validate_data(self, value):
return validate_kinetics_data(value)


class ThermoCommentSerializer(serializers.ModelSerializer):
class Meta:
Expand All @@ -124,7 +305,7 @@ class Meta:
exclude = ["kinetic_model"]


class KineticModelSerializer(NestedModelSerializer):
class KineticModelSerializer(ThroughModelSerializer):
speciesname_set = SpeciesNameSerializer(many=True)
thermocomment_set = ThermoCommentSerializer(many=True)
transportcomment_set = TransportCommentSerializer(many=True)
Expand Down
63 changes: 60 additions & 3 deletions database/views.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import functools
import uuid
from itertools import zip_longest
from collections import defaultdict

from dal import autocomplete
from django.contrib.auth import login
from django.contrib.contenttypes.models import ContentType
from django.http import HttpResponseRedirect
from django.urls import reverse
from django.core.paginator import Paginator, PageNotAnInteger, EmptyPage
from django.views import View
from django.views.generic import TemplateView, DetailView
from django.views.generic.edit import FormView
from django.views.generic import TemplateView, DetailView, FormView, UpdateView
from django_filters.views import FilterView
from django.http import HttpResponse
from django.utils.html import format_html
Expand All @@ -29,6 +30,8 @@
from .filters import SpeciesFilter, ReactionFilter, SourceFilter
from .forms import RegistrationForm
from database.templatetags import renders
from api import serializers
from api.models import Revision


class SidebarLookup:
Expand Down Expand Up @@ -280,7 +283,7 @@ def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
kinetics = self.get_object()
context["table_data"] = kinetics.data.table_data()
context["efficiencies"] = kinetics.data.efficiency_set.all()
context["efficiencies"] = kinetics.efficiency_set.all()
context["kinetics_comments"] = kinetics.kineticscomment_set.order_by("kinetic_model__id")

return context
Expand Down Expand Up @@ -357,3 +360,57 @@ class StructureAutocompleteView(AutocompleteView):
def get_result_label(self, item):
draw_url = reverse("draw-structure", args=[item.pk])
return format_html(f'<img src="{draw_url}" />')


class SpeciesRevisionView(UpdateView):
template_name = "database/revision.html"
model = Species
serializer_class = serializers.SpeciesSerializer
fields = ["prime_id", "cas_number", "isomers"]
success_url = "/species/{id}"
nested_processors = {
"isomers": {
"formset": "forms.IsomerFormSet",
"serializer": serializers.IsomerSerializer,
"structure_set": {
"formset": "forms.StructureFormSet",
"serializer": serializers.StructureSerializer,
},
}
}

def get_formset(self, formset_class, prefix):
instance = self.get_object()
if self.request.POST:
formset = formset_class(self.request.POST, instance=instance, prefix=prefix)
else:
formset = formset_class(instance=instance, prefix=prefix)

return formset

def post(self, request, *args, pk=None, **kwargs):
self.object = self.get_object()
form = self.get_form()
formsets = self.get_formsets()

if form.is_valid() and all(form.is_valid() for form in formsets):
return self.form_valid(form)
else:
return self.form_invalid(form)

def form_valid(self, form):
serializer = self.serializer_class(form.save(commit=False))
Revision.objects.create(
content_type=ContentType.objects.get_for_model(self.model),
diff={k: v for k, v in serializer.data.items() if k in ["id", *form.changed_data]},
created_by=self.request.user,
)

return HttpResponseRedirect(self.get_success_url())

def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)

context["name"] = self.model.__name__

return context

0 comments on commit 65b346f

Please sign in to comment.