From b84356e3ec641c8fb156ede6bc9d3477ce3ff866 Mon Sep 17 00:00:00 2001
From: Sinisa Veseli <sveseli@aps.anl.gov>
Date: Tue, 28 Feb 2017 16:02:06 +0000
Subject: [PATCH] added ability to add users from proposal, or to specify them
 directly to add experiment command

---
 src/python/dm/aps_bss/api/apsBssApi.py        |  6 +-
 src/python/dm/aps_bss/cli/getProposalCli.py   |  2 +-
 src/python/dm/common/client/sessionManager.py |  6 +-
 .../dm/common/utility/noopPlatformUtility.py  | 56 +++++++++++++
 .../dm/ds_web_service/cli/addExperimentCli.py | 83 ++++++++++++++++++-
 5 files changed, 144 insertions(+), 9 deletions(-)
 create mode 100755 src/python/dm/common/utility/noopPlatformUtility.py

diff --git a/src/python/dm/aps_bss/api/apsBssApi.py b/src/python/dm/aps_bss/api/apsBssApi.py
index fb87f28c..11e4f934 100755
--- a/src/python/dm/aps_bss/api/apsBssApi.py
+++ b/src/python/dm/aps_bss/api/apsBssApi.py
@@ -21,7 +21,7 @@ class ApsBssApi(DmApi):
         :type password: str
 
         :param loginFile: BSS login file (not used if username/password are provided; it can be replaced by environment variable DM_BSS_LOGIN_FILE)
-        :type password: str
+        :type loginFile: str
 
         :raises ConfigurationError: in case beamline name or username/password are not provided, and corresponding environment variables are not set
 
@@ -108,8 +108,8 @@ class ApsBssApi(DmApi):
         '''
         Get beamline proposal with a given id.
 
-        :param porposalId: proposal id
-        :type runName: str
+        :param proposalId: proposal id
+        :type proposalId: str
 
         :param runName: run name (if not provided, current run name will be used)
         :type runName: str
diff --git a/src/python/dm/aps_bss/cli/getProposalCli.py b/src/python/dm/aps_bss/cli/getProposalCli.py
index ad13b645..b2f215c4 100755
--- a/src/python/dm/aps_bss/cli/getProposalCli.py
+++ b/src/python/dm/aps_bss/cli/getProposalCli.py
@@ -9,7 +9,7 @@ class GetProposalCli(DmCli):
         DmCli.__init__(self)
         self.addOption('', '--id', dest='proposalId', help='Proposal id.')
         self.addOption('', '--run', dest='runName', help='Run name. If not provided, current run will be used.')
-        self.addOption('', '--login-file', dest='loginFile', help='BSS login file. It may be provided via environment variable DM_BSS_LOGIN_FILE.')
+        self.addOption('', '--login-file', dest='loginFile', help='BSS login file. Login file  may also be specified via environment variable DM_BSS_LOGIN_FILE.')
 
     def checkArgs(self):
         if not self.options.proposalId:
diff --git a/src/python/dm/common/client/sessionManager.py b/src/python/dm/common/client/sessionManager.py
index 2e2dbc69..6becaa80 100755
--- a/src/python/dm/common/client/sessionManager.py
+++ b/src/python/dm/common/client/sessionManager.py
@@ -56,7 +56,9 @@ class SessionManager:
         try:
             # User will be asked for username/password if they are not
             # provided.
-            data = { 'username' : self.getUsername(username), 'password' : self.getPassword(password) }
+            self.username = self.getUsername(username)
+            self.password = self.getPassword(password)
+            data = { 'username' : self.username, 'password' : self.password }
             self.logger.debug('Establishing session for user %s @ %s)' % (username, url))
             (response,responseData) = self.sendRequest(url='%s%s' % (url, selector), method='POST', contentType='application/x-www-form-urlencoded', data=urllib.urlencode(data))
         except urllib2.URLError, ex:
@@ -66,7 +68,7 @@ class SessionManager:
         #self.logger.debug('Got headers: %s' % response.headers)
         # This will save session cookie.
         self.sessionCookie = self.checkResponseHeadersForErrorsAndSaveSession(response.headers)
-        self.logger.debug('User %s session cookie: %s' % (username, self.sessionCookie))
+        self.logger.debug('User %s session cookie: %s' % (self.username, self.sessionCookie))
 
     def getUsername(self, username):
         if username is None and self.requireSessionCredentials:
diff --git a/src/python/dm/common/utility/noopPlatformUtility.py b/src/python/dm/common/utility/noopPlatformUtility.py
new file mode 100755
index 00000000..c7387b11
--- /dev/null
+++ b/src/python/dm/common/utility/noopPlatformUtility.py
@@ -0,0 +1,56 @@
+#!/usr/bin/env python
+
+from dm.common.utility.loggingManager import LoggingManager
+
+class NoopPlatformUtility:
+
+    def __init__(self):
+        self.logger = LoggingManager.getInstance().getLogger(self.__class__.__name__)
+
+    def createGroup(self, name):
+        self.logger.debug('createGroup called for %s' % name)
+
+    def addUserToGroup(self, username, groupName):
+        self.logger.debug('addUserToGroup called for %s, %s' % (username, groupName))
+
+    def deleteUserFromGroup(self, username, groupName):
+        self.logger.debug('deleteUserFromGroup called for %s, %s' % (username, groupName))
+
+    def createLocalGroup(self, name):
+        self.logger.debug('createLocalGroup called for %s' % name)
+
+    def addLocalUserToGroup(self, username, groupName):
+        self.logger.debug('addLocalUserToGroup called for %s, %s' % (username, groupName))
+
+    def deleteLocalUserFromGroup(self, username, groupName):
+        self.logger.debug('adddeleteLocalUserFromGroup called for %s, %s' % (username, groupName))
+
+    def getGroupInfo(self, groupName):
+        self.logger.debug('getGroupInfo called for %s' % name)
+
+    def setGroupUsers(self, groupName, usernameList):
+        self.logger.debug('setGroupUsers called for %s, %s' % (groupName, usernameList))
+
+    def setPathReadExecutePermissionsForGroup(self, path, groupName):
+        self.logger.debug('setPathReadExecutePermissionsForGroup called for %s, %s' % (path, groupName))
+
+    def changePathGroupOwner(self, path, groupName):
+        self.logger.debug('changePathGroupOwner called for %s, %s' % (path, groupName))
+
+    def recursivelyChangePathGroupOwner(self, path, groupName):
+        self.logger.debug('recursivelyChangePathGroupOwner called for %s, %s' % (path, groupName))
+
+    def refreshNscdGroupCache(self):
+        self.logger.debug('refreshNscdGroupCache called')
+
+    def refreshAuthFiles(self):
+        self.logger.debug('refreshAuthFiles called')
+
+    def chmodPathForFilesInDirectory(self, directoryPath, fileMode):
+        self.logger.debug('chmodPathForFilesInDirectory called for %s, %s' % (directoryPath, fileMode))
+
+#######################################################################
+# Testing.
+
+if __name__ == '__main__':
+    pass
diff --git a/src/python/dm/ds_web_service/cli/addExperimentCli.py b/src/python/dm/ds_web_service/cli/addExperimentCli.py
index f509231d..567b96e9 100755
--- a/src/python/dm/ds_web_service/cli/addExperimentCli.py
+++ b/src/python/dm/ds_web_service/cli/addExperimentCli.py
@@ -1,6 +1,9 @@
 #!/usr/bin/env python
 
+from dm.aps_bss.api.apsBssApi import ApsBssApi
 from dm.ds_web_service.api.experimentRestApi import ExperimentRestApi
+from dm.ds_web_service.api.userRestApi import UserRestApi
+
 from dm.common.exceptions.invalidRequest import InvalidRequest
 from dm.common.utility.configurationManager import ConfigurationManager
 from dsWebServiceSessionCli import DsWebServiceSessionCli
@@ -22,6 +25,10 @@ class AddExperimentCli(DsWebServiceSessionCli):
         self.addOption('', '--description', dest='description', help='Experiment description.')
         self.addOption('', '--start-date', dest='startDate', help='Experiment start date in format DD-MMM-YY.')
         self.addOption('', '--end-date', dest='endDate', help='Experiment end date in format DD-MMM-YY.')
+        self.addOption('', '--users', dest='users', help='Comma-separated list of DM usernames to be added to the new experiment as users.')
+        self.addOption('', '--proposal-id', dest='proposalId', help='Beamline proposal id. If specified, all users listed on the proposal will be added to the new experiment.')
+        self.addOption('', '--run', dest='runName', help='Run name. If not specified, current run name is assumed for beamline proposal.')
+        self.addOption('', '--bss-login-file', dest='bssLoginFile', help='BSS login file. Login file may also be specified via environment variable DM_BSS_LOGIN_FILE.')
 
     def checkArgs(self):
         if self.options.experimentName is None:
@@ -64,6 +71,22 @@ class AddExperimentCli(DsWebServiceSessionCli):
     def getEndDate(self):
         return self.options.endDate
 
+    def getProposalId(self):
+        proposalId = self.options.proposalId
+        if proposalId:
+            proposalId = int(proposalId)
+        return proposalId
+
+    def getUsers(self):
+        users = self.options.users
+        if users:
+            # Remove duplicates by converting into set
+            users = list(set(users.split(',')))
+            
+        else:
+            users = []
+        return users
+
     def runCommand(self):
         self.parseArgs(usage="""
     dm-add-experiment 
@@ -71,13 +94,67 @@ class AddExperimentCli(DsWebServiceSessionCli):
         [--description=DESCRIPTION] 
         [--start-date=STARTDATE] 
         [--end-date=ENDDATE]
+        [--users=USERS]
+        [--proposal-id=PROPOSALID]
+        [--run=RUNNAME]
+        [--bss-login-file=BSSLOGINFILE]
 
 Description:
-    Add new experiment to the DM database. 
+    Add new experiment to the DM database. If list of users or proposal id is specified, this command will also add roles for all users listed on the proposal.
         """)
         self.checkArgs()
-        api = ExperimentRestApi(self.getLoginUsername(), self.getLoginPassword(), self.getServiceHost(), self.getServicePort(), self.getServiceProtocol())
-        experiment = api.addExperiment(self.getExperimentName(), self.getStationName(), self.getTypeName(), self.getDescription(), self.getStartDate(), self.getEndDate())
+        dsExperimentApi = ExperimentRestApi(self.getLoginUsername(), self.getLoginPassword(), self.getServiceHost(), self.getServicePort(), self.getServiceProtocol())
+        dsUserApi = UserRestApi(self.getLoginUsername(), self.getLoginPassword(), self.getServiceHost(), self.getServicePort(), self.getServiceProtocol())
+
+        description = self.getDescription()
+        proposalId = self.getProposalId()
+        experimenters = []
+        if proposalId:
+            bssApi = ApsBssApi(loginFile=self.options.bssLoginFile)
+            proposal = bssApi.getBeamlineProposal(proposalId=proposalId, runName=self.options.runName)
+            experimenters = proposal.get('experimenters', [])
+            if not description:
+                description = '%s (Proposal id: %s)' % (proposal['title'], proposalId)
+
+
+        users = self.getUsers()
+        pis = []
+        for experimenter in experimenters:
+            badge = int(experimenter['badge'])
+            if not badge:
+                #print 'Skipping user %s due to invalid badge.' % lastName 
+                continue
+            username = 'd%s' % badge
+
+            # Clasify user
+            if experimenter.get('piFlag') == 'Y':
+                if not pis.count(username):
+                    pis.append(username)
+                if users.count(username):
+                    users.remove(username)
+            else:
+                if not users.count(username):
+                    users.append(username)
+
+        for username in users+pis:
+            # Check that user exists
+            dsUserApi.getUserByUsername(username)
+           
+
+        # Everything looks good, add experiment and users
+        experiment = dsExperimentApi.addExperiment(self.getExperimentName(), self.getStationName(), self.getTypeName(), description, self.getStartDate(), self.getEndDate())
+
+        # Add pis.
+        experimentName = experiment['name']
+        roleName = 'PI'
+        for username in pis:
+            dsUserApi.addUserExperimentRole(username, roleName, experimentName)
+        roleName = 'User'
+        for username in users:
+            dsUserApi.addUserExperimentRole(username, roleName, experimentName)
+
+        if len(users+pis):
+            experiment = dsExperimentApi.getExperimentByName(experimentName)
         print experiment.getDisplayString(self.getDisplayKeys(), self.getDisplayFormat())
 
 #######################################################################
-- 
GitLab