Elixir recursion - recursion

I have a group, which I can attach to other modules. The group can have a parent group:
schema "groups" do
field :name, :string
field :deleted, :boolean
belongs_to :parent, Group
has_many :users_groups, UserGroup, foreign_key: :group_id
timestamps()
end
Via user_groups i Can attach users. Now I want to filter, if the user is allowed to see the attached module. I made a check, to see if the user is inside the attached group:
def get_visible_module(module, user_id) do
case module.group do
nil -> module
_ ->
case module.group.users_groups do
nil -> module
_ ->
val = Enum.filter(module.group.users_groups, fn(x)->
x.user_id == user_id
end)
case val do
[] ->
case false do
true -> module
false -> nil
end
_ -> module
end
end
end
end
This may be not the best code, but I am still learning, so improvements to this part are also welcome :)
Now my problem is to add a recursion to check, if the user_id is attached via user_group to a group, which is attached via the parent_id. I am stucked at this point. For the understanding: A module has a group attached. Only the user in the group or in the group attached via parent_id are allowed to see the module. Groups are structered as a tree, so I need to eager load the parent group and check if the user_group contains the user and check every parent-group too.
Hope it is understandable.
THX

Here is a rough skeleton which adapts the naming of your source code. It should give you an idea on how to make a recursion in elixir.
# Exit case when a module has no more parent
def get_visible_module(module, nil, user_id) do
user_in_groups?(module.group.users_groups, user_id)
end
# Case when the module has a parent_id
def get_visible_module(module, parent_id, user_id) do
# check the groups for user_id permission followed by the recusive part
user_in_groups?(module.group.users_groups, user_id) and get_visible_module(parent_module, parent_module.parent_id, user_id)
end
# checks if the user is in the group
defp user_in_groups?(users_groups, user_id) do
# check if the use is in one of the groups
true
end
As #bla already mentioned you should try to use pattern matching to clean up your code and reduce the nesting level of your code.

Related

How to Redirect to different pages using branch in Oracle Apex?

I have defined a function returning a URL(redirect).
branch
Page 1: Home-
Pre-Rendering-
Before Header-
Branches
With the following script:
declare
x number:=1;
myurl varchar2(255);
begin
if v('AGENCY') = 'R-AG' then
x := 3;
myurl := 'f?p=&APP_ID.:3:&SESSION.'; ---page 3
else
x := 50;
myurl := 'f?p=&APP_ID.:50:&SESSION.'; --- page 50
end if;
return myurl;
end;
I have defined item applications
Name : AGENCY
Scope : Application
Session State Protection: Unrestricted
I have defined an application computation
Sequence :10
Computation Item: AGENCY
Computacion Point: After
Authentication Computacion Type: SQL Query(return single value)
Computacion:
SELECT ROL
FROM USERS;
But for some reason the branch only took page 3 as priority and "else" is not fulfilled to redirect to page 50 when it is the case.
Your computation looks like something that should have a filter - how many rows return from a select from 'users' with no filter?
Though if there were any issues from that, I would have thought p50 would be the result.
You could also do this more declaratively. As in, have a branch to page 3 when item AGENCY = R-AG, and a second branch to p50 with no condition.
You first branch to p3 could also have just a query returning rows, such as
select null
from users
where username = :app_user -- or whatever your filter should probably be here
and rol = 'R-AG'

DynamoDB transaction

I have a Users table, with key {id: hash, email: range} and I want to create an user only if no user with the same email exists. I am trying to use a transaction, so I think I need to use a condition:
case Dynamo.transact_write_items([
{
:condition_check,
{
table_name(),
%{id: user_id, email: email},
condition_expression: "email <> :email",
expression_attribute_values: [email: email]
}
},
{
:put,
{
table_name(),
new_user
}
}
])
|> ExAws.request() do
{:ok, _} -> {:ok, new_user}
{:error, _} -> {:error, "unable to create user"}
end
This is not working, and I have the following questions:
in the key argument, in the condition, I have to specify: %{id: user_id, email: email}, but I have to specify user_id, which still does not exist, I have only generated it. How can I define a key with no value ?
I am having the following error: Transaction request cannot include multiple operations on one item, so I suppose that, given the way I have defined the key in the condition, it counts either the check and the put as two operations on the same entity. How can I check the existence of an entity in a consistent way (and so in a transaction) ?
The Elixir code here is of course not relevant, that's just the language I am using :)
According to the doc, no two actions can target the same item when using transaction. I don't think this is what you are looking for.
You can however, use conditional checks in regular operations. From the doc for instance,
aws dynamodb update-item \
--table-name ProductCatalog \
--key '{"Id": {"N": "456"}}' \
--update-expression "SET Price = Price - :discount" \
--condition-expression "Price > :limit" \
--expression-attribute-values file://values.json
will do the following:
get item with given ID
read the price and checks the condition against it
update accordingly if the condition is met
If the condition is not met, then the query will fail with a specific code.
note that update is actually an upsert
You must know beforehand the ID of the item you are looking for (actually, you must know the hash key. It could be an ID, a username, or whathever you have chosen)
Also, note that this is a unique query that is thus completely concurrent-safe.

Unable to return query output in Apex PL SQL expression

I am trying to write a following PL/SQL function body for a dynamic action
The purpose of dynamic action is to set value for text area based on input parameters. Way I am trying to do it, is that setting the value into variable for different options
declare
P_NOTE varchar(100); -- derive value
P_WEBSERVER varchar(100); -- derive name
begin
-- for getting the P_NOTE value
select distinct note into P_NOTE from port_mapping where PLATFORM = :P3_PLATFORM and VERSION = :P3_VERSION;
-- for getting web server value
select CONCAT(P_NOTE,CONCAT('https-',:P3_CLIENT)) into P_WEBSERVER from dual order by 1;
if (:P3_PLATFORM = 'Apache') then
return P_WEBSERVER;
end if;
end;
However I am getting error
ORA-06550: line 15, column 5:
PLS-00372: In a procedure, RETURN statement cannot contain an expression
ORA-06550: line 15, column 5:
PL/SQL: Statement ignored
declare
P_NOTE varchar(100);
P_WEBSERVER varchar(100);
I am not sure what I am missing.
(Since you did not post any apex version this explanation deals with version 4.2)
If this -is- a dynamic action and the code you posted is in a true action of type 'Execute PL/SQL Code' then you can not use RETURN. The plsql block is not a function body (close, Mr Kemp!).
If you want to return values from the session state to page items then you need to use the "Page Items to Return" item of the true action.
This will put the session state of the defined page items into the value of the item on the page. This means that you can not use any variable to just put stuff in to be able to return it to the page, but you need to use an actual page item (after all, these are bind variables).
To clarify further, you would not write :
return P_WEBSERVER;
But you'd have to use a page item, say P3_WEBSERVER, and you'll need to create one if it doesn't exist of course:
:P3_WEBSERVER := p_webserver;
Of course you'd need to make sure that the correct value will be in there as you can not shortcircuit as you did in your code sample (p_webserver will usually hold a value even if the platform is not 'Apache') eg:
if (:P3_PLATFORM = 'Apache') then
:P3_WEBSERVER := P_WEBSERVER;
else
:P3_WEBSERVER := NULL;
end if;
Just read error message:
line 15, column 5
So, trouble caused by this line:
return P_WEBSERVER;
return not allowed in PL/SQL blocks, use output parameter to return a value.
Read Tom's answer to find out how to do that.

Ora-00904 - Error with creating a View

I'm stuck with creating a view in Oracle, but before I create the view, I always test it first and I always got this error: Ora-00904.
This is the situation. I have this one Set of Query let say Query A that I need to combined using UNION ALL with the Query A itself with only few modifications applied to create another bigger Set of Query - Query B. The main constraint that keeps me on doing this is the Database Design, and I'm not in the position in the company to change it, so I have to adapt to it. Query A unions Query A for 6 times creating Query B. The additional Major constraint is Query B is from 1 database user only, but there are 54 database users with the same structures that I need to fetch the same query. Query B (db user1) unions Query B (db user2) unions Query B (db user3) and so on until 54 then finally creating Query C --- the final output. My scrip has already reached 6048 lines, then I got this problem that I don't get when I test Query A and Query B. All my table names, owner names, and column names are all correct but I got that error.
This is the code (that needs to be repeated for 54x6 times) - the Query A. Query B applies some similar modification only.:
Select
'2013' "YEAR",
Upper(a.text_month) "MONTH",
Upper('Budget') "VERSION",
case
when length(b.level1_name) > 5 then 'Parent'
else 'SUBSIDIARIES'
end "COMPANY_GROUP",
case
when length(b.level1_name) < 6 and b.level1_name <> '1000' then 'Subsidiaries'
else '1000 Parent'
end "COMPANY",
case
when length(b.level1_name) < 6 and b.level1_name <> '1000' then 'SUBS'
else '1000'
end "COMPANY_CODE",
case
when length(b.level1_name) > 5 then 'Parent'
else 'SUBSIDIARIES'
end "COMPANY_NAME",
b.level1_displayname "DIVISION",
b.level1_name "DIVISION_CODE",
case
when length(b.level1_name) > 5 then ltrim(upper(substr(b.level1_displayname, 8)))
else upper(ltrim(substr(b.level1_displayname, 10)))
end "DIVISION_NAME",
upper(a.text_nature_of_trip) "NATURE_OF_TRAVEL",
upper(a.text_placeeventstraining) "TRAVEL_DETAILS",
upper(a.text_country) "COUNTRY",
a.text_name_of_employee "EMPLOYEE_NAME", a.float_no_of_attendees "NO_OF_ATTENDEES",
a.text_sponsored "SPONSORED",
a.text_remarks "REMARKS",
'OTHER TRAVEL EXPENSES' "COST_ELEMENT",
a.FLOAT_702120005_OTHER_TRAVEL_E "AMOUNT"
From PUBLISH_PNL_AAAA_2013.et_travel_transaction a,
PUBLISH_PNL_AAAA_2013.cy_2elist b
Where a.elist = b.level3_iid
ORA-00904 is "invalid column name" -- either you've spelled the column name wrongly, or prefixed it with the wrong table alias, omitted quotes from a string literal, or any number of other issues.
Check the point in the code that the error message mentions for mistakes like that.

How to dynamically add content to email in PL/SQL?

I'm using Oracle apex and am trying to practice with automatic emails. Ideally, this is how the scenario would go: A user can 'recommend' games to friends through his wishlist. The user selects the game(s) they would like to recommend though a checkbox, then selects the friends and email content. The code I have in place for this is as follows:
DECLARE
content VARCHAR2(4000) := :P4_EMAIL || 'Here are the game(s):';
game VARCHAR2(100);
i NUMBER := 1;
/*Declare the cursor and it's query */
cursor CURSOR IS
SELECT name
from gs_games
where game_id = APEX_APPLICATION.G_F01(i)
FOR UPDATE;
BEGIN
FOR i in 1..APEX_APPLICATION.G_F01.count
LOOP
OPEN cursor;
FETCH cursor INTO game;
CLOSE cursor;
END LOOP;
htmldb_mail.Send(p_to => :P4_FRIENDS,
p_from => 'gametracker#gametracker.com',
p_subj => 'Game recommendations from ' || :F56_USER_NAME,
p_body => content || ' ' || game);
END;
This only partially works. For a single selection, everyone works perfectly, but once the user selects multiple games, then the email only contains the name of the first game checked off, instead of every game as it should. I realize this is tied to the way I've got my cursor set up, but I'm not entirely sure how I can use it while keeping that for loop active. Does anyone have any ideas? Thank you.
The immediate problem is that every time you fetch the next row from the cursor into your local variable game, you are overwriting the prior value of that variable. Once you've fetched every row from the cursor, game will have the value of whatever the last row you processed was.
Assuming that you want your email to contain a comma-separated list of game names, you could do something like
-- You probably want to create this function inside of a package that provides other methods
-- for interacting with games
CREATE OR REPLACE FUNCTION get_game_name( p_game_id IN gs_games.game_id%type )
RETURN gs_games.name%type
IS
l_name gs_games.name%type;
BEGIN
SELECT name
INTO l_name
FROM gs_games
WHERE game_id = p_game_id;
RETURN l_name;
END get_game_name;
DECLARE
l_content VARCHAR2(4000) := :P4_EMAIL || 'Here are the game(s):';
l_game_list VARCHAR2(100);
BEGIN
FOR i in 1..APEX_APPLICATION.G_F01.count
LOOP
l_game_list := l_game_list || ', ' || get_game_name( APEX_APPLICATION.G_F01(i) );
END LOOP;
l_game_list := LTRIM( ', ' );
apex_mail.send( p_to => :P4_FRIENDS,
p_from => 'gametracker#gametracker.com',
p_subj => 'Game recommendations from ' || :F56_USER_NAME,
p_body => l_content || ' ' || l_game_list);
END;
A few notes about style
Naming a cursor CURSOR is problematic and should be avoided at all costs. If you do need to declare a cursor, you really ought to give it a meaningful name.
Local variables generally ought to be named in such a way that differentiates them from the names of columns in tables (in my case, I use a l_ prefix for local variables and a p_ prefix for parameters though there are many different valid conventions. This is important in PL/SQL because identifiers in SQL statements are resolved first using the names of columns in the table and then using local variables. That makes it far too easy to inadvertently write code that uses a column when you meant for it to use a local variable if you don't have some convention to make it clear which you're using.
You don't want to use a cursor to select a single row of data. If you have a query that you know should return exactly 1 row, use a SELECT INTO.
I created a separate function to get the name for a particular game_id because that maximizes reuse-- there will likely be many other places that you need to do something similar so you want to write that code once and use it many times. It would be perfectly legal to put the SELECT INTO into the loop in your anonymous PL/SQL block, it would be preferable to factor that code out into a function.
Assuming you are using a vaguely recent version of APEX, you should be using the apex_mail package not the old htmldb_mail package. There shouldn't be any functional difference between the two but HTML DB was the old name for Oracle APEX many releases ago so it's a good idea to phase out the use of the old package names.

Resources