示例#1
0
 def google_storage(self):
     try:
         return self._google_storage
     except AttributeError:
         pass
     self._google_storage = Storage(CredentialsModel, 'id', self.request.user, 'credential')
     return self._google_storage
示例#2
0
def events_get(request):
    REDIRECT_URI = "https://%s%s" % (request.get_host(),
                                     reverse("scheduler:events_return"))
    # FLOW = flow_from_clientsecrets(
    # 		CLIENT_SECRETS,
    # 		scope=SCOPES,
    # 		redirect_uri=REDIRECT_URI
    # 	)
    FLOW = OAuth2WebServerFlow(
        client_id=
        '323423619559-orlpuuiaalb7sp3ooblt4mjmp32ffq1t.apps.googleusercontent.com',
        client_secret=os.environ['CLIENT_SECRET'],
        scope=SCOPES,
        redirect_uri=REDIRECT_URI)
    user = request.user
    storage = Storage(CredentialsModel, 'id', user, 'credential')
    credential = storage.get()
    if credential is None or credential.invalid is True:
        FLOW.params['state'] = xsrfutil.generate_token(settings.SECRET_KEY,
                                                       user)
        authorize_url = FLOW.step1_get_authorize_url()
        f = FlowModel(id=user, flow=FLOW)
        f.save()
        return HttpResponseRedirect(authorize_url)
    else:
        http = httplib2.Http()
        http = credential.authorize(http)
        service = build('calendar', 'v3', http=http)
        events = service.events().list(calendarId='primary').execute()
        return Response(events)
def index(request):

  '''I Have created a static user as I dont have any logged in user in my app right now'''
  U = User(
      username = '******',
      firstname= 'Bla Bla',
      lastname= 'Bla Bla',
      email = '*****@*****.**'
  )
  U.save()

  #This is a class created by google to save the credentials automatically in the database
  storage = Storage(CredentialsModel, 'id', U, 'credential')
  credential = storage.get()
  if credential is None or credential.invalid == True:
    FLOW.params['state'] = xsrfutil.generate_token(settings.SECRET_KEY,
                                                   U)
    authorize_url = FLOW.step1_get_authorize_url()
    print(authorize_url)
    return HttpResponseRedirect(authorize_url)
  else:
    http = httplib2.Http()
    http = credential.authorize(http)
    service = build('drive', 'v3', http=http)

    #GOOGLE DRIVE FUNCTION CALLS
    id = creatingNewFolder(service)
    id1 = creatingFolderInsideAFolder(service, id)
    print("Successful")
    return HttpResponse("Folder Id: "+id+" Subfolder Id: "+id1)
def get_credentials(gmail_account):
    """
    Get the credentials for the given EmailAccount, what should be a Gmail account.

    If there are no valid credentials for the account, is_authorized is set to
    False and there will be an InvalidCredentialsError raised.

    Arguments:
        gmail_account (instance): EmailAccount instance

    Returns:
        credentials for the EmailAccount

    Raises:
        InvalidCredentialsError, if there are no valid credentials for the account.

    """
    storage = Storage(GmailCredentialsModel, 'id', gmail_account, 'credentials')
    credentials = storage.get()

    if credentials is not None and credentials.invalid is False:
        return credentials
    else:
        gmail_account.is_authorized = False
        gmail_account.save()
        logger.error('No or invalid credentials for account %s' % gmail_account)
        raise InvalidCredentialsError('No or invalid credentials for account %s' % gmail_account)
示例#5
0
def index(request):
    """
    Step 1 of Google OAuth 2.0 flow.
    """
    # Create and store the per-user flow object
    FLOW = flow_from_clientsecrets(
        CLIENT_SECRETS,
        scope=request.session.get('gauth_scope', ''),
        redirect_uri=request.build_absolute_uri(reverse('gauth_callback')))

    FLOW.params['access_type'] = 'offline'
    FLOW.params['approval_prompt'] = 'force'  # Properly gets refresh token
    FLOW.params['include_granted_scopes'] = 'true'  # incremental authorization
    FLOW.params['state'] = xsrfutil.generate_token(settings.SECRET_KEY,
                                                   request.user)

    authorize_url = FLOW.step1_get_authorize_url()
    flow_storage = Storage(FlowModel, 'id', request.user, 'flow')
    flow_storage.put(FLOW)

    # Don't worry if a next parameter is not set. `auth_return` will use the
    # homepage by default. Allows for `next_view` to be set from other places.
    try:
        request.session['next_view'] = request.GET['next']
    except KeyError:
        pass

    return redirect(authorize_url)
示例#6
0
def run_subs(ffu):
    storage = Storage(CredentialsModel, 'id', ffu.credentials, 'credential')
    credential = storage.get()
    if credential is None or credential.invalid == True:
        raise Exception("invalid credentials")
    http = httplib2.Http()
    http = credential.authorize(http)
    service = discovery.build('gmail', 'v1', http)
    output = ListRecentMessagesMatchingQuery(service, "me",
                                             '"has subscribed to you"')
    added = 0
    for item in output:
        if SubEvent.objects.filter(external_id=item['id'],
                                   updater=ffu).count():
            break
        message = GetMessage(service, "me", item['id'])
        headers = message['payload']['headers']
        f, s, d = '', '', ''
        for header in headers:
            if header['name'] == "From": f = header['value']
            if header['name'] == "Subject": s = header['value']
            if header['name'] == "Date": d = header['value']
        if '*****@*****.**' in f:
            s = s.strip().replace(" has subscribed to you on YouTube!", "")
            try:
                e = SubEvent(external_id=item['id'], details=s, updater=ffu)
                e.save()
                added += 1
            except Exception, E:
                print "Failed to create specific subevent for %s: \n    %s: %s" % (
                    ffu.id, type(E), E)
示例#7
0
文件: support.py 项目: shanz/mirandum
def run_fan_funding(ffu):
    added = 0
    storage = Storage(CredentialsModel, 'id', ffu.credentials, 'credential')
    credential = storage.get()
    if credential is None or credential.invalid == True:
        raise Exception("bad creds")
        return added
    http = httplib2.Http()
    http = credential.authorize(http)
    resp, data = http.request("%sfanFundingEvents?part=snippet&maxResults=5" %
                              BASE_URL)
    data = json.loads(data)
    events = []
    if 'items' in data:
        for i in data['items']:
            if FanFundingEvent.objects.filter(external_id=i['id'],
                                              updater=ffu).count() > 0:
                break

            details = json.dumps(i)
            try:
                ffe = FanFundingEvent(external_id=i['id'],
                                      updater=ffu,
                                      details=details)
                ffe.save()
            except Exception, E:
                print "Failed in individual fan funding run: %s\nData:\n%s" % (
                    E, details)
            added += 1
示例#8
0
def auth(request):
    # use the first REDIRECT_URI if you are developing your app locally, and the second in production
    #REDIRECT_URI = 'http://localhost:8000/gmail_setup/callback'
    REDIRECT_URI = "http://%s%s" % (BASE_URL, reverse("oauth2:return")) 
    #REDIRECT_URI = 'https://' + BASE_URL + '/gmail_setup/callback'
    print "ACCESSING CLIENT SECRETS"
    with open(CLIENT_SECRETS) as json_data:
        d = json.load(json_data)
        print "DATA:", d

    FLOW = flow_from_clientsecrets(
        CLIENT_SECRETS,
        scope='https://www.googleapis.com/auth/contacts.readonly https://www.googleapis.com/auth/gmail.readonly https://www.googleapis.com/auth/gmail.settings.basic https://www.googleapis.com/auth/gmail.modify',
        redirect_uri=REDIRECT_URI
    )

    FLOW.params['access_type'] = 'offline'

    user = request.user
    storage = Storage(CredentialsModel, 'id', user, 'credential')
    credential = storage.get()
    if not credential or credential.invalid:
        if 'group' in request.GET:
            group_name = request.GET['group']
            FLOW.params['state'] = xsrfutil.generate_token(settings.SECRET_KEY, user) + '___' + group_name
            authorize_url = FLOW.step1_get_authorize_url()
            f = FlowModel(id=user, flow=FLOW)
            f.save()
            return HttpResponseRedirect(authorize_url)
        else:
            return HttpResponseRedirect("/gmail_setup")
    else:
        return HttpResponseRedirect("/gmail_setup")
示例#9
0
    def get(self, request):
        # Get the state param from the request.
        state = str(request.GET.get('state'))
        # Replace %xx characters with single quotes and UTF8 decode the string.
        state = urllib.unquote(state).decode('utf8')
        # Deserialize the JSON string.
        state = anyjson.deserialize(b64decode(state))

        if not validate_token(settings.SECRET_KEY, state.get('token'),
                              request.user.pk):
            return HttpResponseBadRequest()

        error = request.GET.get('error')
        if error:
            messages.error(
                self.request,
                _('Sorry, Lily needs authorization from Google to synchronize your email account.'
                  ))
            return HttpResponseRedirect('/#/preferences/emailaccounts')

        credentials = FLOW.step2_exchange(code=request.GET.get('code'))

        # Setup service to retrieve email address.
        service = build_gmail_service(credentials)
        profile = service.users().getProfile(userId='me').execute()

        # Create account based on email address.
        account, created = EmailAccount.objects.get_or_create(
            owner=request.user, email_address=profile.get('emailAddress'))

        # Store credentials based on new email account.
        storage = Storage(GmailCredentialsModel, 'id', account, 'credentials')
        storage.put(credentials)

        # Set account as authorized.
        account.is_authorized = True
        account.is_deleted = False

        account.label = account.label or account.email_address
        account.from_name = account.from_name or ' '.join(
            account.email_address.split('@')[0].split('.')).title()

        set_to_public = bool(int(state.get('public')))
        if account.public is not set_to_public:
            account.public = set_to_public

        only_sync_new_mails = bool(int(state.get('only_new')))
        if only_sync_new_mails and created:
            # Setting it before the first sync means it will only fetch changes starting now.
            account.history_id = profile.get('historyId')
            account.full_sync_finished = True

        account.save()

        post_intercom_event(event_name='email-account-added',
                            user_id=request.user.id)

        return HttpResponseRedirect('/#/preferences/emailaccounts/edit/%s' %
                                    account.pk)
示例#10
0
def get_access_token(credentials, integration_type, code=None):
    """
    Generic function to retrieve an OAuth 2.0 access token.

    Args:
        credentials (object): Contains the OAuth 2.0 credentials needed to retrieve a token.
        integration_type (str): Name of the integration for which the credentials should be retrieved.
        code (str, optional): Authorization code which will be exchanged for an access token.

    Returns:
        credentials (object): Updated credentials with a new access token.
    """
    payload = {
        'client_id': credentials.client_id,
        'client_secret': credentials.client_secret,
    }

    if code:
        # Trade the auth code for an access token.
        payload.update({
            'grant_type': 'authorization_code',
            'redirect_uri': credentials.redirect_uri,
            'code': code
        })
    else:
        # Already authenticated once, but access token expired.
        payload.update({
            'grant_type': 'refresh_token',
            'refresh_token': credentials.refresh_token
        })

    integration_type = IntegrationType.objects.get(
        name__iexact=integration_type)

    response = requests.post(
        url=integration_type.token_url,
        data=payload,
    )

    if response.status_code == 200:
        data = response.json()

        expires = data.get('expires_in')

        credentials.access_token = data.get('access_token')
        credentials.refresh_token = data.get('refresh_token')

        if expires:
            credentials.expires = timezone.now() + timedelta(seconds=expires)

        details = IntegrationDetails.objects.get(type=integration_type.id)
        storage = Storage(IntegrationCredentials, 'details', details,
                          'credentials')

        storage.put(credentials)

        return credentials
    else:
        return None
示例#11
0
    def test_storage(self):
        storage = Storage(CredentialsModel, 'user', self.user_with_analytics,
                          'credential')
        credential = storage.get()

        self.assertIsInstance(storage, Storage)
        self.assertIsInstance(credential, OAuth2Credentials)
        self.assertEqual(credential.access_token, self.credential.access_token)
示例#12
0
def finalize(request):
    ac = AppCreds(user=request.user, label=request.POST['label'])
    ac.save()
    credential = FLOW.step2_exchange(request.POST)
    internal_label = "%s-%s" % (request.user.id, request.POST['label'])
    storage = Storage(CredentialsModel, 'id', ac, 'credential')
    storage.put(credential)
    return HttpResponseRedirect("/accounts/")
示例#13
0
    def test_constructor(self):
        storage = Storage(FakeCredentialsModel, self.key_name, self.key_value,
                          self.property_name)

        self.assertEqual(storage.model_class, FakeCredentialsModel)
        self.assertEqual(storage.key_name, self.key_name)
        self.assertEqual(storage.key_value, self.key_value)
        self.assertEqual(storage.property_name, self.property_name)
示例#14
0
def auth_return(request):
    if not xsrfutil.validate_token(settings.SECRET_KEY,
                                   request.REQUEST['state'], request.user):
        return HttpResponseBadRequest()
    credential = FLOW.step2_exchange(request.REQUEST)
    storage = Storage(CredentialsModel, 'id', request.user, 'credential')
    storage.put(credential)
    return HttpResponseRedirect("/")
示例#15
0
def index(request):
    '''I Have created a static user as I dont have any logged in user in my app right now'''
    U = User(username='******',
             firstname='Bla Bla',
             lastname='Bla Bla',
             email='*****@*****.**')
    U.save()

    #This is a class created by google to save the credentials automatically in the database
    storage = Storage(CredentialsModel, 'id', U, 'credential')
    credential = storage.get()
    if credential is None or credential.invalid == True:
        FLOW.params['state'] = xsrfutil.generate_token(settings.SECRET_KEY, U)
        authorize_url = FLOW.step1_get_authorize_url()
        print(authorize_url)
        return HttpResponseRedirect(authorize_url)
    else:
        http = httplib2.Http()
        http = credential.authorize(http)
        service = build('calendar', 'v3', http=http)
        listEvents(service)
        '''Just For Testing'''
        event = {
            'summary': 'Google I/O 2015',
            'location': '800 Howard St., San Francisco, CA 94103',
            'description':
            'A chance to hear more about Google\'s developer products.',
            'start': {
                'dateTime': '2016-04-07T09:00:00-07:00',
                'timeZone': 'America/Los_Angeles',
            },
            'end': {
                'dateTime': '2016-04-08T17:00:00-07:00',
                'timeZone': 'America/Los_Angeles',
            },
            'recurrence': ['RRULE:FREQ=DAILY'],
            'attendees': [{
                'email': '*****@*****.**'
            }],
            'reminders': {
                'useDefault':
                False,
                'overrides': [
                    {
                        'method': 'email',
                        'minutes': 24 * 60
                    },
                    {
                        'method': 'popup',
                        'minutes': 10
                    },
                ],
            },
        }

        link = addEvent(service, event)
        print("Successful")
        return HttpResponse(link)
示例#16
0
文件: api.py 项目: soyapark/murmur
def update_gmail_filter(user, group_name, whitelist_emails, filter_hash):

    storage = Storage(CredentialsModel, 'id', user, 'credential')
    credential = storage.get()
    http = httplib2.Http()
    http = credential.authorize(http)
    service_mail = build('gmail', 'v1', http=http)
    forward_address = '%s@%s' % (group_name, BASE_URL)
    return True
示例#17
0
def auth_return(request):
    print settings.SECRET_KEY, request.GET['state'], request.user
    if not xsrfutil.validate_token(settings.SECRET_KEY,
                                   str(request.GET['state']), request.user):
        return HttpResponseBadRequest()
    credential = FLOW.step2_exchange(request.GET)
    print credential, request.user
    storage = Storage(CredentialsModel, 'id', request.user, 'credential')
    storage.put(credential)
    return HttpResponseRedirect("/fitcraft")
示例#18
0
def oauth2callback(request):
    if not xsrfutil.validate_token(settings.SECRET_KEY,
                                   str(request.GET['state']), request.user):
        return HttpResponseBadRequest()

    credential = FLOW.step2_exchange(request.GET)
    storage = Storage(GoogleCredentialsModel, 'id', request.user, 'credential')
    storage.put(credential)
    redirect_url = request.session['redirect_uri_after_step2']
    return HttpResponseRedirect(redirect_url)
示例#19
0
    def process_exception(self, request, exception):
        if isinstance(exception, AccessTokenRefreshError):
            storage = Storage(CredentialsModel, 'user', request.user, 'credential')
            storage.delete()

            return HttpResponseBadRequest(
                content=b'{"message": "%s"}' % exception.message,
                content_type='application/json')

        return None
示例#20
0
def _get_google_credential(request):
    token = None
    if request.user.is_authenticated():
        storage = Storage(TokenStorageModel, 'id', request.user, 'credential')
        credential = storage.get()
    elif request.session.get('access_token'):
        credential = google_client.OAuth2Credentials.from_json(token)

    return credential or HttpResponseRedirect(
        google_flow.step1_get_authorize_url())
示例#21
0
def build_services(user):
    storage = Storage(CredentialsModel, 'id', user, 'credential')
    credential = storage.get()
    if credential and not credential.invalid:
        http = httplib2.Http()
        http = credential.authorize(http)
        credential.refresh(http)
        service_people = build('people', 'v1', http=http)
        service_mail = build('gmail', 'v1', http=http)
        return {'mail' : service_mail, 'people' : service_people}
示例#22
0
def deauth(request):
    user = request.user
    credential = None
    storage = Storage(CredentialsModel, 'id', user, 'credential')
    storage.delete()
    if 'group' in request.GET:
        group_name = request.GET['group']
        return HttpResponseRedirect("authorize?group=" + group_name)
    else:
        return HttpResponseRedirect("authorize")
示例#23
0
def auth_return(request):
    user = request.user
    if not xsrfutil.validate_token(settings.SECRET_KEY,
                                   str(request.GET['state']), user):
        return HttpResponseBadRequest()
    FLOW = FlowModel.objects.get(id=user).flow
    credential = FLOW.step2_exchange(request.GET)
    storage = Storage(CredentialsModel, 'id', user, 'credential')
    storage.put(credential)
    return HttpResponseRedirect("/scheduler/home")
示例#24
0
    def post(self, request, integration_type):
        """
        Get the authentication URL for the given integration type.
        """
        client_id = request.POST.get('client_id')
        client_secret = request.POST.get('client_secret')
        integration_context = request.POST.get('integration_context')

        if integration_context:
            integration_context = anyjson.loads(integration_context)

        errors = {}

        if not client_id:
            errors.update({
                'client_id': ['Please enter a valid client ID'],
            })

        if not client_secret:
            errors.update({
                'client_secret': ['Please enter a valid client secret'],
            })

        if errors:
            return HttpResponseBadRequest(anyjson.serialize(errors), content_type='application/json')

        integration_type = IntegrationType.objects.get(name__iexact=integration_type)
        redirect_uri = request.build_absolute_uri()

        params = {
            'client_id': client_id,
            'client_secret': client_secret,
            'redirect_uri': redirect_uri,
            'scope': integration_type.scope,
            'response_type': 'code',
        }

        details, created = IntegrationDetails.objects.get_or_create(type=integration_type)

        storage = Storage(IntegrationCredentials, 'details', details, 'credentials')

        credentials = LilyOAuthCredentials(
            client_id=client_id,
            client_secret=client_secret,
            redirect_uri=redirect_uri,
            integration_context=integration_context,
        )

        storage.put(credentials)

        auth_url = integration_type.auth_url + urllib.urlencode(params)

        response = anyjson.serialize({'url': auth_url})

        return HttpResponse(response, content_type='application/json')
示例#25
0
def auth_return(request):
    user = request.user
    secret = request.REQUEST['state'].split('___')[0]
    group_name = request.REQUEST['state'].split('___')[1]
    if not xsrfutil.validate_token(settings.SECRET_KEY, str(secret), user):
        return HttpResponseBadRequest()
    FLOW = FlowModel.objects.get(id=user).flow
    credential = FLOW.step2_exchange(request.REQUEST)
    storage = Storage(CredentialsModel, 'id', user, 'credential')
    storage.put(credential)
    return HttpResponseRedirect("/gmail_setup?group=" + group_name)
示例#26
0
    def test_constructor(self):
        key_name = 'foo'
        key_value = 'bar'
        property_name = 'credentials'
        storage = Storage(FakeCredentialsModel, key_name, key_value,
                          property_name)

        self.assertEqual(storage.model_class, FakeCredentialsModel)
        self.assertEqual(storage.key_name, key_name)
        self.assertEqual(storage.key_value, key_value)
        self.assertEqual(storage.property_name, property_name)
示例#27
0
def auth_return(request):
    """
    Step 2 of Google OAuth 2.0 flow.
    """
    if 'state' not in request.REQUEST:
        return redirect('gauth_index')

    if not xsrfutil.validate_token(
            settings.SECRET_KEY, str(request.REQUEST['state']), request.user):
        return HttpResponseBadRequest()

    FLOW = Storage(FlowModel, 'id', request.user, 'flow').get()
    if FLOW is None:
        return redirect('gauth_index')

    credential = FLOW.step2_exchange(request.REQUEST)
    cred_storage = Storage(CredentialsModel, 'id', request.user, 'credential')
    cred_storage.put(credential)

    return redirect(request.session.get('next_view', '/'))
示例#28
0
    def test_locked_get_no_entities(self, djangoModel):
        entities = []
        filter_mock = mock.Mock(return_value=entities)
        object_mock = mock.Mock()
        object_mock.filter = filter_mock
        FakeCredentialsModelMock.objects = object_mock

        storage = Storage(FakeCredentialsModelMock, self.key_name,
                          self.key_value, self.property_name)
        credential = storage.locked_get()
        self.assertIsNone(credential)
示例#29
0
def ytapicall(appcreds, url):
    storage = Storage(CredentialsModel, 'id', appcreds, 'credential')
    credential = storage.get()
    if credential is None or credential.invalid == True:
        raise Exception("bad creds")
    http = httplib2.Http()
    http = credential.authorize(http)
    resp, data = http.request(url)
    data = json.loads(data)
    if 'error' in data:
        raise Exception("YouTube API Error: %s" % data['error'])
    return data 
示例#30
0
def import_gsheet(request, id):
    gsheet_endpoint = None
    silo = None
    read_url = request.GET.get('link', None)
    file_id = request.GET.get('resource_id', None)
    file_name = request.GET.get("name", "Google Sheet Import")
    if read_url == None or file_id == None:
        messages.error(request, "A Google Spreadsheet is not selected to import data from.")
        return HttpResponseRedirect(reverse('index'))

    storage = Storage(GoogleCredentialsModel, 'id', request.user, 'credential')
    credential = storage.get()
    if credential is None or credential.invalid == True:
        FLOW.params['state'] = xsrfutil.generate_token(settings.SECRET_KEY, request.user)
        authorize_url = FLOW.step1_get_authorize_url()
        #FLOW.params.update({'redirect_uri_after_step2': "/export_gsheet/%s/?link=%s&resource_id=%s" % (id, read_url, file_id)})
        request.session['redirect_uri_after_step2'] = "/import_gsheet/%s/?link=%s&resource_id=%s" % (id, read_url, file_id)
        return HttpResponseRedirect(authorize_url)

    credential_json = json.loads(credential.to_json())
    user = User.objects.get(username__exact=request.user)
    gsheet_endpoint = None
    read_type = ReadType.objects.get(read_type="GSheet Import")
    try:
        silo = Silo.objects.get(id=id)
        if silo.unique_fields.exists() == False:
            messages.error(request, "A unique column must be specfied when importing to an existing table. <a href='%s'>Specify Unique Column</a>" % reverse_lazy('silo_detail', kwargs={"id": silo.id}))
            return HttpResponseRedirect(request.META['HTTP_REFERER'])
    except Silo.DoesNotExist:
        silo = Silo(name=file_name, owner=request.user, public=False, description="Google Sheet Import")
        silo.save()

    try:
        gsheet_endpoint = Read.objects.get(silos__id=id, type=read_type, silos__owner=user.id, resource_id=file_id, read_name='GSheet Import')
    except Read.MultipleObjectsReturned:
        messages.error(request, "There should not be multiple records for the same gsheet, silo, and owner")
    except Read.DoesNotExist:
        gsheet_endpoint = Read(read_name="GSheet Import", type=read_type, resource_id=file_id, owner=user)
        gsheet_endpoint.read_url = read_url
        gsheet_endpoint.save()
        silo.reads.add(gsheet_endpoint)
        silo.save()
    except Exception as e:
        messages.error(request, "An error occured: %" % e.message)

    #print("about to export to gsheet: %s" % gsheet_endpoint.resource_id)
    if import_from_google_spreadsheet(credential_json, silo, gsheet_endpoint.resource_id) == True:
        link = "Your imported data is available at here. <a href='%s'>See the table</a>" % reverse_lazy('silo_detail', kwargs={"id": silo.id})
        messages.success(request, link)
    else:
        messages.error(request, 'Something went wrong.')
    #messages.success(request, "Now, it should import data from GSheet")
    return HttpResponseRedirect(reverse('index'))