How to generate multiple nested changesets with cast_assoc - nested

I have Memberships, Lists, Items and Memories defined as follows :
schema "lists" do
has_many :itemlists, Learnit.Itemlist, on_delete: :delete_all
many_to_many :items, Learnit.Item, join_through: Learnit.Itemlist
has_many :memberships, Learnit.Membership, on_delete: :delete_all
end
schema "memberships" do
belongs_to :list, Learnit.List
has_many :memorys, Learnit.Memory, on_delete: :delete_all
end
schema "itemlists" do
belongs_to :item, Learnit.Item
belongs_to :list, Learnit.List
end
schema "items" do
has_many :itemlists, Learnit.Itemlist
many_to_many :lists, Learnit.List, join_through: Learnit.Itemlist
has_many :memorys, Learnit.Memory
end
schema "memorys" do
belongs_to :membership, Learnit.Membership
belongs_to :item, Learnit.Item
end
My Membership's model
def changeset(struct, params \\ %{}) do
struct
|> cast(params, [:user_id, :list_id])
|> validate_required([:user_id, :list_id])
|> cast_assoc(:memorys)
end
My Memory's model
def changeset(struct, params \\ %{}) do
struct
|> cast(params, [:status, :membership_id, :item_id])
|> foreign_key_constraint([:membership_id, :item_id])
|> unique_constraint(:membership_id_item_id)
|> validate_required([:membership_id, :item_id])
end
I try to create a new membership and generate his memories associated with one query. I need to get first Items that are associated with the Membership through its list. Then my plan is to use Membership's cast_assoc property to save the whole in Repo.
How can I load these items in the Membership's changeset?
Items are already loaded correctly, I get a Map.put/4 Undefined...
def create(conn, %{"membership" => membership_params}) do
memories = %{}
list =
List
|> Repo.get!(membership_params["list_id"])
|> Repo.preload(:items)
|> Map.get(:items) # Get the list of items
|> Enum.map(&load_items(&1, memories)) # Loop through the list to get each item
IO.inspect(memories)
Map.put(membership_params, :memorys, memories)
membership_with_memories = Membership.changeset(%Membership{}, membership_params)
case Repo.insert(membership_with_memories) do
{:ok, _} ->
conn
|> put_flash(:info, "Membership created successfully.")
|> redirect(to: list_path(conn, :index))
{:error, membership_with_memories} ->
Logger.debug("Membership : failed to save membership")
conn
|> put_flash(:alert, "Membership was not created.")
|> redirect(to: topic_path(conn, :index))
end
end
defp load_items(item, memories) do
item
|> Map.put(memories, :item, item) # Add item to the hash
end

I found my way to load the params correctly, but still I cant find the way to save it in repo (I get a "cannot convert the given list to a string").
%{"list_id" => "1",
"memorys" => [%{"item_id" => "1"}, %{"item_id" => "2"}, %{"item_id" => "3"}],
"user_id" => "1"}
def create(conn, %{"membership" => params}) do
memories = []
params =
List
|> Repo.get!(params["list_id"])
|> Repo.preload(:items)
|> Map.get(:items) # Get the list of items
|> Enum.map(&add_items(&1, memories)) # Loop through the list to get list of item ids
|> (&Map.put(params, "memorys", &1)).() # Add the list to membership params
|> IO.inspect()
membership_with_memories = Membership.changeset(%Membership{}, params)
case Repo.insert(membership_with_memories) do
{:ok, _} ->
conn
|> put_flash(:info, "Membership created successfully.")
|> redirect(to: list_path(conn, :index))
{:error, membership_with_memories} ->
Logger.debug("Membership : failed to save membership")
conn
|> put_flash(:error, "Membership was not created.")
|> redirect(to: list_path(conn, :index))
end
end
defp add_items(item, memories) do
memories ++ %{"item_id" => Kernel.inspect(item.id)} # Make sure id is a string
end

Just found the problem in my Memory's model. I don't really get why the validate_required blocks the cast_assoc, but it is fine for me...
def changeset(struct, params \\ %{}) do
struct
|> cast(params, [:status, :membership_id, :item_id])
#|> foreign_key_constraint([:membership_id, :item_id]) # Dont use that !
|> unique_constraint(:membership_id_item_id) # Dont forget to put constraint on table too
|> validate_required([:item_id]) # Cannot use :membership_id with cast_assoc
end

Related

kedro dynamic catalog creation only for specific nodes before their run

I have several thousands of files to be processed of the different types. I am using dynamic catalog creation with hooks. I used first after_catalog_created hook but it is too early in and I need those entries only for specific nodes. My try is with before_node_run for specific node tags returning the dictionary with just dynamically created entries. Node function is **kwargs only. It works as I see that node get updated inputs, but the problem is that I need to provide for the node specification the already existing catalog entry. So I have such, fake one. Then I am using it to build a dictionary with the same length as the dictionary that is being returned by the hook.
Pipeline code
for doc in docs["Type1_documents"]:
item = doc["name"]
item_name, _ = os.path.splitext(item)
type1_datasets_dict[item_name] = "brace_dictionary"
return Pipeline(
[
node(
func=func1,
inputs=type1_datasets_dict,
outputs=[
f"output1",
f"output2",
],
name=f"type1_eta",
tags=["dynamic-catalog", "type1", "data-engineering"],
)
]
)
Hook code
#hook_impl
def before_node_run(
self, node: Node, catalog: DataCatalog
) -> Optional[Dict[str, Any]]:
self.node = node
self.catalog = catalog
if "dynamic-catalog" in node.tags:
input_catalog_name = node.name
catalog_string = f"params:{input_catalog_name}.full_name"
if self.catalog.exists(catalog_string):
true_datasets_dict = {}
catalog_properties = self.catalog.load(f"params:{input_catalog_name}")
catalog_name = catalog_properties["full_name"]
type = catalog_properties["type"]
subtype = catalog_properties["subtype"]
datasets_dict = self.catalog.load(f"params:{catalog_name}")
for dataset in datasets_dict:
doc_name, _ = os.path.splitext(dataset["name"])
self.add_text_dataset(
name=doc_name,
folder=f"parsed/{type}/{subtype}",
)
true_datasets_dict[doc_name] = doc_name
return true_datasets_dict
return true_datasets_dict
But I am getting value error for this:
line 487, in _run_with_dict
raise ValueError(
ValueError: Node type1_eta: func1([brace_dictionary,brace_dictionary,brace_dictionary,..,brace_dictionary]) -> [output1, output2] expected 1 input(s) ['brace_dictionary'], but got the following 1497 input(s) instead: ['file1', 'file2', ...].
Is there another way how to do it conditionally?

List Slicing in Power Query M Code running into runtime errors with functions being assigned to lists?

I am trying to use the following M code in my custom function to slice a list by a list of split offsets.
I am not sure why this line brings up an error. I have localized it using query designer to the last expression before "in."
let
BodyText = Table1_2,
splitfunc = SMTSplit,
SplitLines = Text.Split(BodyText, "#(lf)"),
CleanedLines = List.Transform(SplitLines, each Text.Remove(_, {":",";"," "})),
SplitCriteria = List.Transform(CleanedLines, each splitfunc(_)),
CriteriaIndexes = List.PositionOf(SplitCriteria, true,Occurrence.All),
Rejoin = (LineList as list) as text => List.Combine(List.Transform(LineList, each _ + "#(lf)")),
IndexIndex = List.Positions(CriteriaIndexes),
MaxIndexIndex = List.Max(IndexIndex),
MaxLineIndex = List.Max(List.Positions(SplitLines)),
BodySplits = List.Transform( IndexIndex, each if _ < MaxIndexIndex
then List.Range(CriteriaIndexes(_), CriteriaIndexes(_)- CriteriaIndexes(_))
else List.Range(CriteriaIndexes(_), MaxLineIndex - CriteriaIndexes(_))
)
in
BodySplits
This results in an expression error indicating that I tried to convert a list to a function.
Expression.Error: We cannot convert a value of type List to type Function.
Details:
Value=[List]
Type=[Type]
If anyone knows of a better way to slice lists by index in Power Query, please let me know!
Edit:
The data in Table1_2 is a string with line returns in it.
SMT6
Sometexthere
TMs
Header1
kkagorqr
an
fgaklgas55SMT3
dall
WorkingonSMT6
Also, the custom function SMTSplit is:
SMTSplit = (TestText as text) => let
split = Text.Length(TestText) < 6 and Text.Contains(TestText, "SMT")
in
split
enter code here
I was using () function syntax where I should have been using {} syntax to access a list element by index.
It works now!
= (BodyText as text, splitfunc as function) => let
SplitLines = Text.Split(BodyText, "#(lf)"),
CleanedLines = List.Transform(SplitLines, each Text.Remove(_, {":",";"," "})),
SplitCriteria = List.Transform(CleanedLines, each splitfunc(_)),
CriteriaIndexes = List.PositionOf(SplitCriteria, true,Occurrence.All),
Rejoin = (LineList as list) as text => Text.Combine(List.Transform(LineList, each Text.Combine({_, "#(lf)"}))),
IndexIndex = List.Positions(CriteriaIndexes),
MaxLineIndex = List.Max(List.Positions(SplitLines)),
BodySplits = List.Transform(IndexIndex, each if _ < List.Max(IndexIndex) and List.Max(IndexIndex) >0
then Rejoin(List.Range(SplitLines,CriteriaIndexes{_}, CriteriaIndexes{_+1} - CriteriaIndexes{_}))
else Rejoin(List.Range(SplitLines, CriteriaIndexes{_}, List.Max(List.Positions(SplitLines))))
),
LineList = List.Transform(IndexIndex, each CleanedLines{CriteriaIndexes{_}}),
ResultTable = Table.FromColumns({LineList, BodySplits}, {"Line","SplitBody"})
in ResultTable

i want to create a search in django which have order by istartswith first than icontains

def autocomplete(request):
template_name='searchresults.html'
if 'term' in request.GET:
qs=Post.objects.filter(Q(title__istartswith=request.GET.get('term'))|Q(content__icontains=request.GET.get('term')))
titles=list()
for post in qs:
titles.append(post.title)
return JsonResponse(titles, safe=False)
return render(request, template_name)
How can I can order them ins such a way that if it begins with term order it as first and the one that contains it but does not begin with it as second
You can make use of .union(…) [Django-doc]:
term = request.GET.get('term')
qs = Post.objects.filter(
title__istartswith=term
).union(Post.objects.filter(
content__icontains=term
))
or with an .annotate(…) [Django-doc]:
from django.db.models import BooleanField, ExpressionWrapper, Q
term = request.GET.get('term')
qs = Post.objects.filter(
Q(title__istartswith=term) | Q(content__icontains=term)
).annotate(
is_start=ExpressionWrapper(
Q(title__istartswith=term),
output_field=BooleanField()
)
).order_by('-is_start')

Arango query, collection with Edge count

Really new to Arango and I'm experimenting with it, so please bear with me. I have a feed collection and every feed can be liked by a user
[user]---likes_feed--->[feed].
I'm trying to create a query that will return a feed by its author, and add the number of likes to the result. This is what I have so far and it seems to work, but it only returns feed that have at least 1 like (An edge exists between the feed and a user)
below is my query
FOR f IN feed
SORT f.creationDate
FILTER f.author == #user_key
LIMIT #start_index, #end_index
FOR x IN INBOUND CONCAT('feed','/',f._key) likes_feed
OPTIONS {bfs: true, uniqueVertices: 'global'}
COLLECT feed = f WITH COUNT INTO counter
RETURN {
'feed':feed,
likes: counter
}
this is an example of result
[
"feed":{
"_key":"string",
"_id":"users_feed/1680835",
"_rev":"_W8zRPqe--_",
"author":"author_a",
"creationDate":"98879845467787979",
"title":"some title",
"caption":"some caption'
},
"likes":1
]
If a feed has no likes, no edge inbound to that feed, how do I return the likes count as 0
Something like this?
[
"feed":{
"_key":"string",
"_id":"users_feed/1680835",
"_rev":"_W8zRPqe--_",
"author":"author_a",
"creationDate":"98879845467787979",
"title":"some title",
"caption":"some caption'
},
"likes":0
]
So finally I found the solution. I had to create a graph and traverse it. Final result below
FOR f IN users_feed
SORT f.creationDate
FILTER f.author == #author_id
LIMIT #start_index, #end_index
COLLECT feed = f
LET inboundEdges = LENGTH(FOR v IN 1..1 INBOUND feed GRAPH 'likes_graph' RETURN 1)
RETURN {
feed :feed
,likes: inboundEdges
}

Ecto validate_required of either of two attributes

What's the best way to do a validation of the presence of either of two attributes for a changeset?
The validation should only fail if neither of the two attributes is provided
e.g. consider a User model with attributes :name, :uid, :email, :phone
:name always needs to be present
either :email or :uid should be present
:phone is optional
Would this work?
def changeset(struct, params \\ %{}) do
struct
|> cast(params, [:name,:email,:uid,:phone])
|> validate_required([:name, :email]) || validate_required([:name, :uid]
end
end
You may start with something like:
def changeset(struct, params \\ %{}) do
struct
|> cast(params, ~w(name email uid phone)a)
|> validate_required(:name)
|> validate_required_params(params)
end
end
def validate_required_params(changeset, %{"uid" => _}) do
changeset
|> validate_required(:uid)
end
def validate_required_params(changeset, %{"email" => _}) do
changeset
|> validate_required(:email)
end
def validate_required_params(changeset, _params) do
changeset
|> add_error(:params, "either :uid or :email is required")
end
But you need to also check for example if both uid and email are provided. What in such case?

Resources