Reference file with JSON locators class - python

I have JSON data in my main file, lets call this main.py (this uses Selenium driver):
main.py
def get_user_data(browser):
browser.get(JSON_URL)
user_data = json.loads(browser.find_element_by_tag_name('body').text)
browser.back()
return user_data
To get a value from this, I use:
name = user_data['name']
This works just fine. This is a larger program and I would like to have these locators in another file called locators.py using a class so if a locator changes, I just change it in one place:
locators.py
class UserDataLocators:
NAME = user_data['name']
Now, the above fails because 'user_data' is not defined. I like this approach since I have other selenium locators in this file in a separate class. This may be a super simple fix, but how would I get the above to work so in my main.py file, I can import UserDataLocators and do the following:
from locators import UserDataLocators
name = UserDataLocators.NAME
Thanks!
Update 1:
#maxhaz So with what you wrote, it gave me an idea to maybe just use your UserData class to parse everything instead of just storing locators. Basically this is what I'm looking to accomplish. The user_data['name'] will get updated/changed as other modules interact with it. I'd like to store the user_data in locators.py and be able to access it and update it with the other modules, and after everytime it is update, each module can get the updated data.
For example, locators.py has the user_data as None to start since no browser is initiated. Main.py will be the first to interact with it and passes the browser to get the initial user_data['name'] value. Module2.py will also import from locators.py and get that same user_data that main.py just updated. Module2 will update the user_data['name']. Once this happens, is this new user_data['name'] now updated for main.py and locators.py?
#locators.py
user_data = None
class UserData:
def __init__(self, browser):
self.browser = browser
self.name = self.get_user_data()['name']
def get_user_data(self):
self.browser.get(JSON_URL)
user_data = json.loads(browser.find_element_by_tag_name('body').text)
browser.back()
return user_data
#main.py
import locators
locators.user_data = UserData(browser).name
#module2.py
import locators
## do something here that modifies the actual user data that gets pulled ##
locators.user_data = UserData(browser).name

You probably want to move the get_user_data function in the class:
#locators.py
class UserDataLocators:
def __init__(self, browser):
self.browser = browser
self.name = self.get_user_data()['name']
def get_user_data(self):
self.browser.get(JSON_URL)
user_data = json.loads(browser.find_element_by_tag_name('body').text)
browser.back()
return user_data
#main.py
from locators import UserDataLocators
name = UserDataLocators(browser).name

Related

Can i export my view to my viewmodel (Python/Flask)

Im trying to use python and flask so that i can have someone submit a report (via ajax)
I got the items from my form in the python view, but now im trying to export it to my viewmodel so that from within my viewmodel i can call a function, however, im stuck as to how to do this with or continue...
This is my view
#bp.route("/rapid7/submit_report", methods=["GET"])
#login_required_with_ip_whitelist
def submit_report():
log_request()
vm = Rapid7()
return render_template("rapid7/submit_report.html", **vm.to_dict())
#bp.route("/rapid7/submit_report/process", methods=["POST"])
#login_required_with_ip_whitelist
def process():
log_request()
getFormData = (request.form)
reportName = (getFormData['reportName'])
districtName = (getFormData['districtName'])
rapid7Query = (getFormData['rapid7Query'])
rapid7SeverityLevel = (getFormData['rapid7SeverityLevel'])
print(reportName, districtName, rapid7Query, rapid7SeverityLevel)
r7report = Rapid7.check_action(reportName, districtName, rapid7Query, rapid7SeverityLevel)
print(r7report)
#exort to viewmodel
#r7_insightvm_report.configure_report(reportName, districtName, rapid7Query, rapid7SeverityLevel)
return reportName, districtName, rapid7Query, rapid7SeverityLevel
my viewmodel
from shutil import ExecError
from app.viewmodels.rapid7.rapid7 import rapid_7_report
from flask import current_app
from services.rapid7 import insightvmreport
from app.viewmodels.shared.viewmodelbase import ViewModelBase
class Rapid7(ViewModelBase):
def __init__(self):
super().__init__()
self.title = "Rapid7 Test"
self.check_action()
def check_action(self,reportName, districtName, rapid7Query, rapid7SeverityLevel):
try:
self.response = insightvmreport.check_action(
#path = current_app.config["r7_host_path"],
districtName=districtName,
reportName=reportName,
rapid7Query=rapid7Query,
rapid7SeverityLevel=rapid7SeverityLevel,
site_scope={"site":"DISTRICT 1 (EUR)","scan":"DISCOVERY"},
version="2.3.0",
action="download",
)
except Exception as e:
print(f"Failed to connect Error message: {e}")
when i run my flask app, it says
NameError: name 'reportName' is not defined
i want to use the reportName from the view (from what the user submitted to the post request), if i do a print reportName, i can see that the user was able to submit the data
i kind of understand that i have Rapid7() in my view (under the get request), which could be part of the problem, but im wondering if there's a way around this or a way to fix it, in order for my ajax script to work the way i want (Reload without having to refresh), id like for it to load the class, if this wont work is there a better solution?

Unable to use same SQLite connection across multiple objects in Python

I'm working on a Python desktop app using wxPython and SQLite. The SQLite db is basically being used as a save file for my program so I can save and backup and reload the data being entered. I've created separate classes for parts of my UI so make it easier to manage from the "main" window. The problem I'm having is that each control needs to access the database, but the filename, and therefore the connection name, needs to be dynamic. I originally created a DBManager class that hardcoded a class variable with the connection string, which worked but didn't let me change the filename. For example
class DBManager:
conn = sqlite3.Connection('my_file.db')
#This could then be passed to other objects as needed
class Control1:
file = DBManager()
class Control2:
file = DBManager()
etc.
However, I'm running into a lot of problems trying to create this object with a dynamic filename while also using the same connection across all controls. Some examples of this I've tried...
class DBManager:
conn = None
def __init__(self):
pass
def __init__(self, filename):
self.conn = sqlite3.Connection(filename)
class Control1:
file = DBManager()
class Control2:
file = DBManager()
The above doesn't work because Python doesn't allow overloading constructors, so I always have to pass a filename. I tried adding some code to the constructor to act differently based upon whether the filename passed was blank or not.
class DBManager:
conn = None
def __init__(self, filename):
if filename != '':
self.conn = sqlite3.Connection(filename)
class Control1:
file = DBManager('')
class Control2:
file = DBManager('')
This let me compile, but the controls only had an empty connection. The conn object was None. It seems like I can't change a class variable after it's been created? Or am I just doing something wrong?
I've thought about creating one instance of DBManager that I then pass into each control, but that would be a huge mess if I need to load a new DB after starting the program. Also, it's just not as elegant.
So, I'm looking for ideas on achieving the one-connection path with a dynamic filename. For what it's worth, this is entirely for personal use, so it doesn't really have to follow "good" coding convention.
Explanation of your last example
You get None in the last example because you are instantiating DBManager in Control1 and Control2 with empty strings as input, and the DBManager constructor has an if-statement saying that a connection should not be created if filename is just an empty string. This leads to the self.conn instance variable never being set and any referal to conn would resolve to the conn class variable which is indeed set to None.
self.conn would create an instance variable only accessible by the specific object.
DBManager.conn would refer to the class variable and this is what you want to update.
Example solution
If you only want to keep one connection, you would need to do it with e.g. a. class variable, and update the class variable every time you interact with a new db.
import sqlite3
from sqlite3 import Connection
class DBManager:
conn = None
def __init__(self, filename):
if filename != '':
self.filename = filename
def load(self) -> Connection:
DBManager.conn = sqlite3.Connection(self.filename) # updating class variable with new connection
print(DBManager.conn, f" used for {self.filename}")
return DBManager.conn
class Control1:
db_manager = DBManager('control1.db')
conn = db_manager.load()
class Control2:
db_manager = DBManager('control2.db')
conn = db_manager.load()
if __name__ == "__main__":
control1 = Control1()
control2 = Control2()
would output the below. Note that the class variable conn refers to different memory addresses upon instantiating each control, showing that it's updated.
<sqlite3.Connection object at 0x10dc1e1f0> used for control1.db
<sqlite3.Connection object at 0x10dc1e2d0> used for control2.db

How to access the variables of a class - Python

I have the following code to enable the file browser using blender:
import bpy
import os
from bpy.props import StringProperty
from bpy_extras.io_utils import ImportHelper
from bpy.types import Operator
sel = ''
class OpenBrowser(bpy.types.Operator):
bl_idname = "open.file"
bl_label = "Select Excel File"
bli_description = "Simulation output excel file"
filter_glob: StringProperty(default = '*.xls;*.xlsx',options = {'HIDDEN'})
filepath: bpy.props.StringProperty(subtype="FILE_PATH")
#somewhere to remember the address of the file
def execute(self, context):
global sel
sel = self.filepath
#self.selected_file = self.filepath
#display = "filepath= "+self.filepath
#print(display) #Prints to console
#Window>>>Toggle systen console
return {'FINISHED'}
def invoke(self, context, event): # See comments at end [1]
context.window_manager.fileselect_add(self)
global sel
sel = self.filepath
#Open browser, take reference to 'self'
#read the path to selected file,
#put path in declared string type data structure self.filepath
return {'RUNNING_MODAL'}
# Tells Blender to hang on for the slow user input
bpy.utils.register_class(OpenBrowser)
#Tell Blender this exists and should be used
# [1] In this invoke(self, context, event) is being triggered by the below command
#but in your script you create a button or menu item. When it is clicked
# Blender runs invoke() automatically.
#execute(self,context) prints self.filepath as proof it works.. I hope.
bpy.ops.open.file('INVOKE_DEFAULT')
print(sel)
The issue I am facing is that I have declared a global variable sel to which I want to save the filepath selected from the user when running the code. However, when I run the script I see that sel has not changed and it is as it was initialized. Could someone please help me on how to access from the class the self.filepath variable? What am I doing wrong here?
If I understand correctly, you want to store that value for later.
I'm not sure why 'sel' doesn't even update in your case, but I think the more correct way would be to use a property like so:
import bpy
# Assign a custom property to an existing type.
bpy.types.Scene.my_sel_value = bpy.props.StringProperty(name="Sel")
# Set property value.
bpy.context.scene.my_sel_value = "Foo"
# Get property value.
print(bpy.context.scene.my_sel_value)
Properties can be added to all ID types, but for "global" values, bpy.types.scene us ussualy used. Though there can be multiple scenes in one project and they will have separate values. Property values are stored when Blender closes.
If you are making an addon, you can also store your value in Addon Preferences. This value will be the same for all blender projects.

Python: import of Modules with mutual dependency

I have two files rest_api.py and Contact.py. Contact is similar to a domain object (contains Contact class), while rest_api has functions for setting up the application.
In rest_api I have the following lines:
from Contact import Contact
...
client = MongoClient('localhost',27017)
collection = client.crypto_database.test_collection
def dbcollection(){
return collection
}
...
api.add_resource(Contact,'/contact/<string:contact_id>')
In Contact I try to do the following:
from rest_api import dbcollection
class Contact(Resource):
def get(self,contact_id):
result = {}
result['data'] = dbcollection.find_one({'contact_id':contact_id})
result['code'] = 200 if result['data'] else 404
return make_response(dumps(result), result['code'],{"Content-type": "application/json"})
This fails with the following error:
ImportError: cannot import name Contact
What is the correct way of importing contact, so that it can also use variables/functions from rest_api?
p.s If I move the collection code to a different file, and import that file instead things work, but I assume there is some other way..
This is a circular import dependency, which cannot be solved as such. The problem is, that importing a python module really runs the code, which has to follow some order, one of the modules has to go first.
I would say that having the support code in a different file would be the proper way to go.
In this case however, the dbcollection is not actually needed at import time. Thus you can solve this by removing the import from the module level, into the get function. For example
class Contact(Resource):
def get(self,contact_id):
from rest_api import dbcollection
result = {}
result['data'] = dbcollection.find_one({'contact_id':contact_id})
result['code'] = 200 if result['data'] else 404
return make_response(dumps(result), result['code'],{"Content-type": "application/json"})
A similar approach would be the following:
import rest_api
class Contact(Resource):
def get(self,contact_id):
result = {}
result['data'] = rest_api.dbcollection.find_one({'contact_id':contact_id})
result['code'] = 200 if result['data'] else 404
return make_response(dumps(result), result['code'],{"Content-type": "application/json"})
This should work, as python does some effort to resolve circular import dependencies: When it start importing a module, it creates an empty module dict for that one. Then when it finds a nested import, it proceeds with that one. If that in turn imports a module that is already in the import process it just skips it. Thus at the time Contact.py is being loaded, the import rest_api just takes the module dict that is already there. Since it does not contain dbcollection yet, from rest_api import dbcollection fails. A simple import rest_api does work however, since it's member is only addressed after Contact.py finishes importing (unless you call Contact.get at module level from within).
Assuming you haven't a clue which modules are going to import which ever other ones, you can track that yourself and NOT do the import.
In your __init__.py, define these --
__module_imports__ = {}
def requires_module(name):
return name not in __module_imports__
def importing_module(name):
__module_imports__[name] = True
Then, at the top of each class file, where you define your classes add the following to this_module.py:
from my_modules import requires_module, importing_module
importing('ThisModule')
if requires_module('ThatModule')
from my_modules.that_module import ThatModule
class ThisModule:
""" Real Stuff Goes Here """
pass
and this to that_module.py:
from my_modules import requires_module, importing_module
importing('ThatModule')
if requires_module('ThisModule')
from my_modules.this_module import ThisModule
class ThatModule:
""" Real Stuff Goes Here """
pass
now you get your imports regardless of which gets imported first or whatever.

Understanding Class inheritance to DRY up some code

I am using the cloudant python library to connect to my cloudant account.
Here is the code I have so far:
import cloudant
class WorkflowsCloudant(cloudant.Account):
def __init__(self):
super(WorkflowsCloudant, self).__init__(settings.COUCH_DB_ACCOUNT_NAME,
auth=(settings.COUCH_PUBLIC_KEY,
settings.COUCH_PRIVATE_KEY))
#blueprint.route('/<workflow_id>')
def get_single_workflow(account_id, workflow_id):
account = WorkflowsCloudant()
db = account.database(settings.COUCH_DB_NAME)
doc = db.document(workflow_id)
resp = doc.get().json()
if resp['account_id'] != account_id:
return error_helpers.forbidden('Invalid Account')
return jsonify(resp)
This Flask controller will have CRUD operations inside of it, but with the current implementation, I will have to set the account and db variables in each method before performing operations on the document I want to view/manipulate. How can I clean up (or DRY up) my code so that I only have to call to my main WorkflowsCloudant class?
I don't know cloudant, so I may be totally off base, but I believe this answers your question:
Delete the account, db, and doc lines from get_single_workflow.
Add the following lines to __init__:
db = account.database(settings.COUCH_DB_NAME)
self.doc = db.document(workflow_id)
Change the resp line in get_single_workflow to:
resp = WorkflowsCloudant().doc.get().json()

Categories

Resources