コード例 #1
0
ファイル: lol_teamAPI.py プロジェクト: bintao/cteemo
	def post(self, user_id):
		args = teamParser.parse_args()
		profileID = args['profileID']

		profile = Profile.objects(id=profileID).first()
		request = Request.objects(user=user_id,type='invite').only('requests_list').first()
		if request is None or profile is None:
			raise InvalidUsage('Request is illegal')

		team = profile.LOLTeam

		success = request.update(pull__requests_list=profile)
		if success is 0 or team is None or team.captain != profile:
			raise InvalidUsage('Request is illegal') 

		profile = Profile.objects(user=user_id).first()
		if profile.LOLTeam is not None:
			raise InvalidUsage('Already joined a team')
		try:
			assert len(team.members) < 6
			team.members.append(profile)
		except:
			raise InvalidUsage('Team is full', 403)
		profile.LOLTeam = team

		team.save()
		profile.save()

		rongcloudJoinGroup(profile.id,team.id,teamName)

		return team_serialize(team)
コード例 #2
0
    def post(self, user_id):
        """
        Add a specific user to friend list, and
        send a friend request to that user
        """
        args = friendsParser.parse_args()
        profile_id = args['profile_id']

        if profile_id is None:
            abort(400)

        friend_profile = Profile.objects(id=profile_id).only('user').first()
        if friend_profile is None:
            abort(400)

        # add the user to friend list
        success = Friend.objects(user=user_id).only(
            'friends_list').update_one(add_to_set__friends_list=profile_id)
        if success is 0:
            friends = Friend(user=user_id, friends_list=[profile_id])
            friends.save()

        # put the friend request to the user's request list
        user_profile = Profile.objects(user=user_id).only('id').first()
        success = Request.objects(user=friend_profile.user).update_one(
            add_to_set__requests_list=user_profile)
        if success is 0:
            friend_request = Request(
                user=friend_profile.user,
                type='friends', requests_list=[user_profile])
            friend_request.save()

        return {'status': 'success', 'message':
                'The user has been added to your friend list'}
コード例 #3
0
ファイル: lol_teamAPI.py プロジェクト: bintao/cteemo
	def delete(self, user_id):
		args = teamParser.parse_args()
		profileID = args['profileID']
		profile = Profile.objects(user=user_id).first()
		team = profile.LOLTeam
		# avoid illegal operation
		if team is None:
			abort(400)
		if team.captain != profile:
			raise InvalidUsage('Unauthorized',401)
		# query the player u want to kick
		member = Profile.objects(id=profileID).first()
		if member == profile:
			raise InvalidUsage('Cannot kick yourself')

		success = team.update(pull__members=member)
		if success is 0:
			raise InvalidUsage('Member not found')
		
		member.LOLTeam = None
		team.save()
		member.save()

		rongcloudLeaveGroup(member.id,team.id)
		
		return {'status' : 'success'}
コード例 #4
0
    def post(self, user_id):
        """
        Upload user's profile icon
        """
        uploaded_file = request.files['upload']
        filename = "_".join([user_id, uploaded_file.filename])

        # upload the file to S3 server
        conn = boto.connect_s3(os.environ['S3_KEY'], os.environ['S3_SECRET'])
        bucket = conn.get_bucket('profile-icon')
        key = bucket.new_key(filename)
        key.set_contents_from_file(uploaded_file)

        # update the user's profile document
        profile = Profile.objects(user=user_id).first()
        if profile is None:
            profile = Profile(
                user=user_id,
                profile_icon=
                'https://s3-us-west-2.amazonaws.com/profile-icon/%s' %
                filename)
            profile.save()
        else:
            profile.profile_icon = 'https://s3-us-west-2.amazonaws.com/profile-icon/%s' % filename
            profile.save()

        return serialize(profile)
コード例 #5
0
ファイル: userAPI.py プロジェクト: bintao/cteemo
    def post(self):
        args = fbUserParser.parse_args()
        fb_id = args['fbid']
        fb_token = args['fbtoken']   
        if fb_id is None or fb_token is None:
           abort(400)

        fbuser_info = requests.get('https://graph.facebook.com/me?access_token=%s' %fb_token).json()
        if not fbuser_info.get('id') or fb_id != fbuser_info['id']:
            raise InvalidUsage('User info does not match',406)

        fb_email = args['fbemail']
        user = User.objects(email=fb_email).first()
        
        if user is None:
            user = User(email=fb_email, fb_id=fbuser_info['id'])
            user.save()
            
        profile = Profile.objects(user=user).first()
        if profile is None:
            profile = Profile(user=user)
            profile.save()

        rongToken = rongcloudToken(profile.id)
        token = user.generate_auth_token()
        redis_store.set(str(user.id), token)
        return {'token': token, 'rongToken' : rongToken}
コード例 #6
0
ファイル: profileAPI.py プロジェクト: bintao/cteemo
    def get(self, user_id):
        # load profile 
        profile =  Profile.objects(user=user_id).first()
        if profile is None:
        	return {}

        return serialize(profile)
コード例 #7
0
ファイル: profileAPI.py プロジェクト: bintao/cteemo
    def post(self, user_id):
    	args = profileParser.parse_args()
        username = args['username']
        school = args['school']
        intro = args['intro']
        lolID = args['lolID']
        dotaID = args['dotaID']
        hstoneID = args['hstoneID']
        gender = args['gender']

        profile = Profile.objects(user=user_id).first()
        if profile is None:
            profile = Profile(user=user_id)
        profile.username = username
        profile.school = school
        profile.intro = intro
        profile.lolID = lolID
        profile.dotaID = dotaID
        profile.hstoneID = hstoneID
        profile.gender = gender
        profile.save()
        
        rongRefresh(profile.id)

        return serialize(profile)
コード例 #8
0
    def post(self):
        """
        Verify the information from user
        Send a reset password email if the information is correct
        """
        args = forgetPasswordParser.parse_args()
        email = args['email']
        username = args['username']
        school = args['school']

        if email is None or username is None or school is None:
            abort(400)

        user = User.objects(email=email).first()
        if user is None:
            return {'status': 'error', 'message':
                    'There is no user associated with the email'}

        profile = Profile.objects(user=user).first()
        if not profile.checkInfo(username, school):
            return {'status': 'error', 'message':
                    'The information does not match the record'}

        token = user.generate_auth_token(expiration=360000)
        send_forget_password_email(email, token)

        return {'status': 'success', 'message':
                'An email has been sent to you letting you reset password'}
コード例 #9
0
    def post(self):
        """
        Verify the information from user
        Send a reset password email if the information is correct
        """
        args = forgetPasswordParser.parse_args()
        email = args['email']
        username = args['username']
        school = args['school']

        if email is None or username is None or school is None:
            abort(400)

        user = User.objects(email=email).first()
        if user is None:
            return {
                'status': 'error',
                'message': 'There is no user associated with the email'
            }

        profile = Profile.objects(user=user).first()
        if not profile.checkInfo(username, school):
            return {
                'status': 'error',
                'message': 'The information does not match the record'
            }

        token = user.generate_auth_token(expiration=360000)
        send_forget_password_email(email, token)

        return {
            'status': 'success',
            'message':
            'An email has been sent to you letting you reset password'
        }
コード例 #10
0
ファイル: postAPI.py プロジェクト: bintao/cteemo
	def post(self, user_id):
		args = postParser.parse_args()
		content = args['content']

		profile = Profile.objects(user=user_id).first()
		post = PlayerPost(user_profile=profile, content=content)
		post.save()

		return {'status': 'success'}
コード例 #11
0
    def get(self, user_id):
        """
        Load the user's profile
        """
        profile = Profile.objects(user=user_id).first()
        if profile is None:
            return {}

        return serialize(profile)
コード例 #12
0
    def get(self, user_id):
        """
        Load the user's profile
        """
        profile = Profile.objects(user=user_id).first()
        if profile is None:
            return {}

        return serialize(profile)
コード例 #13
0
ファイル: tournamentAPI.py プロジェクト: bintao/cteemo
	def get(self, user_id):
		args = tournamentParser.parse_args()
		page = args['page']
		if page is None:
			page = 0

		profile = Profile.objects(user=user_id).first()
		tournaments = Tournament.objects(creator=profile).only('id','creator','isEnded','createTime','rounds','isFull')

		return tournament_search_serialize(tournaments[5*page:5*(page+1)])
コード例 #14
0
ファイル: postAPI.py プロジェクト: Xuefeng-Zhu/flask-user-api
    def post(self, user_id):
        """
        Make a new post
        """
        args = postParser.parse_args()
        content = args['content']

        profile = Profile.objects(user=user_id).first()
        post = Post(user=user_id, user_profile=profile, content=content)
        post.save()

        return {'status': 'success'}
コード例 #15
0
ファイル: tournamentAPI.py プロジェクト: bintao/cteemo
	def get(self, user_id):
		profile = Profile.objects(user=user_id).first()
		team = profile.LOLTeam
		if team is None:
			raise InvalidUsage('Team not found',404)
		if team.captain != profile:
			raise InvalidUsage('Unauthorized',401)
		if team.inGame is False:
			raise InvalidUsage('Not in a tournament')

		match = team.matchHistory[-1]

		return match_serialize(match)
コード例 #16
0
ファイル: lol_teamAPI.py プロジェクト: bintao/cteemo
	def post(self, user_id):
		args = teamParser.parse_args()
		profileID = args['profileID']

		request = Request.objects(user=user_id, type='join').only('requests_list').first()
		if request is None:
			raise InvalidUsage('Request does not exist')

		captain = Profile.objects(user=user_id).first()
		team = captain.LOLTeam
		if team is None:
			raise InvalidUsage('Request is illegal')

		if team.captain != captain:
			raise InvalidUsage('Unauthorized',401)
		# query the player u want to invite
		profile = Profile.objects(id=profileID).first()
		success = request.update(pull__requests_list=profile)
		if success is 0:
			raise InvalidUsage('Request not found')

		if profile is None:
			raise InvalidUsage('Member not found',404)
		if profile.LOLTeam is not None:
			raise InvalidUsage('The user already joined a team')
		try:
			assert len(team.members) < 6
			team.members.append(profile)
		except:
			raise InvalidUsage('Team is full',403)
		profile.LOLTeam = team
		team.save()
		profile.save()

		rongcloudJoinGroup(profile.id,team.id,teamName)

		return team_serialize(team)
コード例 #17
0
ファイル: lol_teamAPI.py プロジェクト: bintao/cteemo
	def post(self, user_id):
		profile = Profile.objects(user=user_id).first()
		team = profile.LOLTeam
		# prevent bad request
		if team.captain != profile:
			raise InvalidUsage('Unauthorized',401)
		uploaded_file = request.files['upload']
		filename = "_".join([user_id, uploaded_file.filename])
		conn = boto.connect_s3('AKIAJAQHGWIZDOAEQ65A', 'FpmnFv/jte9ral/iXHtL8cDUnuKXAgAqp9aXVQMI')
		bucket = conn.get_bucket('team-icon')
		key = bucket.new_key(filename)
		key.set_contents_from_file(uploaded_file)
		team.teamIcon = 'https://s3-us-west-2.amazonaws.com/team-icon/%s' %filename
		team.save()
		return team_serialize(team)
コード例 #18
0
ファイル: challongeAPI.py プロジェクト: bintao/cteemo
	def post(self, user_id):
		args = profileParser.parse_args()
		tournamentId = args['tournamentId']

		profile = Profile.objects(user=user_id).first()
		if profile.LOLTeam is None:
			raise InvalidUsage()

		team = profile.LOLTeam
		if team.captain is not profile:
			raise InvalidUsage()

		challonge.participants.create(tournamentId,team.teamName)
		#rongcloudJoinGroup()
		return {'status' : 'success'}
コード例 #19
0
ファイル: lol_teamAPI.py プロジェクト: bintao/cteemo
	def post(self, user_id):
		args = teamParser.parse_args()
		profileID = args['profileID']
		teamIntro = args['teamIntro']
	
		profile = Profile.objects(user=user_id).first()
		if profileID == profile.id:
			raise InvalidUsage('Cannot send request to yourself')

		team = profile.LOLTeam
		if profileID is None and teamIntro is not None:
			if team.captain is not profile:
				raise InvalidUsage('Unauthorized',401)
			team.teamIntro = teamIntro
			team.save()
			return {'status' : 'success'}
		# avoid illegal operation
		if team is None:
			abort(400)
		if team.captain != profile:
			raise InvalidUsage('Unauthorized',401)
		# query the player u want to invite
		profile = Profile.objects(id=profileID).first()
		if profile is None:
			raise InvalidUsage('Member not found',404)
		try:
			assert len(team.members) < 6
		except:
			raise InvalidUsage('Team is full',403)
		request = Request.objects(user=profile.user,type='invite').only('requests_list').first()
		if request is None:
			request = Request(user=profile.user,type='invite')
			request.save()
		request.update(add_to_set__requests_list=team.captain)

		return {'status' : 'success'}
コード例 #20
0
ファイル: rongcloudAPI.py プロジェクト: bintao/cteemo
def rongRefresh(profile_id):
    profile = Profile.objects(id=profile_id).first()
    if profile is None:
        raise InvalidUsage("Wrong action",401)

    user = profile.user
    if user.rongToken is None:
        return rongcloudToken(profile_id)
    api.call_api(
            action="/user/refresh",
            params={
                "userId": profile_id,
                "name": profile.username,
                "portraitUri": profile.profile_icon
            }
        )
コード例 #21
0
ファイル: lol_teamAPI.py プロジェクト: bintao/cteemo
	def post(self, user_id):
		profile = Profile.objects(user=user_id).first()
		if profile.LOLTeam is not None:
			raise InvalidUsage('Only one team per game allowed')
		args = teamParser.parse_args()
		teamName = args['teamName']
		teamIntro = args['teamIntro']
		isSchool = args['isSchool']
		school = args['school']
		team = LOLTeam(teamName=teamName,teamIntro=teamIntro,captain=profile,isSchool=isSchool)
		if isSchool is True:
			team.school = school
		try:
			team.save()
		except ValidationError, e:
			raise InvalidUsage(e.message)  
コード例 #22
0
ファイル: postAPI.py プロジェクト: bintao/cteemo
	def post(self, user_id):
		args = postParser.parse_args()
		content = args['content']

		profile = Profile.objects(user=user_id).first()
		team = profile.LOLTeam

		if team is None:
			raise InvalidUsage('No team')

		if team.captain != profile:
			raise InvalidUsage('Unauthorized',401)

		post = TeamPost(user_profile=profile, team=team)
		post.save()

		return {'status' : 'success'}
コード例 #23
0
ファイル: tournamentAPI.py プロジェクト: bintao/cteemo
	def post(self, user_id):
		profile = Profile.objects(user=user_id).first()
		team = profile.LOLTeam
		if team is None:
			raise InvalidUsage('Team not found',404)
		if team.captain != profile:
			raise InvalidUsage('Unauthorized',401)
		if team.inGame is False:
			raise InvalidUsage('Not in a tournament')

		args = matchParser.parse_args()
		matchID = args['matchID']
		win = args['win']

		match = MatchHistory.objects(id=matchID).first()
		if team == match.teams[0]:
			index = 0
		else:
			index = 1
		if win is True:
			match.scores[index] += 0.5
		else:
			match.scores[index^1] += 0.5

		round = match.round
		if match.scores[0] > 0.5*round.bestOfN or match.scores[1] > 0.5*round.bestOfN:
			if int(match.scores[0]) != match.scores[0]:
				return {'status' : 'Dispute needs screen shots verifying'}
			if match.scores[0] > match.scores[1]:
				win_team = match.teams[0]
				lose_team = match.teams[1]
			else:
				win_team = match.teams[1]
				lose_team = match.teams[0]

			lose_team.inGame = False
			lose_team.save()
			round = round.next
			if round is None:
				win_team.inGame = False
				win_team.save()
				return {'status' : 'Tournament Ended'}
			update(win_team,round)

		return {'status' : 'success', 'message' : 'Please get tournament code for the next game'}
コード例 #24
0
ファイル: lol_teamAPI.py プロジェクト: bintao/cteemo
	def delete(self, user_id):
		profile = Profile.objects(user=user_id).first()
		if profile.LOLTeam is None:
			raise InvalidUsage('Not joined any team yet')

		team = profile.LOLTeam
		if team.captain == profile:
			raise InvalidUsage('Captain is not allowed to quit')
	
		team.update(pull__members=profile)

		profile.LOLTeam = None
		profile.save()
		team.save()

		rongcloudLeaveGroup(profile.id,team.id)

		return {'status' : 'success'}
コード例 #25
0
ファイル: lol_teamAPI.py プロジェクト: bintao/cteemo
	def delete(self, user_id):
		profile = Profile.objects(user=user_id).first()
		if profile.LOLTeam is None:
			raise InvalidUsage('Not joined any team yet')
		team = profile.LOLTeam
		if team.captain != profile:
			raise InvalidUsage('Unauthorized',401)
		# update members' profiles
		for member in team.members:
			member.LOLTeam = None
			member.save()

		profile.LOLTeam = None
		profile.save()
		team.delete()

		rongcloudDismissGroup(profile.id,team.id)

		return {'status' : 'success'}
コード例 #26
0
ファイル: profileAPI.py プロジェクト: bintao/cteemo
    def post(self, user_id):
        uploaded_file = request.files['upload']
        filename = "_".join([user_id, uploaded_file.filename])

        conn = boto.connect_s3('AKIAJAQHGWIZDOAEQ65A', 'FpmnFv/jte9ral/iXHtL8cDUnuKXAgAqp9aXVQMI')
        bucket = conn.get_bucket('profile-icon')
        key = bucket.new_key(filename)
        key.set_contents_from_file(uploaded_file)

        profile = Profile.objects(user=user_id).first()
        if profile is None:
            profile = Profile(user=user_id, profile_icon='https://s3-us-west-2.amazonaws.com/profile-icon/%s' %filename)
            profile.save()
        else:
            profile.profile_icon = 'https://s3-us-west-2.amazonaws.com/profile-icon/%s' %filename
            profile.save()

        rongRefresh(profile.id)
        
        return serialize(profile)
コード例 #27
0
ファイル: rongcloudAPI.py プロジェクト: bintao/cteemo
def rongcloudToken(profile_id):
    # load profile 
    profile = Profile.objects(id=profile_id).first()
    if profile is None:
    	raise InvalidUsage("Wrong action",401)

    user = profile.user

    token = api.call_api(
    action="/user/getToken",
    params={
        "userId": profile_id,
        "name":profile.username,
        "portraitUri":profile.profile_icon
        }
    )

    user.rongToken = token['token']
    user.save()
    return token
コード例 #28
0
ファイル: userAPI.py プロジェクト: bintao/cteemo
    def post(self):
        args = userParser.parse_args()
        email = args['email']
        password = args['password']
        if email is None or password is None:
            abort(400)
  
        user = User.objects(email=email).first()

        if not user or not user.verify_password(password):
            raise InvalidUsage('Email and password do not match')
        if not user.is_activated:
            raise InvalidUsage('Account not activated')

        profile = Profile.objects(user=user.id).first()

        rongToken = rongcloudToken(profile.id)
        token = user.generate_auth_token()
        redis_store.set(str(user.id), token)
        return {'token': token, 'rongToken' : rongToken}
コード例 #29
0
ファイル: lol_teamAPI.py プロジェクト: bintao/cteemo
	def post(self, user_id):
		args = teamParser.parse_args()
		profile = Profile.objects(user=user_id).first()
		if profile.LOLTeam is not None:
			raise InvalidUsage('Already joined a team')

		teamName = args['teamName']
		team = LOLTeam.objects(teamName=teamName).first()

		if team is None:
			raise InvalidUsage('Team not found',404)

		captain = team.captain
		request = Request.objects(user=captain.user,type='join').only('requests_list').first()
		if request is None:
			request = Request(user=captain.user,type='join')
			request.save()
		request.update(add_to_set__requests_list=profile)

		return {'status' : 'success'}
コード例 #30
0
ファイル: passwordAPI.py プロジェクト: bintao/cteemo
    def post(self):
        args = forgetPasswordParser.parse_args()
        email = args['email']
        username = args['username']
        school = args['school']

        if email is None or username is None or school is None:
            abort(400)

        user = User.objects(email=email).first()
        if user is None:
            raise InvalidUsage('User not found',404)

        profile = Profile.objects(user=user).first()
        if not profile.checkInfo(username, school):
            raise InvalidUsage('Information does not match',401)

        token = user.generate_auth_token(expiration=360000)
        send_forget_password_email(email, token)

        return {'status': 'success', 'message': 'An email has been sent to you letting you reset password'}
コード例 #31
0
ファイル: tournamentAPI.py プロジェクト: bintao/cteemo
	def delete(self, user_id):
		args = tournamentParser.parse_args()
		tournamentID = args['tournamentID']
		if tournamentID is None:
			raise InvalidUsage('Provide tournament id please')

		profile = Profile.objects(user=user_id).first()

		tournament = Tournament.objects(id=tournamentID).first()
		if tournament is None:
			raise InvalidUsage('Tournament not found',404)
		if tournament.creator != profile:
			raise InvalidUsage('Unauthorized',401)
		# More deletions need to be added
		rule = Rule.objects(tournament=tournament).first()
		if rule is not None:
			rule.delete()
		for round in tournament.rounds:
			round.delete()

		tournament.delete()
		return {'status':'success'}
コード例 #32
0
    def post(self, user_id):
        """
        Edit the user's profile if the profile exists
        Otherwise, create a new profile document
        """
        args = profileParser.parse_args()
        username = args['username']
        school = args['school']
        intro = args['intro']

        profile = Profile.objects(user=user_id).first()
        if profile is None:
            profile = Profile(
                user=user_id, username=username, school=school, intro=intro)
            profile.save()
        else:
            profile.username = username
            profile.school = school
            profile.intro = intro
            profile.save()

        return serialize(profile)
コード例 #33
0
ファイル: tournamentAPI.py プロジェクト: bintao/cteemo
	def post(self, user_id):
		args = tournamentParser.parse_args()
		tournamentName = args['tournamentName']
		isSchool = args['isSchool']
		school = args['school']
		descriptions = args['descriptions']
		entryFee = args['entryFee']
		size = args['size']
		try:
			rounds = request.json['rounds']
		except:
			raise InvalidUsage('Round information is not valid')
		teamSize = args['teamSize']
		#group_stage = args['group_stage']
		map = args['map']
		pick = args['pick']
		roundNumber = int(math.log(size,2))
		totalPrize = args['totalPrize']
		profile = Profile.objects(user=user_id).first()

		if roundNumber != math.log(size,2):
			return {'status' : 'error', 'message' : 'size not acceptable'}

		tournament = Tournament(tournamentName=tournamentName,isSchool=isSchool,descriptions=descriptions,entryFee=entryFee,size=size,school=school,creator=profile,totalPrize=totalPrize)

		for i in range(roundNumber):
			round = Round(roundName=str(Fraction(2*(i+1)/size))+' Final', startTime=rounds[i]['startTime'], bestOfN=rounds[i]['bestOfN'])
			round.save()
			tournament.rounds.append(round)
		for i in range(roundNumber-1):
			tournament.rounds[i].next = tournament.rounds[i+1]
		tournament.rounds[roundNumber-1].next = None


		try:
			tournament.save()
		except ValidationError,e:
			raise InvalidUsage(e.message)
コード例 #34
0
ファイル: tournamentAPI.py プロジェクト: bintao/cteemo
	def post(self, user_id):
		args = matchParser.parse_args()
		matchID = args['matchID']
		gameID = args['gameID']

		match = MatchHistory.objects(id=matchID).first()
		if match is None:
			raise InvalidUsage('Match not found',404)
		profile = Profile.objects(user=user_id).first()
		team = profile.LOLTeam
		# prevent bad request
		if team.captain != profile:
			raise InvalidUsage('Unauthorized',401)
		
		uploaded_file = request.files['upload']
		filename = "_".join([team.teamName, matchID, gameID, uploaded_file.filename])
		conn = boto.connect_s3('AKIAJAQHGWIZDOAEQ65A', 'FpmnFv/jte9ral/iXHtL8cDUnuKXAgAqp9aXVQMI')
		bucket = conn.get_bucket('lol-reports')
		key = bucket.new_key(filename)
		key.set_contents_from_file(uploaded_file)
		team.teamIcon = 'https://s3-us-west-2.amazonaws.com/lol-reports/%s' %filename
		team.save()
		return team_serialize(team)
コード例 #35
0
    def post(self, user_id):
        """
        Edit the user's profile if the profile exists
        Otherwise, create a new profile document
        """
        args = profileParser.parse_args()
        username = args['username']
        school = args['school']
        intro = args['intro']

        profile = Profile.objects(user=user_id).first()
        if profile is None:
            profile = Profile(user=user_id,
                              username=username,
                              school=school,
                              intro=intro)
            profile.save()
        else:
            profile.username = username
            profile.school = school
            profile.intro = intro
            profile.save()

        return serialize(profile)