SAS script to list all SAS server users from metadata - excel

I need a way to import a list of all SAS users from SAS metadata into an Excel worksheet. I was considering doing this using the SAS plugin for Microsoft Office to create a dynamic data source the retrieves the list of users dynamically from the SAS server. If I am to do this I need to know how to do this in SAS code.
Does anyone know how I would write a SAS script to display a list of all users in SAS metadata, or if this is even possible?
I've been trying to find something online but haven't had any luck.
I have full administrator privileges, so no problem there.
Thanks!

Thanks to Joe in the comments I found the answer I need:
http://support.sas.com/documentation/cdl/en/lrmeta/63180/HTML/default/viewer.htm#p1k9zipe59ha2an1pq34gu143lay.htm
I used the very last example in this page, modified to do a PROC PRINT instead of exporting to an Excel sheet. In Enterprise Guide I created a new program as follows:
/*Connect to the metadata server using the metadata system options as
shown in the first example. */
data work.Identities;
/* The LENGTH statement defines the lengths of variables for function arguments. */
length IdentId IdentName DispName ExtLogin IntLogin DomainName $32
uri uri2 uri3 uri4 $256;
/* The LABEL statement assigns descriptive labels to variables. */
label
IdentId = "Identity Id"
IdentName = "Identity Name"
DispName = "Display Name"
ExtLogin = "External Login"
IntLogin = "Is Account Internal?"
DomainName = "Authentication Domain";
/* The CALL MISSING statement initializes the output variables to missing values. */
call missing(IdentId, IdentName, DispName, ExtLogin, IntLogin, DomainName,
uri, uri2, uri3, uri4);
n=1;
n2=1;
/* The METADATA_GETNOBJ function specifies to get the Person objects in the repository.
The n argument specifies to get the first person object that is returned.
The uri argument will return the actual uri of the Person object. The program prints an
informational message if no objects are found. */
rc=metadata_getnobj("omsobj:Person?#Id contains '.'",n,uri);
if rc<=0 then put "NOTE: rc=" rc
"There are no identities defined in this repository"
" or there was an error reading the repository.";
/* The DO statement specifies a group of statements to be executed as a unit.
The METADATA_GETATTR function gets the values of the Person object's Id, Name,
and DisplayName attributes. */
do while(rc>0);
objrc=metadata_getattr(uri,"Id",IdentId);
objrc=metadata_getattr(uri,"Name",IdentName);
objrc=metadata_getattr(uri,"DisplayName",DispName);
/* The METADATA_GETNASN function gets objects associated via the
InternalLoginInfo association. The InternalLoginInfo association returns
internal logins. The n2 argument specifies to return the first associated object
for that association name. The URI of the associated object is returned in
the uri2 variable. */
objrc=metadata_getnasn(uri,"InternalLoginInfo",n2,uri2);
/* If a Person does not have any internal logins, set their IntLogin
variable to 'No' Otherwise, set to 'Yes'. */
IntLogin="Yes";
DomainName="**None**";
if objrc<=0 then
do;
put "NOTE: There are no internal Logins defined for " IdentName +(-1)".";
IntLogin="No";
end;
/* The METADATA_GETNASN function gets objects associated via the Logins association.
The Logins association returns external logins. The n2 argument specifies to return
the first associated object for that association name. The URI of the associated
object is returned in the uri3 variable. */
objrc=metadata_getnasn(uri,"Logins",n2,uri3);
/* If a Person does not have any logins, set their ExtLogin
variable to '**None**' and output their name. */
if objrc<=0 then
do;
put "NOTE: There are no external Logins defined for " IdentName +(-1)".";
ExtLogin="**None**";
output;
end;
/* If a Person has many logins, loop through the list and retrieve the name of
each login. */
do while(objrc>0);
objrc=metadata_getattr(uri3,"UserID",ExtLogin);
/* If a Login is associated to an authentication domain, get the domain name. */
DomainName="**None**";
objrc2=metadata_getnasn(uri3,"Domain",1,uri4);
if objrc2 >0 then
do;
objrc2=metadata_getattr(uri4,"Name",DomainName);
end;
/*Output the record. */
output;
n2+1;
/* Retrieve the next Login's information */
objrc=metadata_getnasn(uri,"Logins",n2,uri3);
end; /*do while objrc*/
/* Retrieve the next Person's information */
n+1;
n2=1;
rc=metadata_getnobj("omsobj:Person?#Id contains '.'",n,uri);
end; /*do while rc*/
/* The KEEP statement specifies the variables to include in the output data set. */
keep IdentId IdentName DispName ExtLogin IntLogin DomainName;
run;
/* The PROC PRINT statement writes a basic listing of the data. */
proc print data=work.Identities label;
run;
/* The PROC EXPORT statement can be used to write the data to an Excel spreadsheet. */
/* Change DATA= to the data set name you specified above. */
/* Change OUTFILE= to an appropriate path for your system. */
/*
proc export data=work.Identities
dbms=EXCE
outfile="C:\temp\Identities.xls"
replace;
run;
*/
PROC PRINT DATA=work.Identities;
When this was executed it created a SAS Report. I exported that Report as a .srx file and then used the SAS Plugin for Microsoft Office to add the report into an Excel worksheet (the "Reports" button).
I then right-clicked on the cell where the report was added and clicked Properties, and then set it to automatically update whenever the document is opened.
It's a great way to review users as an administrator. Rather than having to check each system individually to see if a user exists (when they leave the company for example) I have a sheet for each of our SAS systems, a sheet for each of our Teradata systems (auto-updated using a query run through ODBC), and another sheet auto-updated from a separate spreadsheet that contains the list of our MicroStrategy users. It makes checking all systems as simple as a single Ctrl + F.

If you just want to extract the list of users in SAS you can compile this macro and run:
%mm_getusers(outds=myusers)
Disclaimer - I wrote it, and we use it in our commercial product (https://datacontroller.io) so users can see group memberships when applying permissions within the tool.

Related

How to display default value from prompt macro in value prompt CA11?

I was wondering if it is possible to display the default value from a prompt macro in a Value prompt. My prompt macro looks like this "#prompt('pMonth','MUN','[Previous Month]')#"
so my goal in the value prompt would be to have 202103 displayed instead of header text name which I have named "Previous Month"
I tried with an old javascript from Cognos 10 where you desc the Months and specify what index it should pick but the issue with that code is that everytime you try to change to a different month the report re-runs and loops back to to the same Index value.
<script>
var theSpan = document.getElementById("A1");
var a = theSpan.getElementsByTagName("select"); /* This stmt return an array of all value prompts within span */
for( var i = a.length-1; i >= 0; i-- ) /* now loop through the elements */
{ var prompts = a[i];
if( prompts.id.match(/PRMT_SV_/))
{ prompts.selectedIndex = 2; } /* This selects the second options from top */
canSubmitPrompt();
}
</script>
All solutions, tips and ideas are highly appreciated.
Best regards,
Rubrix
For Cognos Analytics, running with full interactivity, you probably need a Page Module. Check out IBM's Scriptable Reports documentation for Cognos Analytics. You'll want to craft your script to use the current parameter value as default (if you can get it), then fail over to your default value from the data. You will probably need to integrate this with a Custom Control to be able to get that default value from the data.

Android Studio Room query to get a random row of db and saving the rows 2nd column in variable

like the title mentions I want a Query that gets a random row of the existing database. After that I want to save the data which is in a specific column of that row in a variable for further purposes.
The query I have at the moment is as follows:
#Query("SELECT * FROM data_table ORDER BY RANDOM() LIMIT 1")
fun getRandomRow()
For now I am not sure if this query even works, but how would I go about writing my function to pass a specific column of that randomly selected row to a variable?
Ty for your advice, tips and/or solutions!
Your query is almost correct; however, you should specify a return type in the function signature. For example, if the records in the data_table table are mapped using a data class called DataEntry, then the query could read as shown below (note I've also added the suspend modifier so the query must be run using a coroutine):
#Query("SELECT * FROM data_table ORDER BY RANDOM() LIMIT 1")
suspend fun getRandomRow(): DataEntry?
If your application interacts with the database via a repository and view model (as described here: https://developer.android.com/topic/libraries/architecture/livedata) then the relevant methods would be along the lines of:
DataRepository
suspend fun findRandomEntry(): DataEntry? = dataEntryDao.getRandomRow()
DataViewModel
fun getRandomRecord() = viewModelScope.launch(Dispatchers.IO) {
val entry: DataEntry? = dataRepository.findRandomEntry()
entry?.let {
// You could assign a field of the DataEntry record to a variable here
// e.g. val name = entry.name
}
}
The above code uses the view model's coroutine scope to query the database via the repository and retrieve a random DataEntry record. Providing the returning DataEntry record is not null (i.e. your database contains data) then you could assign the fields of the DataEntry object to variables in the let block of the getRandomRecord() method.
As a final point, if it's only one field that you need, you could specify this in the database query. For example, imagine the DataEntry data class has a String field called name. You could retrieve this bit of information only and ignore the other fields by restructuring your query as follows:
#Query("SELECT name FROM data_table ORDER BY RANDOM() LIMIT 1")
suspend fun getRandomRow(): String?
If you go for the above option, remember to refactor your repository and view model to expect a String instead of a DataEntry object.

SDSF ST via REXX

I've got an interesting idea. I want to see JCL in SDSF via REXX.
Currently I can see necessary job names, using:
Address SDSF "ISFEXEC ST"
Maybe anybody has some idea of what to add to my script to make analog of:
command s
And totally get the same output
Using SDSF with the REXX programming language - Examples of REXX execs, as per the comment, has example code such as:-
List action characters
Set the ISFACTIONS special variable to ON, which causes the action characters to be returned in the ISFRESP variables. Then access the ST panel, and list the valid action characters for that panel.
/* REXX */
rc=isfcalls('ON')
/* Set isfactions special variable to */
/* the equivalent of SET ACTION ON */
isfactions="ON"
/* Invoke the ST panel */
Address SDSF "ISFEXEC ST"
if rc<>0 then
Exit rc
/* List each of the valid action characters */
/* for the panel. */
Say "Actions valid on the panel are:"
do ix=1 to isfresp.0
Say " " isfresp.ix
end
rc=isfcalls('OFF')
and
Access an SDSF panel
Access the ST panel, then list the column variables.
/* REXX */
rc=isfcalls('ON')
/* Access the ST panel */
Address SDSF "ISFEXEC ST"
if rc<>0 then
Exit rc
/* Get fixed field name from first word */
/* of isfcols special variable */
fixedField = word(isfcols,1)
Say "Number of rows returned:" isfrows
/* Process all rows */
do ix=1 to isfrows
Say "Now processing job:" value(fixedField"."ix)
/* List all columns for row */
do jx=1 to words(isfcols)
col = word(isfcols,jx)
Say " Column" col"."ix "has the value:" value(col"."ix)
end
end
rc=isfcalls('OFF')
An IBM Red Book, a PDF Downlaod, Implementing Rexx Support in SDSF may also be of use.

Java Agent in Domino designer not signed after import

I imported a java-agent into an iNotes application using DXLImporter. The agent seems to be signed using my User Id. When imported into the host application, I am receiving the fowllowing error in domino designer:
Could not open the editor: Error - Document is not signed..
I tried to configure the agents before exporting them as DXL. I also configured my DXLImporter like this:
dip.ReplaceDBProperties = False
'Don't import any documents
dip.DocumentImportOption = DXLIMPORTOPTION_IGNORE
dip.ReplicaRequiredForReplaceOrUpdate = False
'Use ACL of host DB
dip.ACLImportOption = DXLIMPORTOPTION_IGNORE
dip.DesignImportOption = DXLIMPORTOPTION_REPLACE_ELSE_CREATE
I would like to ignore my signature or to programmatically sign the stuff with the User Id of the host application if this is possible.
You can easily use the sign method of NotesDatabase- Class to sign the whole database, or just some specific elements or even one specific element. Check out this link for the designer help for is method.
Here is an excerpt of the linked site:
Call notesDatabase.Sign( [ documentType% ] [ , existingSigsOnly ] [ , nameStr$] [ , nameStrIsNoteID ] )
Parameters
documentType%
Integer. Optional. One of the following constants.
DBSIGN_DOC_ACL (64) signs the ACL
DBSIGN_DOC_AGENT (512) signs all agents
DBSIGN_DOC_ALL (32767) (default) signs all elements except data documents' active content
DBSIGN_DOC_DATA (1) signs all data documents' active content (hotspots)
DBSIGN_DOC_FORM (4) signs all forms
DBSIGN_DOC_HELP (256) signs the "About Database" and "Using Database" documents
DBSIGN_DOC_ICON (16) signs the icon
DBSIGN_DOC_REPLFORMULA (2048) signs the replication formula
DBSIGN_DOC_SHAREDFIELD (1024) signs all shared fields
DBSIGN_DOC_VIEW (8) signs all views
existingSigsOnly
Boolean. Optional.
True to sign only elements with existing signatures.
False (default) to sign all elements.
nameStr
String. Optional. Programmatic name or note ID of a single design element. If this parameter is not specified, all design elements of type parameter 1 are signed.
nameStrIsNoteID
Boolean. Optional.
True if parameter 3 represents a note ID.
False (default) if parameter 3 represents a programmatic name.
For your example it would be something like:
call db.sign(DBSIGN_DOC_AGENT, False, "NameOfYourAgent", False)

Retrieving Properties from DbSqlQuery

Background: Project is a Data Import utility for importing data from tsv files into a EF5 DB through DbContext.
Problem: I need to do a lookup for ForeignKeys while doing the import. I have a way to do that but the retrieval if the ID is not functioning.
So I have a TSV file example will be
Code Name MyFKTableId
codevalue namevalue select * from MyFKTable where Code = 'SE'
So when I process the file and Find a '...Id' column I know I need to do a lookup to find the FK The '...' is always the entity type so this is super simple. The problem I have is that I don't have access to the properties of the results of foundEntity
string childEntity = column.Substring(0, column.Length - 2);
DbEntityEntry recordType = myContext.Entry(childEntity.GetEntityOfReflectedType());
DbSqlQuery foundEntity = myContext.Set(recordType.Entity.GetType()).SqlQuery(dr[column])
Any suggestion would be appreciated. I need to keep this generic so we can't use known type casting. The Id Property accessible from IBaseEntity so I can cast that, but all other entity types must be not be fixed
Note: The SQL in the MyFKTableId value is not a requirement. If there is a better option allowing to get away from SqlQuery() I would be open to suggestions.
SOLVED:
Ok What I did was create a Class called IdClass that only has a Guid Property for Id. Modified my sql to only return the Id. Then implemented the SqlQuery(sql) call on the Database rather than the Set([Type]).SqlQuery(sql) like so.
IdClass x = ImportFactory.AuthoringContext.Database.SqlQuery<IdClass>(sql).FirstOrDefault();
SOLVED:
Ok What I did was create a Class called IdClass that only has a Guid Property for Id. Modified my sql to only return the Id. Then implemented the SqlQuery(sql) call on the Database rather than the Set([Type]).SqlQuery(sql) like so.
IdClass x = ImportFactory.AuthoringContext.Database.SqlQuery<IdClass>(sql).FirstOrDefault();

Resources