How to read CSV file and create User - python-3.x

I want to read and create user from csv file. but my code gave me MultiValueDictKeyError at /api/user/
read and crate user from CSV file
my Code is:
def read_and_create_csv_file():
header = {'Authorization': f'Token {get_token()}'}
url = f"{URL}/api/user/"
with open(r"E:\WC_API\employees.csv") as csv_file:
csv_reader = csv.DictReader(csv_file)
for row in csv_reader:
data = {
"USERNAME": row['username'],
"FIRST_NAME": row['FIRST_NAME'],
"LAST_NAME": row['LAST_NAME'],
"EMAIL": row["EMAIL"],
"PASSWORD": "Abc#123"}
response = requests.post(url, json=data, headers=header)
if response.status_code == 201:
print("User created successfully")
else:
print("Failed to create user")
read_and_create_csv_file()
this is the resule:
Failed to create user or MultiValueDictKeyError

Without seeing the csv file, my guess is that there is not a column named 'username', and there is a column named 'USERNAME'. Change line #8 accordingly.

Related

How to call a loop under main fuction python

I am working on a script which i have to modify in order loop through the multiple resources within a functions.
Below are the items which we need to loop through to get the data from and, this is coming from Config_local
BASE_URL = "https://synergy.hpe.example.com/rest/"
RES_EXT = [ 'resource-alerts?count=500&start=1',
'resource-alerts?count=500&start=501'
'resource-alerts?count=500&start=1001,
'resource-alerts?count=500&start=1501'
]
While i am looping through above list under def main(): section and taking get_resource_alerts_response() under loop the data coming out of the loop getting over-written and thus returning only last loop data only.
Main Script:
import os
import shutil
import smtplib
from email.message import EmailMessage
import pandas as pd
pd.set_option('expand_frame_repr', True)
import requests
from Config_local import (
BASE_URL,
DST_DIR,
OUTFILE,
PASSWORD,
SRC_DIR,
TIMESTAMP_DST,
USERNAME,
SUBJECT,
FROM,
TO,
EMAIL_TEMPLATE,
SMTP_SERVER,
RES_EXT,
)
class FileMoveFailure(Exception):
pass
class SynergyRequestFailure(Exception):
pass
class SessionIdRetrievalFailure(SynergyRequestFailure):
pass
class ResourceAlertsRetrievalFailure(SynergyRequestFailure):
pass
def move_csv_files():
for csv_file in os.listdir(SRC_DIR):
if csv_file.endswith(".csv") and os.path.isfile(os.path.join(SRC_DIR, csv_file)):
try:
shutil.move(
os.path.join(f"{SRC_DIR}/{csv_file}"),
f"{DST_DIR}/{csv_file}-{TIMESTAMP_DST}.log"
)
except OSError as os_error:
raise FileMoveFailure(
f'Moving file {csv_file} has failed: {os_error}'
)
def get_session_id(session):
try:
response = session.post(
url=f"{BASE_URL}/login-sessions",
headers={
"accept": "application/json",
"content-type": "application/json",
"x-api-version": "120",
},
json={
"userName": USERNAME,
"password": PASSWORD
},
verify=False
)
except requests.exceptions.RequestException as req_exception:
# you should also get this logged somewhere, or at least
# printed depending on your use case
raise SessionIdRetrievalFailure(
f"Could not get session id: {req_exception}"
)
json_response = response.json()
if not json_response.get("sessionID"):
# always assume the worse and do sanity checks & validations
# on fetched data
raise KeyError("Could not fetch session id")
return json_response["sessionID"]
#def get_all_text(session, session_id):
# all_text = ''
# for res in RES_EXT:
# url= f"{BASE_URL}{res}"
# newresult = get_resource_alerts_response(session, session_id, url)
# all_text += newresult
# print(f"{all_text}")
# return str(all_text)
#
def get_resource_alerts_response(session, session_id, res):
try:
return session.get(
url=f"{BASE_URL}{res}",
headers={
"accept": "application/json",
"content-type": "text/csv",
"x-api-version": "2",
"auth": session_id,
},
verify=False,
stream=True
)
except requests.exceptions.RequestException as req_exception:
# you should also get this logged somewhere, or at least
# printed depending on your use case
raise ResourceAlertsRetrievalFailure(
f"Could not fetch resource alerts: {req_exception}"
)
def resource_alerts_to_df(resource_alerts_response):
with open(OUTFILE, 'wb') as f:
for chunk in resource_alerts_response.iter_content(chunk_size=1024*36):
f.write(chunk)
return pd.read_csv(OUTFILE)
def send_email(df):
server = smtplib.SMTP(SMTP_SERVER)
msg = EmailMessage()
msg['Subject'], msg['From'], msg['To'] = SUBJECT, FROM, TO
msg.set_content("Text version of your html template")
msg.add_alternative(
EMAIL_TEMPLATE.format(df.to_html(index=False)),
subtype='html'
)
server.send_message(msg)
def main():
move_csv_files()
session = requests.Session()
session_id = get_session_id(session)
for res in RES_EXT:
resource_alerts_response = get_resource_alerts_response(session,
session_id, res)
print(resource_alerts_response)
df = resource_alerts_to_df(resource_alerts_response)
print(df)
send_email(df)
if __name__ == '__main__':
main()
any help or hint will be much appreciated.
This is a copy of the code which I recall we had over SO but not what you want now, However, as the whole code body is okay and the idea of for loop is also looks good, you Just need to tweek it to meet the requirement.
1- You need to create and empty DataFrame assignment Synergy_Data = pd.DataFrame()
2- then you can append the data you received from for loop ie resource_alerts_response which becomes df = resource_alerts_to_df(resource_alerts_response)
3- lastly, you can append this df to the empty Synergy_Data and then call that under your if __name__ == '__main__' to send an e-mail. Also don't forget to declare Synergy_Data as a global variable.
Synergy_Data = pd.DataFrame()
def main():
global Synergy_Data
move_csv_files()
session = requests.Session()
session_id = get_session_id(session)
for res in RES_EXT:
resource_alerts_response = get_resource_alerts_response(session,
session_id, res)
df = resource_alerts_to_df(resource_alerts_response)
Synergy_Data = Synergy_Data.append(df)
if __name__ == '__main__':
main()
send_email(Synergy_Data)
Hope this will be helpful.
Only the last response is being used because this value is overwritten in resource_alerts_response on each iteration of the loop. You may consider acting on the data on each iteration or storing it for use later i.e. after the loop. I've included these options with modifications to the main() function below.
Option 1
Send an email for each resource alert response
def main():
move_csv_files()
session = requests.Session()
session_id = get_session_id(session)
for res in RES_EXT:
resource_alerts_response = get_resource_alerts_response(session,
session_id, res)
print(resource_alerts_response)
# Indent lines below so that the operations below are executed in each loop iteration
df = resource_alerts_to_df(resource_alerts_response)
print(df)
send_email(df)
Option 2
Merge all resource alert responses and send one email
def main():
move_csv_files()
session = requests.Session()
session_id = get_session_id(session)
df_resource_alerts_responses = None
for res in RES_EXT:
resource_alerts_response = get_resource_alerts_response(session,
session_id, res)
print(resource_alerts_response)
df = resource_alerts_to_df(resource_alerts_response)
if df_resource_alerts_responses is None:
df_resource_alerts_responses = df
else:
df_resource_alerts_responses = df_resource_alerts_responses.append(df, ignore_index=True)
print(df_resource_alerts_responses)
if df_resource_alerts_responses is not None:
send_email(df_resource_alerts_responses)

How can i fix AWS python event key problem

I have a problem with my AWS python code.
I am trying to send a post request to my code on AWS but I have key problems
My code on AWS
import json
import random
def lambda_handler(event, context):
name = surname = birthDate = favoriteFilm = password = ""
indexList = keysArray = []
setParams(event, name, surname, birthDate, favoriteFilm)
fillArray(keysArray, name, surname, birthDate, favoriteFilm)
arrayLength = len(keysArray)
while len(password)<6:
index = getRandomRangeIndex(arrayLength)
if index in indexList:
continue
password = password + keysArray[index]
indexList.append(index)
return {
'statusCode': 200,
'body': json.dumps(password)
}
def setParams(event, name, surname, birthDate, favoriteFilm):
name = event['first_name']
surname = event['last_name']
birthDate = event['d_o_b']
favoriteFilm = event['favorite_film']
def fillArray(keysArray, name, surname, birthDate, favoriteFilm):
for names in name.split():
keysArray.append(names)
keysArray.append(surname)
for dates in birthDate.split('-'):
keysArray.append(dates)
for films in favoriteFilm.split():
keysArray.append(films)
def getRandomRangeIndex(arrayLength):
return random.randint(0, arrayLength-1)
My Postman request header
{
"first_name": "John",
"last_name": "Smith",
"d_o_b": "1985-12-04",
"favorite_film": "Back to the Future"
}
My problem log
[ERROR] KeyError: 'first_name'
Traceback (most recent call last):
File "/var/task/password.py", line 7, in lambda_handler
setParams(event, name, surname, birthDate, favoriteFilm)
File "/var/task/password.py", line 24, in setParams
name = event['first_name']
I am not able to find any solution. How can I fix this problem? Thank you.
When you submit your json though api gateway, the event object that your function receives is different than what you submit. The format is shown in aws docs.
In your case the event will be something like this:
{
"resource": "/test",
"path": "/test",
"httpMethod": "POST",
#
# a banch of data removed for length
#
"body": "{\n \"first_name\": \"John\",\n \"last_name\": \"Smith\",\n \"d_o_b\": \"1985-12-04\",\n \"favorite_film\": \"Back to the Future\"\n}",
"isBase64Encoded": false
}
Thus, to get your actual data and you have to parse body you can use python's ast. You body is not json string, thus need to use ast:
import json
import random
import ast
def lambda_handler(event, context):
# overwrite event to keep using event in later parts of your code
event = ast.literal_eval(event['body'])
#....

how to add content_type explicitly for json and file in post request

I would like to add content type explicity for multipart form data before sending post request
Below is my sample code i managed to add conten type for file data but couldn't figure out how to add content type correctly for json data, i would like to add "application/json; charset=utf-8" for json data
import requests
import json
import traceback
def uploadLogs(fileName):
f = open(fileName, 'rb')
payload = { "var1":"this", "var2" : "that"
}
files = {'file': ('current', f, "text/plain; charset=us-ascii")}
data = {'info': json.dumps(payload)}
headers = {'type': 'myReport', "Keep-Alive": "timeout=100"}
try:
url = "http://localhost:8009/upload"
response = requests.post(url, data=data, files=files, headers=headers)
print(response.request.body)
print(response.request.headers)
print(response.status_code)
if (response != None and (response.status_code == 200 or
response.status_code == 201)):
return True
except:
traceback.print_exc()
return False
filename = "C:\\sample.txt"
print(uploadLogs(filename))
If someone knows how to do please suggest

Excel file represented by string in API response: how to download as file with python?

I am calling an API that returns the following object as response:
{
"BrokenRules": [],
"ReturnCode": 0,
"ReturnData": "\"UEsDBBQAAAAIAGJAylC560mkEgIAAM4DAAAPABwAeGwvd29ya2Jvb2sueG1sIKIYACigFAAAAAAAAAAAAAAAAAAAAAAAAAAAAO29B2AcSZYlJi9tynt/SvVK1+B0oQiAYBMk2JBAEOzBiM3mku.....ASAMAABMAAAAAAAAAAAAAAAAA0P8QAFtDb250ZW50X1R5cGVzXS54bWxQSwUGAAAAAAgACAD9AQAAqAERAAAA\""
}
I am supposed to be able to download an excel file (.xlsx) from the ReturnData object. How do I do that with python 3?
Here is what I have tried, but the file is corrupt and is empty. Any ideas ?
filename = "api_output.xlsx"
response = requests.post(URL, headers=headers, json=data)
res = json.loads(response.json()["ReturnData"])
with open(filename, 'w') as file:
print("writing file")
file.write(res)
So I found the answer:
response = requests.post(URL, headers=headers, json=data)
res = json.loads(response.json()["ReturnData"])
data = base64.b64decode(res)
with open(filename, 'wb') as file:
print("writing file")
file.write(data)

flask wtf upload image file to aws s3

I want to upload image to aws s3 using presigned url and the image is selected by users only.
So I make file input field using flask-wtf filefield.
After user submit the form(PostForm), I want to get the image's data and send to presigned-url.
But I don't know how to get image file's information from the Form.
Please help me!!!
I used example code from below tutorials. But the difference is that I use flask-wtf not local image file.
https://boto3.amazonaws.com/v1/documentation/api/latest/guide/s3-presigned-urls.html
In following code, the problem line is here.
files = {'file': (save_objectname, image.read())}
http_response = requests.post(response['url'], data=response['fields'], files=files)
when I print(image.read())
it shows
b'' <--- nothing...
How can I fix it???
def create_post():
form = PostForm()
save_post_foldername = 'post_images'
if form.validate_on_submit():
if form.post_image.data:
image_file = form.post_image.data
save_post_objectname = generate_filename(image_file)
# Generate a presigned S3 POST URL
post_to_aws_s3(image_file, save_post_foldername, save_post_objectname)
def post_to_aws_s3(image, save_foldername, save_objectname):
fields = {
"acl": "public-read",
"Content-Type": image.content_type
}
conditions = [
{"acl": "public-read"},
{"Content-Type": image.content_type}
]
try:
response = s3_client.generate_presigned_post(
Bucket=S3_BUCKET,
Key=save_foldername+'/'+save_objectname,
Fields=fields,
Conditions=conditions,
ExpiresIn=600
)
print(response)
print(response['url'])
print(image.content_type)
#with open(image, 'rb') as f:
#files = {'file': ('abc.png', f)}
#files = {'file': (image, f)}
files = {'file': (save_objectname, image.read())}
http_response = requests.post(response['url'], data=response['fields'], files=files)
print(image.read())
except ClientError as e:
print("error")
Access_ID and secret ID refer to your aws user access id and secret key, because boto3 will use that to access your aws s3 using the user you assign to it.
s3 = boto3.resource("s3", aws_access_key_id=os.getenv("ACCESS_ID"), aws_secret_access_key=os.getenv("ACCESS_SECRET_ID"))
s3.Bucket("[NAME OF BUCKET]").put_object(Key="images/"+request.files["image"].filename, Body=request.files["image"] )
make sure you you include "enctype" under forms, under jinja, e.g.
<form method="POST" action="" enctype="multipart/form-data">

Resources