def inferRelationships(person):
    from girderformindlogger.models.invitation import Invitation
    from girderformindlogger.models.profile import Profile

    if "schema:knows" not in person:
        return (person)
    start = deepcopy(person)
    for rel in list(person['schema:knows'].keys()):
        if rel in DEFINED_RELATIONS.keys():
            if "owl:equivalentProperty" in DEFINED_RELATIONS[rel]:
                for ep in DEFINED_RELATIONS[rel]["owl:equivalentProperty"]:
                    if ep not in person['schema:knows']:
                        person['schema:knows'][ep] = []
                    for related in person['schema:knows'][rel]:
                        if related not in person['schema:knows'][ep]:
                            person['schema:knows'][ep].append(related)
    if any([
            bool(rp not in start.get('schema:knows', {}).get(rel, []))
            for rp in person['schema:knows'][rel]
            for rel in list(person['schema:knows'].keys())
    ]):
        newPerson = Profile().load(person['_id'], force=True)
        if 'schema:knows' in newPerson:
            newPerson['schema:knows'].update(person['schema:knows'])
        else:
            newPerson['schema:knows'] = person['schema:knows']
        Profile().save(
            newPerson,
            validate=False) if 'userId' in newPerson else Invitation().save(
                newPerson, validate=False)
    return (person)
Ejemplo n.º 2
0
 def listUsers(self, applet, role, user=None, force=False):
     from .profile import Profile
     if not force:
         if not any([
                 self.isCoordinator(applet['_id'], user),
                 self._hasRole(applet['_id'], user, 'reviewer')
         ]):
             return ([])
     userlist = {
         p['_id']: Profile().display(p, role)
         for p in list(Profile().find({
             'appletId': applet['_id'],
             'userId': {
                 '$in': [
                     user['_id'] for user in list(UserModel().find({
                         'groups': {
                             '$in': [
                                 ObjectId(group)
                                 for group in self.getAppletGroups(
                                     applet).get(role, {}).keys()
                             ]
                         }
                     }))
                 ]
             }
         }))
     }
     return (userlist)
Ejemplo n.º 3
0
    def deactivateApplet(self, folder):
        from girderformindlogger.models.profile import Profile

        applet = folder
        user = Applet().getCurrentUser()
        applet['meta']['applet']['deleted'] = True
        applet = AppletModel().setMetadata(applet, applet.get('meta'), user)
        if applet.get('meta', {}).get('applet', {}).get('deleted') == True:
            message = 'Successfully deactivated applet {} ({}).'.format(
                AppletModel().preferredName(applet), applet.get('_id'))

            users = list(Profile().find(
                {
                    'appletId': ObjectId(applet['_id']),
                    'deactivated': {
                        '$ne': True
                    }
                },
                fields=['userId']))
            Profile().deactivateProfile(applet['_id'], None)

            for user in users:
                if 'userId' in user:
                    UserModel().removeApplet(
                        UserModel().findOne({'_id': ObjectId(user['userId'])}),
                        applet['_id'])
        else:
            message = 'Could not deactivate applet {} ({}).'.format(
                AppletModel().preferredName(applet), applet.get('_id'))
            Description().errorResponse(message, 403)
        return (message)
Ejemplo n.º 4
0
 def removeCode(self, profileId, code):
     from girderformindlogger.models.profile import Profile
     idCode = self.findOne({'profileId': ObjectId(profileId), 'code': code})
     if idCode is not None:
         self.remove(idCode)
     if not len(self.findIdCodes(profileId)):
         self.createIdCode(Profile().load(profileId, force=True))
     return (Profile().load(profileId, force=True))
Ejemplo n.º 5
0
    def getAppletUsers(self, applet, user=None, force=False):
        """
        Function to return a list of Applet Users

        :param applet: Applet to get users for.
        :type applet: dict
        :param user: User making request
        :type user: dict
        :returns: list of dicts
        """
        from .invitation import Invitation
        from .profile import Profile

        profileFields = ["_id", "coordinatorDefined", "userDefined"]

        try:

            if not isinstance(user, dict):
                user = UserModel().load(
                    id=user, level=AccessType.READ, force=True) if isinstance(
                        user, str) else {}

            if not force:
                if not self.isCoordinator(applet.get('_id', applet), user):
                    return ([])

            userDict = {
                'active': [
                    Profile().displayProfileFields(p, user, forceManager=True)
                    for p in list(Profile().find(
                        query={'appletId': applet['_id']}))
                ],
                'pending': [
                    Profile().displayProfileFields(p, user, forceManager=True)
                    for p in list(Invitation().find(
                        query={'appletId': applet['_id']}))
                ]
            }

            missing = threading.Thread(target=Profile().generateMissing,
                                       args=(applet, ))
            missing.start()

            if len(userDict['active']):
                return (userDict)

            else:
                return ({**userDict, "message": "cache updating"})
        except:
            import sys, traceback
            print(sys.exc_info())
            return ({traceback.print_tb(sys.exc_info()[2])})
    def filterScheduleEvents(self,
                             schedule,
                             user,
                             filterRequired,
                             profilesCache={}):
        from girderformindlogger.models.profile import Profile

        if filterRequired and 'events' in schedule:
            events = []

            for event in schedule['events']:
                notForCurrentUser = '******' in event

                if 'users' in event:
                    for appletUser in event['users']:
                        if appletUser not in profilesCache:
                            userData = Profile().findOne(
                                query={'_id': ObjectId(appletUser)},
                                fields=['userId'])

                            if userData and 'userId' in userData:
                                profilesCache[appletUser] = userData['userId']
                        if profilesCache.get(appletUser, '') == user['_id']:
                            notForCurrentUser = False
                            break
                if not notForCurrentUser:
                    events.append(event)

            if len(events):
                newSchedule = schedule.copy()
                newSchedule['events'] = events
                return newSchedule
            else:
                return {}
        return schedule
Ejemplo n.º 7
0
def getSchedule(currentUser, timezone=None):
    from girderformindlogger.models.profile import Profile

    schedule = {}

    accounts = AccountProfile().getAccounts(currentUser['_id'])
    applets = []

    for account in accounts:
        for applet in account.get('applets', {}).get('user', []):
            applets.append(applet)

    for appletId in applets:
        profile = Profile().findOne({
            'appletId': appletId,
            'userId': currentUser['_id']
        })
        activities = profile['completed_activities']

        appletSchedule = {}
        for activity in activities:
            appletSchedule['activity/{}'.format(activity['activity_id'])] = {
                'lastResponse':
                None if not activity['completed_time']
                else activity['completed_time'].astimezone(
                    pytz.timezone(timezone)).isoformat() if
                (isinstance(timezone, str) and timezone in pytz.all_timezones)
                else activity['completed_time'].isoformat()  #,
                # 'nextScheduled': None,
                # 'lastScheduled': None
            }
        schedule['applet/{}'.format(appletId)] = appletSchedule

    return schedule
Ejemplo n.º 8
0
    def getScheduleForUser(self, applet_id, user_id, is_coordinator):
        if is_coordinator:
            individualized = False
        else:
            profile = Profile().findOne({
                'appletId': ObjectId(applet_id),
                'userId': ObjectId(user_id)
            })
            individualized = self.hasIndividual(applet_id, profile['_id'])

        events = self.getEvents(applet_id, individualized)
        for event in events:
            event['id'] = event['_id']
            event.pop('_id')

        return {
            "type": 2,
            "size": 1,
            "fill": True,
            "minimumSize": 0,
            "repeatCovers": True,
            "listTimes": False,
            "eventsOutside": True,
            "updateRows": True,
            "updateColumns": False,
            "around": 1585724400000,
            'events': events
        }
Ejemplo n.º 9
0
    def createInvitationForSpecifiedUser(
        self,
        applet,
        coordinator,
        role,
        user,
        firstName,
        lastName,
        MRN,
        userEmail = ""
    ):
        """
        create new invitation
        params

        applet: The applet for which this invitation exists
        coordinator: the person who invites (should be manager/coordinator of applet)
        role: invited role
        user: invited person
        displayName: name of invited person 
        """
        from girderformindlogger.models.applet import Applet
        from girderformindlogger.models.profile import Profile

        query = {'appletId': applet['_id']}
        if user:
            query['userId'] = user['_id']
        else:
            query['userEmail'] = userEmail

        invitation = self.findOne(query)

        now = datetime.datetime.utcnow()

        if not invitation:
            invitation = {
                'appletId': applet['_id'],
                'created': now
            }
            if user:
                invitation['userId'] = user['_id']

        invitation.update({
            'inviterId': coordinator['_id'],
            'role': role,
            'firstName': firstName,
            'lastName': lastName,
            'MRN': MRN,
            'updated': now,
            'size': 0,
            'userEmail': userEmail,
            'invitedBy': Profile().coordinatorProfile(
                applet['_id'],
                coordinator
            )
        })

        return self.save(invitation, validate=False)
Ejemplo n.º 10
0
    def _hasRole(self, appletId, user, role):
        from .profile import Profile

        user = Profile()._canonicalUser(appletId, user)
        return (bool(
            str(appletId) in [
                str(applet.get('_id'))
                for applet in self.getAppletsForUser(role, user)
                if applet.get('_id') is not None
            ]))
Ejemplo n.º 11
0
    def _hasRole(self, appletId, user, role):
        from girderformindlogger.models.profile import Profile

        user = Profile()._canonicalUser(appletId, user)
        return (bool(
            str(appletId) in [
                str(applet.get('_id'))
                for applet in self.getAppletsForUser(role, user, idOnly=True)
                if applet.get('_id') is not None
            ]))
Ejemplo n.º 12
0
    def isCoordinator(self, appletId, user):
        from .profile import Profile

        try:
            user = Profile()._canonicalUser(appletId, user)
            return (any([
                self._hasRole(appletId, user, 'coordinator'),
                self.isManager(appletId, user)
            ]))
        except:
            return (False)
Ejemplo n.º 13
0
    def load(self,
             id,
             level=AccessType.ADMIN,
             user=None,
             objectId=True,
             force=False,
             fields=None,
             exc=False):
        """
        Calls AccessControlMixin.load while doing some auto-correction.

        Takes the same parameters as
        :py:func:`girderformindlogger.models.model_base.AccessControlMixin.load`
        """
        from girderformindlogger.models.profile import Profile

        # Ensure we include extra fields to do the migration below
        extraFields = {
            'baseParentId', 'baseParentType', 'parentId', 'parentCollection',
            'code'
        }
        loadFields = self._supplementFields(fields, extraFields)

        doc = super(IDCode, self).load(id=id,
                                       level=level,
                                       user=user,
                                       objectId=objectId,
                                       force=force,
                                       fields=loadFields,
                                       exc=exc)

        if doc is not None:
            if 'baseParentType' not in doc:
                pathFromRoot = self.parentsToRoot(doc, user=user, force=True)
                baseParent = pathFromRoot[0]
                doc['baseParentId'] = baseParent['object']['_id']
                doc['baseParentType'] = baseParent['type']
                self.update({'_id': doc['_id']}, {
                    '$set': {
                        'baseParentId': doc['baseParentId'],
                        'baseParentType': doc['baseParentType']
                    }
                })
            if 'code' not in doc:
                doc['code'] = self.generateCode(Profile().load(doc["parentId"],
                                                               force=True))
                self.update({'_id': doc['_id']},
                            {'$set': {
                                'code': doc['code']
                            }})
            self._removeSupplementalFields(doc, fields)

        return doc
def childByParent(parent, applet, parentProfile=None):
    from girderformindlogger.models.profile import Profile

    parentProfile = Profile().getProfile(
        Profile().createProfile(
            applet['applet']['_id'].split('applet/')[-1],
            parent,
            "user"
        ).get('_id'),
        user=parent
    ) if parentProfile is None else parentProfile
    parentKnows = parentProfile.get('schema:knows', {})
    children = [Profile().displayProfileFields(
        Profile().load(
            p,
            force=True
        ),
        parent
    ) for p in list(
            set(parentKnows.get('rel:parentOf', {})).union(
                set(parentKnows.get('schema:children', {}))
            )
        )
    ]
    return([
        formatChildApplet(child, deepcopy(applet)) for child in children
    ])
    def acceptInvitation(self, invitation, user):
        from girderformindlogger.models.applet import Applet
        from girderformindlogger.models.ID_code import IDCode
        from girderformindlogger.models.profile import Profile

        applet = Applet().load(invitation['appletId'], force=True)
        profiles = None
        if 'idCode' in invitation:
            profiles = IDCode().findProfile(invitation['idCode'])
        if profiles and len(profiles):
            profile = [
                pro for pro in profiles
                if str(pro.get('userId')) == str(user['_id'])
            ]
            profile = profile[0] if len(profile) else None
        else:
            profile = None
        if profiles == None or profile == None or not len(profile):
            profile = Profile().createProfile(applet,
                                              user,
                                              role=invitation.get(
                                                  'role', 'user'))
            IDCode().createIdCode(profile, invitation.get('idCode'))
        if 'schema:knows' in invitation:
            if 'schema:knows' not in profile:
                profile['schema:knows'] = invitation['schema:knows']
            else:
                for k in invitation['schema:knows']:
                    if k in profile['schema:knows']:
                        profile['schema:knows'][k].extend([
                            r for r in invitation['schema:knows'][k]
                            if r not in profile['schema:knows'][k]
                        ])
                    else:
                        profile['schema:knows'][k] = invitation[
                            'schema:knows'][k]
        Profile().save(profile, validate=False)
        self.remove(invitation)
        return (Profile().displayProfileFields(
            Profile().load(profile['_id'], force=True), user))
Ejemplo n.º 16
0
    def updateIndividualSchedulesParameter(self, newEvent, oldEvent):
        new = newEvent['data']['users'] if 'users' in newEvent['data'] else []
        old = newEvent['data']['users'] if oldEvent else []

        dicrementedUsers = list(set(old).difference(set(new)))
        incrementedUsers = list(set(new).difference(set(old)))

        if len(dicrementedUsers):
            Profile().update(query={"_id": {
                "$in": dicrementedUsers
            }},
                             update={'$inc': {
                                 'individual_events': -1
                             }})

        if len(incrementedUsers):
            Profile().update(query={"_id": {
                "$in": incrementedUsers
            }},
                             update={'$inc': {
                                 'individual_events': 1
                             }})
Ejemplo n.º 17
0
    def findIdCodes(self, profileId):
        from girderformindlogger.models.profile import Profile

        idCodes = [
            i['code'] for i in list(
                self.find({
                    'profileId': {
                        '$in': [str(profileId),
                                ObjectId(profileId)]
                    }
                })) if isinstance(i, dict) and 'code' in i
        ]

        if not len(idCodes):
            self.createIdCode(Profile().load(profileId, force=True))
            return (self.findIdCodes(profileId))

        return (idCodes)
Ejemplo n.º 18
0
    def invite(self, applet, role="user", idCode=None, profile=None):
        from girderformindlogger.models.invitation import Invitation
        from girderformindlogger.models.profile import Profile

        user = self.getCurrentUser()
        try:
            if role not in USER_ROLE_KEYS:
                raise ValidationException('Invalid role.', 'role')

            invitation = Invitation().createInvitation(applet=applet,
                                                       coordinator=user,
                                                       role=role,
                                                       profile=profile,
                                                       idCode=idCode)

            return (Profile().displayProfileFields(invitation, user))
        except:
            import sys, traceback
            print(sys.exc_info())
Ejemplo n.º 19
0
    def getResponseData(self, appletId, reviewer, filter={}):
        """
        Function to collect response data available to given reviewer.

        :param appletId: ID of applet for which to get response data
        :type appletId: ObjectId or str
        :param reviewer: Reviewer making request
        :type reviewer: dict
        :param filter: reduction criteria (not yet implemented)
        :type filter: dict
        :reutrns: TBD
        """
        from .ID_code import IDCode
        from .profile import Profile
        from .response_folder import ResponseItem
        from .user import User
        from pymongo import DESCENDING

        if not self._hasRole(appletId, reviewer, 'reviewer'):
            raise AccessException("You are not a reviewer for this applet.")
        query = {
            "baseParentType": "user",
            "meta.applet.@id": ObjectId(appletId)
        }
        responses = list(ResponseItem().find(query=query,
                                             user=reviewer,
                                             sort=[("created", DESCENDING)]))
        respondents = {
            str(response['baseParentId']):
            IDCode().findIdCodes(Profile().createProfile(
                appletId,
                User().load(response['baseParentId'], force=True),
                'user')['_id'])
            for response in responses if 'baseParentId' in response
        }
        return ([{
            "respondent": code,
            **response.get('meta', {})
        } for response in responses
                 for code in respondents[str(response['baseParentId'])]])
Ejemplo n.º 20
0
 def findProfile(self, idCode):
     """
     Find a list of profiles for a given ID code.
     """
     existing = list(self.find({'code': idCode}))
     if len(existing):
         from girderformindlogger.models.profile import Profile
         ps = [
             Profile().load(exist['profileId'], force=True)
             for exist in existing
         ]
         if len(ps):
             return (ps)
     else:
         from girderformindlogger.models.invitation import Invitation
         existing = list(self.find({'idCode': idCode}))
         ps = [
             Invitation().load(exist['_id'], force=True)
             for exist in existing
         ]
         if len(ps):
             return (ps)
     return (None)
Ejemplo n.º 21
0
def inviteUserToApplet(user, appletObject, userB):
    """
    invite a user to an applet

    inputs
    ------
    user: a user object
    appletObject:
    userB: a user object of a user you want to invite. If they aren't
    defined yet, it should be a dict(email="emailaddress")
    """

    groupId = appletObject['roles']['user']['groups'][0]['id']
    currentUser = authenticate(user)
    group = Group().load(id=ObjectId(groupId), force=True)

    invitation = Invitation().createInvitation(appletObject,
                                               currentUser,
                                               role="user",
                                               profile=Profile().createProfile(
                                                   appletObject, userB),
                                               idCode=None)
    return Group().inviteUser(group, userB, level=AccessType.READ)
Ejemplo n.º 22
0
    def createInvitation(
        self,
        applet,
        coordinator,
        role="user",
        profile=None,
        idCode=None
    ):
        """
        Create a new invitation to store information specific to a given (applet
            ∩ (ID code ∪ profile))

        :param applet: The applet for which this invitation exists
        :type parent: dict
        :param coordinator: user who is doing the inviting
        :type coordinator: dict
        :param profile: Profile to apply to (applet ∩ user) if the invitation is
            accepted
        :type profile: dict or none
        :param idCode: ID code to apply to (applet ∩ user) if invitation is
            accepted
        :type idCode: string or None
        :returns: The invitation document that was created.
        """
        from girderformindlogger.models.applet import Applet
        from girderformindlogger.models.profile import Profile

        if not(Applet().isCoordinator(applet['_id'], coordinator)):
            raise AccessException(
                'You do not have adequate permissions to invite users to this '
                'applet ({}).'.format(Applet().preferredName(applet))
            )

        codified = (isinstance(idCode, str) and len(idCode))

        existing = self.findOne({
            'appletId': applet['_id'],
            'idCode': idCode
        }) if codified else None

        if existing:
            return existing

        now = datetime.datetime.utcnow()

        invitation = {
            'appletId': applet['_id'],
            'created': now,
            'updated': now,
            'role': role,
            'size': 0,
            'invitedBy': Profile().coordinatorProfile(
                applet['_id'],
                coordinator
            )
        }

        if codified:
            invitation["idCode"] = idCode

        if isinstance(profile, dict):
            invitation["coordinatorDefined"] = profile

        self.setPublic(invitation, False, save=False)

        # Now validate and save the profile.
        return ({
            k: v for k, v in self.save(invitation, validate=False).items() if (
                k!="idCode" and v is not None
            )
        })
Ejemplo n.º 23
0
    def getAppletUsers(self, applet, user=None, force=False):
        """
        Function to return a list of Applet Users

        :param applet: Applet to get users for.
        :type applet: dict
        :param user: User making request
        :type user: dict
        :returns: list of dicts
        """
        from girderformindlogger.models.invitation import Invitation
        from girderformindlogger.models.profile import Profile

        profileFields = ["_id", "coordinatorDefined", "userDefined"]

        try:

            if not isinstance(user, dict):
                user = UserModel().load(
                    id=user, level=AccessType.READ, force=True) if isinstance(
                        user, str) else {}

            if not force:
                if not self.isCoordinator(applet.get('_id', applet), user):
                    return ([])

            profileModel = Profile()
            userDict = {
                'active': [
                    profileModel.displayProfileFields(p,
                                                      user,
                                                      forceManager=True)
                    for p in list(
                        profileModel.find(
                            query={
                                'appletId': applet['_id'],
                                'userId': {
                                    '$exists': True
                                },
                                'profile': True,
                                'deactivated': {
                                    '$ne': True
                                }
                            }))
                ],
                'pending': []
            }

            for p in list(
                    Invitation().find(query={'appletId': applet['_id']})):
                fields = [
                    '_id', 'firstName', 'lastName', 'role', 'MRN', 'created'
                ]
                userDict['pending'].append(
                    {key: p[key]
                     for key in fields if p.get(key, None)})

            missing = threading.Thread(target=profileModel.generateMissing,
                                       args=(applet, ))
            missing.start()

            if len(userDict['active']):
                return (userDict)

            else:
                return ({**userDict, "message": "cache updating"})
        except:
            import sys, traceback
            print(sys.exc_info())
            return ({traceback.print_tb(sys.exc_info()[2])})
Ejemplo n.º 24
0
    def htmlInvitation(self,
                       invitation,
                       invitee=None,
                       fullDoc=False,
                       includeLink=True):
        """
        Returns an HTML document rendering the invitation.

        :param invitation: Invitation to render
        :type invitation: dict
        :param invitee: Invited user
        :type invitee: dict or None

        :returns: html document
        """
        from girderformindlogger.models.applet import Applet
        from girderformindlogger.models.profile import Profile
        from girderformindlogger.models.protocol import Protocol
        from girderformindlogger.models.token import Token
        from girderformindlogger.models.user import User
        from girderformindlogger.exceptions import GirderException
        from girderformindlogger.api.rest import getApiUrl
        from girderformindlogger.utility import context as contextUtil,        \
            mail_utils

        web_url = os.getenv('WEB_URI') or 'localhost:8082'

        accept = ("To accept or decline, visit <a href=\"{u}\">{u}</a>".format(
            u=f"https://{web_url}/#/invitation/{str(invitation['_id'])}")
                  ) if includeLink else ""
        applet = Applet().load(ObjectId(invitation['appletId']), force=True)
        appletName = applet['meta']['applet'].get(
            'displayName', applet.get('displayName', 'new applet'))
        try:
            skin = contextUtil.getSkin()
        except:
            skin = {}
        instanceName = skin.get("name", "MindLogger")
        role = invitation.get("role", "user")
        try:
            coordinator = Profile().coordinatorProfile(applet['_id'],
                                                       invitation["invitedBy"])
        except:
            coordinator = None
        displayProfile = Profile().displayProfileFields(invitation, invitee)
        description = applet.get('meta', {}).get('applet', {}).get(
            "schema:description",
            Protocol().load(
                applet['meta']['protocol']['_id'].split('protocol/')[-1],
                force=True).get('meta', {}).get('protocol') if 'protocol'
            in applet.get('meta', {}) else {}).get("schema:description", "")
        managers = mail_utils.htmlUserList(Applet().listUsers(applet,
                                                              'manager',
                                                              force=True))
        coordinators = mail_utils.htmlUserList(Applet().listUsers(
            applet, 'coordinator', force=True))
        reviewers = mail_utils.htmlUserList(Applet().listUsers(applet,
                                                               'reviewer',
                                                               force=True))

        body = mail_utils.renderTemplate(
            f'welcome{"Owner" if role == "owner" else ""}.{invitation.get("lang", "en")}.mako',
            {
                'accept':
                accept,
                'appletName':
                appletName,
                'byCoordinator':
                "by {} ({}) ".format(
                    coordinator.get("displayName", "an anonymous entity"),
                    "<a href=\"mailto:{email}\">{email}</a>".format(
                        email=coordinator["email"]) if "email" in coordinator
                    and coordinator["email"] is not None
                    else "email not available") if isinstance(
                        coordinator, dict) else "",
                'coordinators':
                "{}".format(coordinators if len(coordinators) else
                            "<ul><li>None</li></ul>"),
                'instanceName':
                " on {}".format(instanceName)
                if instanceName is not None and len(instanceName) else "",
                'managers':
                "{}".format(
                    managers if len(managers) else "<ul><li>None</li></ul>"),
                'reviewers':
                "{}".format(
                    reviewers if len(reviewers) else "<ul><li>None</li></ul>"),
                'role':
                "an editor" if role == "editor" else "a {}".format(role),
                'url':
                f'https://{web_url}/#/invitation/{str(invitation["_id"])}'
            })

        return (body if not fullDoc else """
            <!DOCTYPE html>
            <html>
            <head>
            <meta charset="UTF-8">
            <title>Invitation to {appletName} on {instanceName}</title>
            </head>
            <body>
            {body}
            </body>
            </html>
        """.format(appletName=appletName, instanceName=instanceName,
                   body=body).strip())
Ejemplo n.º 25
0
    def getScheduleForUser(self, applet_id, user_id, dayFilter=None):
        profile = Profile().findOne({
            'appletId': ObjectId(applet_id),
            'userId': ObjectId(user_id)
        })
        individualized = profile['individual_events'] > 0
        events = self.getEvents(applet_id, individualized, profile['_id'])

        lastEvent = {}
        onlyScheduledDay = {}
        for event in events:
            event['id'] = event['_id']
            event.pop('_id')

            event['valid'] = True
            if dayFilter:
                event['valid'], lastAvailableTime = self.dateMatch(
                    event, dayFilter)

                activityId = event.get('data', {}).get('activity_id', None)

                if not activityId:
                    event['valid'] = False
                    continue

                if not event['valid']:
                    if lastAvailableTime:
                        if activityId not in lastEvent or (
                                lastEvent[activityId] and
                                lastAvailableTime > lastEvent[activityId][0]):
                            lastEvent[activityId] = (lastAvailableTime, event)
                else:
                    lastEvent[activityId] = None

                if event['data'].get('eventType', None) and event['data'].get(
                        'onlyScheduledDay', False):
                    onlyScheduledDay[activityId] = True

        return {
            "type":
            2,
            "size":
            1,
            "fill":
            True,
            "minimumSize":
            0,
            "repeatCovers":
            True,
            "listTimes":
            False,
            "eventsOutside":
            True,
            "updateRows":
            True,
            "updateColumns":
            False,
            "around":
            1585724400000,
            'events': ([event for event in events if event['valid']] + [
                value[1] for value in lastEvent.values() if value and
                (value[1]['data'].get('completion', False) or value[1]
                 ['data'].get('activity_id', None) in onlyScheduledDay)
            ])
        }
Ejemplo n.º 26
0
    def updateUserCache(self, role, user, active=True, refreshCache=False):
        import threading
        from bson import json_util
        from girderformindlogger.models.profile import Profile
        from girderformindlogger.utility import jsonld_expander

        applets = self.getAppletsForUser(role, user, active)
        user['cached'] = user.get('cached', {})
        user['cached'] = json_util.loads(user['cached']) if isinstance(
            user['cached'], str) else user['cached']
        user['cached']['applets'] = user['cached'].get('applets', {})
        user['cached']['applets'][role] = user['cached']['applets'].get(
            role, {})
        formatted = [{
            **jsonld_expander.formatLdObject(applet,
                                             'applet',
                                             user,
                                             refreshCache=refreshCache,
                                             responseDates=False), "users":
            self.getAppletUsers(applet, user),
            "groups":
            self.getAppletGroups(applet, arrayOfObjects=True)
        } if role in ["coordinator", "manager"] else {
            **jsonld_expander.formatLdObject(applet,
                                             'applet',
                                             user,
                                             refreshCache=refreshCache,
                                             responseDates=(role == "user")), "groups":
            [
                group for group in self.getAppletGroups(applet).get(role)
                if ObjectId(group) in [
                    *user.get('groups', []), *user.get('formerGroups', []), *[
                        invite['groupId'] for invite in [
                            *user.get('groupInvites', []),
                            *user.get('declinedInvites', [])
                        ]
                    ]
                ]
            ]
        } for applet in applets if (
            applet is not None
            and not applet.get('meta', {}).get('applet', {}).get('deleted'))]
        postformatted = []
        for applet in formatted:
            if role == 'user' and applet['applet'].get(
                    'informantRelationship') == 'parent':
                parentProfile = Profile().getProfile(Profile().createProfile(
                    applet['applet']['_id'].split('applet/')[-1], user,
                    "user").get('_id'),
                                                     user=user)
                [
                    postformatted.append(a)
                    for a in jsonld_expander.childByParent(
                        user, applet, parentProfile)
                ]
            else:
                postformatted.append(applet)
        user['cached']['applets'].update({role: postformatted})
        thread = threading.Thread(target=UserModel().save, args=(user, ))
        thread.start()
        return (postformatted)
Ejemplo n.º 27
0
def send_push_notification(applet_id,
                           event_id,
                           activity_id=None,
                           send_time=None):
    from girderformindlogger.models.events import Events
    from girderformindlogger.models.profile import Profile

    now = datetime.datetime.utcnow()
    now = datetime.datetime.strptime(now.strftime('%Y/%m/%d %H:%M'),
                                     '%Y/%m/%d %H:%M')

    event = Events().findOne({'_id': event_id})

    if event:
        event_time = datetime.datetime.strptime(
            f"{now.year}/{now.month}/{now.day} {send_time}", '%Y/%m/%d %H:%M')

        timezone = (event_time - now).total_seconds() / 3600

        # this is temporary fix for timezone issue
        if timezone >= 12:
            timezone = timezone - 24
        elif timezone < -12:
            timezone = timezone + 24

        query = {
            'appletId': applet_id,
            'timezone': round(timezone, 2),
            'profile': True,
            'individual_events': 0
        }

        if event['individualized']:
            query['individual_events'] = {'$gte': 1}
            query['_id'] = {'$in': event['data']['users']}

        if activity_id:
            query['completed_activities'] = {
                '$elemMatch': {
                    '$or': [{
                        'activity_id': activity_id,
                        'completed_time': {
                            '$not': {
                                '$gt': now - datetime.timedelta(hours=12),
                                '$lt': now
                            }
                        }
                    }, {
                        'activity_id': activity_id,
                        'completed_time': {
                            '$eq': None
                        }
                    }]
                }
            }

        profiles = list(Profile().find(query=query,
                                       fields=['deviceId', 'badge']))

        # ordered by badge
        message_requests = defaultdict(list)
        for profile in profiles:
            message_requests[profile["badge"]].append(profile["deviceId"])

        for badge in message_requests:
            result = push_service.notify_multiple_devices(
                registration_ids=message_requests[badge],
                message_title=event['data']['title'],
                message_body=event['data']['description'],
                data_message={
                    "event_id": str(event_id),
                    "applet_id": str(applet_id),
                    "activity_id": str(activity_id),
                    "type": 'event-alert'
                },
                badge=int(badge) + 1)

            print(
                f'Notifications with failure status - {str(result["failure"])}'
            )
            print(
                f'Notifications with success status - {str(result["success"])}'
            )

        Profile().updateProfileBadgets(profiles)

        # if random time we will reschedule it in time between 23:45 and 23:59
        if event['data']['notifications'][0][
                'random'] and now.hour == 23 and 59 >= now.minute >= 45:
            Events().rescheduleRandomNotifications(event)
Ejemplo n.º 28
0
    def createResponseItem(
        self,
        applet,
        activity,
        metadata,
        subject_id,
        pending,
        params
    ):
        from girderformindlogger.models.profile import Profile
        try:
            from girderformindlogger.utility.response import aggregateAndSave
            # TODO: pending
            metadata['applet'] = {
                "@id": applet.get('_id'),
                "name": AppletModel().preferredName(applet),
                "url": applet.get(
                    'url',
                    applet.get('meta', {}).get('applet', {}).get('url')
                )
            }
            metadata['activity'] = {
                "@id": activity.get('_id'),
                "name": ActivityModel().preferredName(activity),
                "url": activity.get(
                    'url',
                    activity.get('meta', {}).get('activity', {}).get('url')
                )
            }
            informant = self.getCurrentUser()
            subject_id = subject_id if subject_id else str(
                informant['_id']
            )

            subject_id = Profile().createProfile(
                applet,
                subject_id
            ).get('_id')

            print(subject_id)

            if isinstance(metadata.get('subject'), dict):
                metadata['subject']['@id'] = subject_id
            else:
                metadata['subject'] = {'@id': subject_id}
            now = datetime.now(tzlocal.get_localzone())
            appletName=AppletModel().preferredName(applet)
            UserResponsesFolder = ResponseFolderModel().load(
                user=informant,
                reviewer=informant,
                force=True
            )
            UserAppletResponsesFolder = Folder().createFolder(
                parent=UserResponsesFolder, parentType='folder',
                name=appletName, reuseExisting=True, public=False)
            AppletSubjectResponsesFolder = Folder().createFolder(
                parent=UserAppletResponsesFolder, parentType='folder',
                name=str(subject_id), reuseExisting=True, public=False)

            try:
                newItem = self._model.createResponseItem(
                    folder=AppletSubjectResponsesFolder,
                    name=now.strftime("%Y-%m-%d-%H-%M-%S-%Z"),
                    creator=informant,
                    description="{} response on {} at {}".format(
                        Folder().preferredName(activity),
                        now.strftime("%Y-%m-%d"),
                        now.strftime("%H:%M:%S %Z")
                    ), reuseExisting=False)
            except:
                raise ValidationException(
                    "Couldn't find activity name for this response"
                )

            # for each blob in the parameter, upload it to a File under the item.
            for key, value in params.items():
                # upload the value (a blob)
                um = UploadModel()
                filename = "{}.{}".format(
                    key,
                    metadata['responses'][key]['type'].split('/')[-1]
                )
                newUpload = um.uploadFromFile(
                    value.file,
                    metadata['responses'][key]['size'],
                    filename,
                    'item',
                    newItem,
                    informant,
                    metadata['responses'][key]['type']
                )
                # now, replace the metadata key with a link to this upload
                metadata['responses'][key] = "file::{}".format(newUpload['_id'])

            if metadata:
                newItem = self._model.setMetadata(newItem, metadata)

            print(metadata)
            if not pending:
                # create a Thread to calculate and save aggregates

                # TODO: probably uncomment this as we scale.
                # idea: thread all time, but synchronously do last7 days
                # agg = threading.Thread(target=aggregateAndSave, args=(newItem, informant))
                # agg.start()
                aggregateAndSave(newItem, informant)
                newItem['readOnly'] = True
            print(newItem)
            return(newItem)
        except:
            import sys, traceback
            print(sys.exc_info())
            print(traceback.print_tb(sys.exc_info()[2]))
            return(str(traceback.print_tb(sys.exc_info()[2])))
Ejemplo n.º 29
0
    def acceptInvitation(self, invitation, user, userEmail = ''): # we need to save coordinator/manager's email as plain text
        from girderformindlogger.models.applet import Applet
        from girderformindlogger.models.ID_code import IDCode
        from girderformindlogger.models.profile import Profile
        from girderformindlogger.utility import mail_utils

        applet = Applet().load(invitation['appletId'], force=True)
        profiles = None
        if 'idCode' in invitation:
            profiles = IDCode().findProfile(invitation['idCode'])
        if profiles and len(profiles):
            profile = [
                pro for pro in profiles if str(
                    pro.get('userId')
                )==str(user['_id'])
            ]
            profile = profile[0] if len(profile) else None
        else:
            profile = None
            Profile().removeWithQuery({ '_id': ObjectId(invitation['_id']) })

        if profile==None or not len(profile):
            profile = Profile().createProfile(
                applet,
                user,
                role=invitation.get('role', 'user')
            )
            IDCode().createIdCode(profile, invitation.get('idCode'))
        if 'schema:knows' in invitation:
            if 'schema:knows' not in profile:
                profile['schema:knows'] = invitation['schema:knows']
            else:
                for k in invitation['schema:knows']:
                    if k in profile['schema:knows']:
                        profile['schema:knows'][k].extend([
                            r for r in invitation['schema:knows'][
                                k
                            ] if r not in profile['schema:knows'][k]
                        ])
                    else:
                        profile['schema:knows'][k] = invitation['schema:knows'][
                            k
                        ]

        # append role value
        profile = Profile().load(profile['_id'], force=True)
        profile['roles'] = profile.get('roles', [])
        invited_role = invitation.get('role','user')

        new_roles = []
        # manager has get all roles by default
        for role in USER_ROLES.keys():
            if role not in profile['roles']:
                if invited_role == 'manager' or invited_role == role or role == 'user':
                    new_roles.append(role)
                    profile['roles'].append(role)

        profile['firstName'] = invitation.get('firstName', '')
        profile['lastName'] = invitation.get('lastName', '')
        profile['MRN'] = invitation.get('MRN', '')

        Profile().save(profile, validate=False)

        from girderformindlogger.models.user import User as UserModel
        
        if not mail_utils.validateEmailAddress(userEmail):
            raise ValidationException(
                'Invalid email address.',
                'email'
            )
        if invited_role != 'user' and user.get('email_encrypted', False):
            if UserModel().hash(userEmail) != user['email']:
                raise ValidationException(
                    'Invalid email address.',
                    'email'
                )
            user['email'] = userEmail
            user['email_encrypted'] = False

            UserModel().save(user)

        UserModel().appendApplet(user, applet['_id'], new_roles)

        self.remove(invitation)        
        return(Profile().displayProfileFields(
            Profile().load(profile['_id'], force=True),
            user
        ))
Ejemplo n.º 30
0
    def htmlInvitation(
        self,
        invitation,
        invitee=None,
        fullDoc=False,
        includeLink=True
    ):
        """
        Returns an HTML document rendering the invitation.

        :param invitation: Invitation to render
        :type invitation: dict
        :param invitee: Invited user
        :type invitee: dict or None

        :returns: html document
        """
        from girderformindlogger.models.applet import Applet
        from girderformindlogger.models.profile import Profile
        from girderformindlogger.models.protocol import Protocol
        from girderformindlogger.models.token import Token
        from girderformindlogger.models.user import User
        from girderformindlogger.exceptions import GirderException
        from girderformindlogger.api.rest import getApiUrl
        from girderformindlogger.utility import context as contextUtil,        \
            mail_utils

        accept = (
            "To accept or decline, visit <a href=\"{u}\">{u}</a>".format(
                u="https://web.mindlogger.org/#/invitation/{}".format(str(
                    invitation['_id']
                ))
            )
        ) if includeLink else ""
        applet = Applet().load(ObjectId(invitation['appletId']), force=True)
        appletName = applet.get(
            'displayName',
            'a new applet'
        )
        try:
            skin = contextUtil.getSkin()
        except:
            skin = {}
        instanceName = skin.get("name", "MindLogger")
        role = invitation.get("role", "user")
        try:
            coordinator = Profile().coordinatorProfile(
                applet['_id'],
                invitation["invitedBy"]
            )
        except:
            coordinator = None
        displayProfile = Profile().displayProfileFields(invitation, invitee)
        description = applet.get('meta', {}).get(
            'applet',
            {}
        ).get(
            "schema:desciription",
            Protocol().load(
                applet['meta']['protocol']['_id'].split('protocol/')[-1],
                force=True
            ).get('meta', {}).get('protocol') if 'protocol' in applet.get(
                'meta',
                {}
            ) else {}
        ).get("schema:description", "")
        managers = mail_utils.htmlUserList(
            Applet().listUsers(applet, 'manager', force=True)
        )
        coordinators = mail_utils.htmlUserList(
            Applet().listUsers(applet, 'coordinator', force=True)
        )
        reviewers = mail_utils.htmlUserList(
            Applet().listUsers(applet, 'reviewer', force=True)
        )
        body = """
{greeting}ou were invited {byCoordinator}to be {role} of <b>{appletName}</b>{instanceName}.
<br/>
Below are the users that have access to your data:
{reviewers}
{managers}
{coordinators}
<br/>
{accept}
        """.format(
            accept=accept,
            appletName=appletName,
            byCoordinator="by {} ({}) ".format(
                coordinator.get("displayName", "an anonymous entity"),
                "<a href=\"mailto:{email}\">{email}</a>".format(
                    email=coordinator["email"]
                ) if "email" in coordinator and coordinator["email"] is not None else "email not available"
            ) if isinstance(coordinator, dict) else "",
            coordinators="<h3>Users who can change this applet's settings, "
                "but who cannot change who can see your data: </h3>{}"
                "".format(
                    coordinators if len(
                        coordinators
                    ) else "<ul><li>None</li></ul>"
                ),
            greeting="Welcome to MindLogger! Y",
            instanceName=" on {}".format(
                instanceName
            ) if instanceName is not None and len(instanceName) else "",
            managers="<h3>Users who can change this applet's settings, "
                " including who can access your data: </h3>{}"
                "".format(
                    managers if len(managers) else "<ul><li>None</li></ul>"
                ),
            reviewers="<h3>Users who can see your data for this "
                "applet: </h3>{}"
                "".format(
                    reviewers if len(reviewers) else "<ul><li>None</li></ul>"
                ),
            role="an editor" if role=="editor" else "a {}".format(role)
        ).strip()
        return(body if not fullDoc else """
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Invitation to {appletName} on {instanceName}</title>
</head>
<body>
{body}
</body>
</html>
        """.format(
            appletName=appletName,
            instanceName=instanceName,
            body=body
        ).strip())