I am building a small Jupyter Notebook widget to call a Rest API and get some data.
The user need to provide an auth token to be able to call the API. The auth token (JWT Token) will be provided to the user from a different interface. The API will respond with a json list of data which needs to be put into a combobox and then user can choose 1 which will be inserted into a new cell
The issue I am running into is that I am unable to tell the API call to wait for user to input the auth token and click on the submit button.
There are 2 actions happening here
Act 1 : Text Box and Button for the auth token. User puts the auth token in the text box and clicks submit.
Auth token gets put into a global variable by the button on clicked function
Act 2: Use the global variable to call the Rest API to get the JSON list of data
Put the list into a combobox
User chooses a specific value.
Dump that specific data into a new cell
import requests
import json
from IPython.display import Javascript, display
def click_get_auth_token(b):
global get_datasources_api_token
get_datasources_api_token = atok.value
return None
def show_wid():
global atok
atok = widgets.Text(
placeholder='Auth_Token',
description='Auth Token',
disabled=False
)
display(atok)
button = widgets.Button(description="Submit")
display(button)
button.on_click(click_get_auth_token)
return None
def click_get_auth_token():
show_wid()
headers = {}
resp = requests.get('http://localhost:5000/get_data', headers={"Authorization" : "Bearer " + get_datasources_api_token})
resp = resp.json()
dsr_list = resp['datasources']
opt_list = []
for dsr in dsr_list:
opt_list.append(dsr["name"])
print(opt_list)
global w
w = widgets.Combobox(
options= opt_list,
disabled=False,
ensure_option=True,
)
display(w)
button = widgets.Button(description="Get Data")
display(button)
button.on_click(click_get_datasources)
def click_get_datasources()
dataset = None
for dsr in dsr_list:
if(dsr["name"] == w.value):
dataset = dsr
encoded_code = (base64.b64encode(str.encode(dataset))).decode()
display(Javascript("""
var code = IPython.notebook.insert_cell_below('code');
code.set_text(atob("{0}"));
""".format(encoded_code)))
My code is probably all over the place and is needlessly complex.
At the moment it works as long as I hard code an auth token.
But I am unable to get the Rest call to wait for the user to paste the token.
Any help would be appreciated.
Thank you
Related
Trying to use the Bing Ads API to duplicate what I see on the Hourly report.
Unfortunately, even though I'm properly authenticated, the data I'm getting back is only for One Campaign (one which has like 1 impression per day). I can see the data in the UI just fine, but authenticated as the same user via the API, I can only seem to get back the smaller data set. I'm using https://github.com/BingAds/BingAds-Python-SDK and basing my code on the example:
def get_hourly_report(
account_id,
report_file_format,
return_only_complete_data,
time):
report_request = reporting_service.factory.create('CampaignPerformanceReportRequest')
report_request.Aggregation = 'Hourly'
report_request.Format = report_file_format
report_request.ReturnOnlyCompleteData = return_only_complete_data
report_request.Time = time
report_request.ReportName = "Hourly Bing Report"
scope = reporting_service.factory.create('AccountThroughCampaignReportScope')
scope.AccountIds = {'long': [account_id]}
# scope.Campaigns = reporting_service.factory.create('ArrayOfCampaignReportScope');
# scope.Campaigns.CampaignReportScope.append();
report_request.Scope = scope
report_columns = reporting_service.factory.create('ArrayOfCampaignPerformanceReportColumn')
report_columns.CampaignPerformanceReportColumn.append([
'TimePeriod',
'CampaignId',
'CampaignName',
'DeviceType',
'Network',
'Impressions',
'Clicks',
'Spend'
])
report_request.Columns = report_columns
return report_request
I'm not super familiar with these ad data APIs, so any insight will be helpful, even if you don't have a solution.
I spent weeks back and forth with Microsoft Support. Here's the result:
You can get logs out of the examples by adding this code:
import logging
logging.basicConfig(level=logging.INFO)
logging.getLogger('suds.client').setLevel(logging.DEBUG)
logging.getLogger('suds.transport').setLevel(logging.DEBUG)
The issue was related to the way the example is built. In the auth_helper.py file there is a method named authenticate that looks like this:
def authenticate(authorization_data):
# import logging
# logging.basicConfig(level=logging.INFO)
# logging.getLogger('suds.client').setLevel(logging.DEBUG)
# logging.getLogger('suds.transport.http').setLevel(logging.DEBUG)
customer_service = ServiceClient(
service='CustomerManagementService',
version=13,
authorization_data=authorization_data,
environment=ENVIRONMENT,
)
# You should authenticate for Bing Ads services with a Microsoft Account.
authenticate_with_oauth(authorization_data)
# Set to an empty user identifier to get the current authenticated Bing Ads user,
# and then search for all accounts the user can access.
user = get_user_response = customer_service.GetUser(
UserId=None
).User
accounts = search_accounts_by_user_id(customer_service, user.Id)
# For this example we'll use the first account.
authorization_data.account_id = accounts['AdvertiserAccount'][0].Id
authorization_data.customer_id = accounts['AdvertiserAccount'][0].ParentCustomerId
As you can see, at the very bottom, it says "For this example, we'll use the first account." It turns out that my company had 2 accounts. This was not configurable anywhere and I had no idea this code was here, but you can add a breakpoint here to see your full list of accounts. We only had 2, so I flipped the 0 to a 1 and everything started working.
We have a process to update a jira ticket which i have automated with the following code and using the JIRA package in python 6.4. Which worked perfectly... however they have added a new step to the process which requires me clicking an 'Approval' button in order for the 'customfield_12410' to appear in a separate pop up window with a load of other fields to update.
from jira.client import JIRA
jira_server = "http://jiraserver"
jira_password = f.read()
jira_user = getpass.getuser()
jira_server = {'server': jira_server}
jira = JIRA(options=jira_server, basic_auth=(jira_user, jira_password))
comment = "Test Results. Passes {0} Failed {1}".format(passed,failed)
# Get ticket information
jira_issue = jira.issue(ticketId)
jira_issue.update(fields={'customfield_12410': comment})
The error this code now generates is:
text: Field 'customfield_12410' cannot be set. It is not on the appropriate
screen, or unknown.
How do i click a button on the Jira ticket. By printing the raw contents of the ticket, i don't see anything resembling the button name.
print(jira_issue.raw)
Thanks,
John.
That button is likely a workflow transition. You can change the status using the REST API, examples at https://community.atlassian.com/t5/Jira-questions/JIRA-How-to-change-issue-status-via-rest/qaq-p/528133 and https://jira.readthedocs.io/en/master/examples.html#transitions
Resolved with the following code
from jira.client import JIRA
jira_server = "http://jiraserver"
jira_password = f.read()
jira_user = getpass.getuser()
jira_server = {'server': jira_server}
jira = JIRA(options=jira_server, basic_auth=(jira_user, jira_password))
comment = "Test Results. Passes {0} Failed {1}".format(passed,failed)
# Get ticket information
jira_issue = jira.issue(ticketId)
transitions = jira.transitions(jira_issue)
for t in transitions:
print(t['id'], t['name'])
Output:
12 Approval
14 Cancel
# Resolve the issue with the comment
jira.transition_issue(jira_issue, '12', fields={'customfield_12410': comment})
I have a bot in Python that prompts the user to pick from a list of IDs to change and the idea is that the user will select the ID and then supply the ID they want to change to.
Here is my code:
def build_menu(buttons, n_cols):
menu = [buttons[i:i + n_cols] for i in range(0, len(buttons), n_cols)]
return menu
def change_process(bot, update):
global action
query = update.callback_query
NAME = query.data
if action == 'Change':
# Prompt user for new value
# run sql query to update
def change(bot, update, args):
global action
action = 'Change'
sql = sqlite3.connect('SQL_FILE.db',detect_types=sqlite3.PARSE_DECLTYPES|sqlite3.PARSE_COLNAMES)
cur = sql.cursor()
button_list = [InlineKeyboardButton(s[0], callback_data=s[0]) for s in cur.execute('SELECT Name FROM ID ORDER BY Name ASC')]
reply_markup = InlineKeyboardMarkup(build_menu(button_list, n_cols=3))
update.message.reply_text('ID to change:', reply_markup=reply_markup)
action = None
updater = Updater(token='XXXX:XXXX')
dispatcher = updater.dispatcher
dispatcher.add_handler(CallbackQueryHandler(change_process))
arg_handler = CommandHandler('change', change, pass_args=True)
dispatcher.add_handler(arg_handler)
Here is how I want the program to work:
User runs /change
Bot returns list of IDs that are able to be changed (using InlineKeyboardMarkup() and build_menu()
User picks one of the ids
Bot prompts user for new value
The next message that the user sends to the bot will be the value to use
Bot will use the ID the user selected as well as new value and run query to update in database
Bot comments that it updated ID with new value of 'USER VALUE'
The problem I am having is that I can prompt the user to pick the ID and I can prompt the user for input, but I do not know how to do both and use both values in a single function.
I believe you need to look to conversation examples that python-telegram-bot library provides:
conversationbot.py
conversationbot2.py
nestedconversationbot.py
There are also diagrams that explains the flow of asking data from user.
I'm developing a webapp using Google App Engine and Python.
I'm facing a strange problem and i don't know how to solve it and what causes it.
When I fill a form I send the data for checking them. If they aren't complete and some fields are missed the server send the form back with an advice "FILL ALL THE FIELDS!".
That's work pretty well.
What I'm trying to do is sending the form back with the "description" and "title" fields filled with what the user has written before submitting the form, so he must to fill only the unfilled fields (and he doesn't need to rewrite everything from the beginning).
That's the code:
class SaleAnnCheck(BaseHandler):
def post(self):
title = self.request.POST.get('title')
cat = self.request.POST.get('cat')
description = self.request.POST.get('description')
AcqOpt = self.request.POST.get('AcqOpt')
lat = self.request.POST.get('lat')
lng = self.request.POST.get('lng')
image1 = self.request.POST.get("file1", None)
image2 = self.request.POST.get("file2", None)
image3 = self.request.POST.get("file3", None)
logging.info("info sale announcment")
logging.info(title)
logging.info(cat)
logging.info(description)
logging.info(AcqOpt)
logging.info(lat)
logging.info(lng)
logging.info(image1)
logging.info(image2)
logging.info(image3)
self.session["ECommerceUser"]["description"]=description
self.session["ECommerceUser"]["title"]=title
logging.info('session')
logging.info(self.session["ECommerceUser"])
if title == '' or cat == None or description == '' or AcqOpt == None or lat == '' or lng == '':
error = 'empty'
urlR='/CreateSaleAnn?'+urllib.urlencode({'error':'empty'})
self.redirect(urlR)
class Create(BaseHandler):
def get(self):
error = self.request.get('error')
if error == 'empty':
logging.info('sbagliato')
logging.info(self.session["ECommerceUser"])
template = JINJA_ENVIRONMENT.get_template('templates/CreateAnnouncement.html')
w = self.response.write
w(template.render({'error':'FILL ALL THE MANDATORY FIELDS!', 'description': self.session["ECommerceUser"]["description"], 'title': self.session["ECommerceUser"]["title"]}))
else:
logging.info('giusto')
logging.info(self.session["ECommerceUser"])
template = JINJA_ENVIRONMENT.get_template('templates/CreateAnnouncement.html')
w = self.response.write
w(template.render({'description': self.session["ECommerceUser"]["description"], 'title': self.session["ECommerceUser"]["title"]}))
When I submit the form the content is checked by making an HTTP post request to a certain URL, handled by SaleAnnCheck.
Description and Title are saved in the session correctly (i checked it by printing the content of self.session["ECommerceUser"] in the logs). Then, if a field isn't filled, the server redirect again to the form page, by a GET request to a related URL.
The requests to that URL is handled by Create. But when i try to render the HTML template of the form (using jinja2) with the previous typed values of Description and Title the related text areas are not filled with that values.
It happens because self.session["ECommerceUser"]["description"] and self.session["ECommerceUser"]["title"] are empty, but they weren't when i checked them before (in SaleAnnCheck).
Why it happens? Any explanation? It's a weird problem and there aren't any tips or suggestion about on internet
This is because 'self.session' is not a Session, it's just a class variable and will not be readable outside the class. If you really want to use persistent sessions for storing variables, try something like this:
From the docs:
http://docs.python-requests.org/en/master/user/advanced/
s = requests.Session()
s.get('http://httpbin.org/cookies/set/sessioncookie/123456789')
r = s.get('http://httpbin.org/cookies')
I know I have already asked a question like this before but I have made my code much cleaner and I am still coming up with a problem.
My code goes like this:
class Email_Stuff:
def Get_From_Email():
#code to open up window and get email address
emailaddr = #the input
return emailaddr
def Get_To_Email():
#code to open up window and get to email address
recipaddr = #the input
return recipaddr
def Get_Email_Address():
#code to open up window and get email username
EmailUser = #the input
return EmailUser
def Get_Email_Password():
#code to open up window and get email password
EmailPass = #the input
return EmailPass
def Send_Email():
import smtplib
server = smtplib.SMTP('smtp.gmail.com', 587)
server.login((EmailUser),(EmailPass))
message = "Python Test Email"
server.sendmail(emailaddr,recipaddr,message)
I need to get the variables: emailaddr, recipaddr, EmailUser, and EmailPass into the function Send_Email. I'm not sure how I could do that though because when I run this code, it tells me that "the global name isn't defined".
Any ideas?
Make emailaddr, recipaddr, EmailUser, and EmailPass become instance variables by adding prefix "self.".
class Email_Stuff():
def Get_From_Email(self):
#code to open up window and get email address
self.emailaddr = #the input
def Get_To_Email(self):
#code to open up window and get to email address
self.recipaddr = #the input
def Get_Email_Address(self):
#code to open up window and get email username
self.EmailUser = #the input
def Get_Email_Password(self):
#code to open up window and get email password
self.EmailPass = #the input
def Send_Email(self):
import smtplib
server = smtplib.SMTP('smtp.gmail.com', 587)
server.login((self.EmailUser),(self.EmailPass))
message = "Python Test Email"
server.sendmail(self.emailaddr,self.recipaddr,self.message)
instance = Email_Stuff()
instance.Get_From_Email()
instance.Get_To_Email()
instance.Get_Email_Address()
instance.Get_Email_Password()
instance.Send_Email()
BTW, name of methods should be lowercase.
First, if you want these functions to methods associated with an instance of this class, then each method will need a reference to the instance itself as the first argument, usually designated as self, though you can name it anything you like.
For example:
def Get_Email_Password(self):
#code to open up window and get email password
EmailPass = #the input
return EmailPass
Next, you have two options to get the values ready for sendmail. You can either call each method from within the Send_Email method and store the returned values for each one. That would look like this:
def Send_Email(self):
emailaddr = self.Get_For_Email()
recipaddr = self.Get_Email_Address()
...
Or you can store the values, instead of returning them, as instance variables. So, you would have something like this:
def Get_Email_Password(self):
#code to open up window and get email password
EmailPass = #the input
self.emailaddr = EmailPass
And then, in your Send_Email method, you would reference the instance variables you have saved:
def Send_Email(self):
...
server.sendmail(self.emailaddr, self.recipaddr, self.message)
How you choose to do it is up to you, but I prefer the first way. I also suggest you read up on PEP8