Why is flask throwing a class attribute error - python

My flask app is telling me that the method of a class I created is not defined. It says:
AttributeError: 'GetIndexItemInfo' object has no attribute 'genDFs'
Here is the code:
__init__.py
#app.route('/assistant', methods=['GET', 'POST'])
#login_required
def use_assistant():
from flask import request
if request.method == 'GET':
...FORM GOES HERE
else:
report = request.form.get('report')
from modules.majestic import config
rprt = config[report]
inst=rprt(request.form)
url = inst.genUrl()
dic = inst.getData(url)
df = inst.genDFs(dic)
...
majestic.py
import urllib.request, urllib.parse, urllib.error
import ast
import pandas as pd
import ssl
ctx = ssl.create_default_context()
ctx.check_hostname = False
ctx.verify_mode = ssl.CERT_NONE
class Majestic:
key='APIKEYHERE'
base='https://api.majestic.com/api/json?app_api_key={}'.format(key)
#staticmethod
def getData(url):
req = urllib.request.Request(url=url)
f = urllib.request.urlopen(req, context=ctx)
x = f.read().decode('utf-8')
dic = ast.literal_eval(x)
return dic
class Report(Majestic):
def __init__(self,data):
items=[]
for key,val in data.items():
if 'address' in key:
items.append(val)
self.items = items
self.len = len(items)
self.cmd = data['cmd']
self.base_url = super().base
def genUrl(self):
substr=self.base_url+'&cmd='+self.cmd+'&items='+str(len(self.items))+'&'
for i,item in enumerate(self.items):
substr=substr+'item'+str(i)+'='+item+'&'
return substr[:-1]
class GetIndexItemInfo(Report):
#staticmethod
def genDfs(data):
for i in range(len(data['DataTables']['Results']['Data'])):
new_data=data['DataTables']['Results']['Data'][i]
cols = ['Url','AC Rank','Citation Flow','Trust Flow','Ext. Back Links','Ref Domains','Ref Follow Domains']
maj_cols = ['Item','ACRank','CitationFlow','TrustFlow','ExtBackLinks','RefDomains','RefDomainTypeFollow']
dic = dict(zip(cols,[new_data[i] for i in maj_cols]))
cols = ['Url','AC Rank','Citation Flow','Trust Flow','Ext. Back Links','Ref Domains','Ref Follow Domains']
if i == 0:
main_df = pd.DataFrame(dic,index=[0])[cols]
else:
df = pd.DataFrame(dic,index=[0])[cols]
main_df = main_df.append(df, ignore_index=True)
return main_df
I have tested majestic.py seperately and the class instance of GetIndexItemInfo I create works as expected and does not return the error.
Any ideas why i might be getting this error when i run it in flask?
Many thanks in advance

It is correct.
AttributeError: 'GetIndexItemInfo' object has no attribute 'genDFs'
Your method is called genDfs
Notice the capitalisation.

Related

assertion error omitting two identical items

As far as I can tell the assert calls for id and goal_id AND my code provides them both...
enter image description here
goal_routes.py
from datetime import datetime from typing import OrderedDict from
urllib.request import OpenerDirector from flask import Blueprint,
jsonify, request, make_response, abort from app import db from
app.models.goal import Goal from app.models.task import Task from
app.task_routes import validate_task
Create a Goal: goal_bp = Blueprint("goal_bp", name, url_prefix="/goals")
#goal_bp.route("", methods = ["POST"]) def create_goals():
request_body = request.get_json()
if "title" in request_body:
new_goal = Goal(
title = request_body["title"]
)
else:
return jsonify({"details":"Invalid data"}), 400
db.session.add(new_goal)
db.session.commit()
goal_response = {"goal": new_goal.to_dictionary()}
return (jsonify(goal_response), 201)
Get Goals #goal_bp.route("", methods = ["GET"]) def get_goals():
sort = request.args.get("sort")
#Sort by assending (is default?)
if sort == "asc":
goals =Goal.query.order_by(Goal.title)
#Sort by decending
elif sort == "desc":
goals =Goal.query.order_by(Goal.title.desc())
#No Sort
else:
goals = Goal.query.all()
goals_response = []
for goal in goals:
goals_response.append(goal.to_dictionary())
# If No Saved Goals wil stil return 200
return (jsonify(goals_response), 200)
Get One Goal: One Saved Goal #goal_bp.route("/<goal_id>", methods=["GET"]) def get_one_goal(goal_id):
goal = validate_goal(goal_id)
goal_response = {"goal": goal.to_dictionary()}
return (jsonify(goal_response), 200)
Update Goal #goal_bp.route("/<goal_id>", methods=["PUT"]) def update_goal(goal_id):
goal = validate_goal(goal_id)
request_body = request.get_json()
goal.title = request_body["title"]
db.session.commit()
goal_response = {"goal": goal.to_dictionary()}
return (jsonify(goal_response), 200)
Goal Complete #goal_bp.route("/<goal_id>/mark_complete", methods=["PATCH"]) def goal_complete(goal_id):
goal = validate_goal(goal_id)
goal.completed_at = datetime.utcnow()
db.session.commit()
goal_response = {"goal": goal.to_dictionary()}
return (jsonify(goal_response), 200)
Goal Incomplete #goal_bp.route("/<goal_id>/mark_incomplete", methods=["PATCH"]) def goal_incomplete(goal_id):
goal = validate_goal(goal_id)
goal.completed_at = None
db.session.commit()
goal_response = {"goal": goal.to_dictionary()}
return (jsonify(goal_response), 200)
Delete Goal: Deleting a Goal #goal_bp.route("/<goal_id>", methods=["DELETE"]) def delete_goal(goal_id):
goal = validate_goal(goal_id)
db.session.delete(goal)
db.session.commit()
response = {"details": f"Goal {goal.goal_id} \"{goal.title}\" successfully deleted"}
return (jsonify(response), 200)
Validate there are no matching Goal: Get, Update, and Delete
def validate_goal(goal_id):
try:
goal_id = int(goal_id)
except:
abort(make_response({"message": f"Goal {goal_id} is invalid"}, 400))
goal = Goal.query.get(goal_id)
if not goal:
abort(make_response({"message": f"Goal {goal_id} not found"}, 404))
return goal
#goal_bp.route("/<goal_id>/tasks", methods=["POST"]) def
post_task_ids_to_goal(goal_id):
goal = validate_goal(goal_id)
request_body = request.get_json()
for task_id in request_body["task_ids"]:
task = Task.query.get(task_id)
task.goal_id = goal_id
task.goal = goal
db.session.commit()
return jsonify({"id":goal.goal_id, "task_ids": request_body["task_ids"]}), 200
#goal_bp.route("/<goal_id>/tasks", methods=["GET"]) def
get_tasks_for_goal(goal_id):
goal = validate_goal(goal_id)
task_list = [task.to_dictionary() for task in goal.tasks]
goal_dict = goal.to_dictionary()
goal_dict["tasks"] = task_list
return jsonify(goal_dict)
goal.py
from app import db
class Goal(db.Model):
goal_id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String, nullable=False)
tasks = db.relationship("Task", back_populates="goals", lazy = True)
def to_dictionary(self):
goal_dict = {
"id": self.goal_id,
"title": self.title
}
if self.tasks:
goal_dict["tasks"] = [task.task_id for task in self.tasks]
return goal_dict

How to mock an object for test cases in python

I'm having difficulty in writing test cases for the ZabbixAPILayer class mentioned below. I'm not sure how I can mock the 'zabbix_conn_obj' there. Any help will be appreciated. Thanks!
file: externalapi/apilayer.py
from zabbix.api import ZabbixAPI
import json
import time
class ZabbixAPILayer(object):
def uptime(self,arg,zabbix_conn_obj):
try:
getUpdateItem = zabbix_conn_obj.do_request("item.get", {"host":arg})
lastclock=getUpdateItem['result'][37].get('lastclock')
lastclock=int(lastclock)
curclock=int(time.time())
check_val=curclock-lastclock
limit=60*1000
if check_val<limit:
lastval=getUpdateItem['result'][37].get('lastvalue')
return time.strftime("%H:%M:%S", time.gmtime(float(getUpdateItem['result'][37].get('lastvalue'))))
else:
return "-"
except:
return "NOT AVAILABLE"
.....
class APILayer(ZabbixAPILayer):
def __init__(self):
self.zabbix_conn_obj=ZabbixAPI(url=settings.ZABBIX_URL, user=settings.ZABBIX_USER, password=settings.ZABBIX_PWD)
def uptime(self,arg):
return super(APILayer,self).uptime(arg,self.zabbix_conn_obj)
.....
file: base/admin.py
......
from ..externalapis.apilayer import APILayer
......
gen_obj= APILayer()
gen_obj.uptime()
......
Thanks for your comments. Got this working! Here the way i'm doing this
import mock
...
def test_uptime(self):
zabbix_conn_obj = mock.Mock()
json_blob = {} # This is the json blob i'm using as return value from do request
zabbix_conn_obj.do_request = mock.Mock(return_value=json_blob)
obj = ZabbixAPILayer()
value = obj.uptime("TestHOST",zabbix_conn_obj)
desired_result = '' #whatever my desired value is
self.assertEqual(value, desired_result)

TypeError: Model constructor takes no positional arguments

While creating an answer for a question in the API (see code below), I got the following error:
TypeError: Model constructor takes no positional arguments
Can someone tell me how to solve this? I am using ndb model
import webapp2
import json
from google.appengine.ext import ndb
class AnswerExchange(ndb.Model):
answer=ndb.StringProperty(indexed=False,default="No Message")
class AnswerHandler(webapp2.RequestHandler):
def create_answer(self,question_id):
try:
query = StackExchange.query(StackExchange.questionId == question_id)
questions = query.fetch(1)
ans_json = json.loads(self.request.body)
answerObj = AnswerExchange(answer=ans_json["answer"])
answerObj.put()
questions.answerName=answerObj
questions.put()
except:
raise webapp2.exc.HTTPBadRequest()
class StackExchange(ndb.Model):
questionId=ndb.StringProperty(indexed=True)
questionName=ndb.StringProperty(indexed=False)
#answerID=ndb.StringProperty(indexed=True)
answerName=ndb.StructuredProperty(AnswerExchange,repeated=True)
class StackHandler(webapp2.RequestHandler):
def get_questions(self):
#p = self.request.get('p') #get QueryString Parameter
#self.response.write(p)
query = StackExchange.query()
questions = query.fetch(6)
self.response.content_type = 'application/json'
self.response.write(json.dumps([d.to_dict() for d in questions]))
def get_question(self, question_id):
query = StackExchange.query(StackExchange.questionId == question_id)
questions = query.fetch(1)
self.response.content_type = 'application/json'
self.response.write(json.dumps([d.to_dict() for d in questions]))
def create_question(self):
try:
question_json = json.loads(self.request.body)
# if id in dept_json:
questionNo = question_json["questionId"]
questionToUpdate = StackExchange.query(StackExchange.questionId == questionNo);
#print deptToUpdate.count()
if questionToUpdate.count() == 0:
question = StackExchange(questionId=question_json["questionId"], questionName=question_json["questionName"])
else:
question = questionToUpdate.get()
question.questionName = question_json["questionName"]
#key = dept.put()
question.put()
except:
raise webapp2.exc.HTTPBadRequest()
self.response.headers['Location'] = webapp2.uri_for('get_question', question_id=questionNo)
self.response.status_int = 201
self.response.content_type = 'application/json'
#self.response.write(json.dumps())
def create_answer(self,question_id):
#try:
question_json = json.loads(self.request.body)
questionNo = question_json["questionId"]
query = StackExchange.query(StackExchange.questionId == questionNo)
questions = query.fetch(1)
ans_json = json.loads(self.request.body)
answerObj = AnswerExchange(ans_json["answer"])
#answerObj.put()
#self.response.write(ans_json["answerName"])
questions.answerName = answerObj
questions.put();
#except:
# raise webapp2.exc.HTTPBadRequest()
def get_answer(self, question_id):
query = StackExchange.query(StackExchange.questionId == question_id)
questions = query.fetch(1)
self.response.content_type = 'application/json'
self.response.write(json.dumps([d.to_dict() for d in questions]))
app = webapp2.WSGIApplication([
webapp2.Route(r'/api/v1/questions/<question_id>', methods=['GET'], handler='api.StackHandler:get_question', name='get_question'),
webapp2.Route(r'/api/v1/questions', methods=['GET'], handler='api.StackHandler:get_questions', name='get_questions'),
webapp2.Route(r'/api/v1/questions', methods=['POST'], handler='api.StackHandler:create_question', name='create_question'),
webapp2.Route(r'/api/v1/questions/<question_id>/answers', methods=['POST'], handler='api.StackHandler:create_answer', name='create_answer'),
webapp2.Route(r'/api/v1/questions/<question_id>/answers', methods=['GET'], handler='api.StackHandler:get_answer', name='get_answer')
], debug=True)
Change,
answerObj = AnswerExchange(ans_json["answer"])
in create_answer method StackHandler class
to
answerObj = AnswerExchange(answer=ans_json["answer"])

Function not getting executed in python

I have 2 functions(recharge_list and sms_list) in my below Server() class
import os
import json
import requests
import cherrypy
import ConfigParser
from bs4 import BeautifulSoup
class Server():
#cherrypy.expose
def index(self):
return "Seems Like You're Lost :D"
#cherrypy.expose
def recharge_list(self,carrier, state):
details_array=[]
small_details_array=[]
price_cell_array=[]
lst = []
url = "link{}/{}".format(carrier,state)
try:
if self.t_arr.get(url) is not None:
return json.dumps({'data': self.t_arr[url]})
except AttributeError:
self.t_arr = {}
r = requests.get(url)
data = r.text
soup = BeautifulSoup(data,"html.parser")
table = soup.find('table',{'class':'table'})
s=""
detailtext = table.findAll('div',{'class':'detailtext'})
for det in detailtext:
details_array.append(det.text)
smalldetails = table.findAll('div',{'style':'padding-top:5px'})
for smallDet in smalldetails:
small_details_array.append(smallDet.text);
price_cells = table.findAll('td', {'class': 'pricecell'})
for price_cell in price_cells:
price_cell_array.append(price_cell.text)
for i in range(len(details_array)):
d_arr = {}
d_arr['detail']=details_array[i]
temp = small_details_array[i].split('\n')
d_arr['talktime'] = temp[1]
d_arr['keyword']=temp[3]
tempnew = price_cell_array[i].split('\n')
d_arr['price'] = tempnew[1]
d_arr['validity'] = tempnew[3]
# global list
lst.append(d_arr)
self.t_arr[url] = lst
return json.dumps({'data': self.t_arr[url]})
#cherrypy.expose
def sms_list(self,carrier, state):
details_array=[]
price_cell_array=[]
lst = []
url = "link/{}/{}".format(carrier,state)
try:
if self.t_arr.get(url) is not None:
return json.dumps({'data': self.t_arr[url]})
except AttributeError:
self.t_arr = {}
r = requests.get(url)
data = r.text
soup = BeautifulSoup(data,"html.parser")
table = soup.find('div',{'id':'SMS'})
table2 = table.find('table',{'class':'table'})
print(table2)
s=""
detailtext = table2.findAll('div',{'class':'detailtext'})
for det in detailtext:
details_array.append(det.text)
smalldetails = table2.findAll('div',{'style':'padding-top:5px'})
price_cells = table.findAll('td', {'class': 'pricecell'})
for price_cell in price_cells:
price_cell_array.append(price_cell.text)
for i in range(len(details_array)):
d_arr = {}
d_arr['detail']=details_array[i]
tempnew = price_cell_array[i].split('\n')
d_arr['price'] = tempnew[1]
d_arr['validity'] = tempnew[3]
# global list
lst.append(d_arr)
self.t_arr[url] = lst
return json.dumps({'data': self.t_arr[url]})
if __name__ == '__main__':
''' Setting up the Server with Specified Configuration'''
cherrypy.config.update({'server.socket_host': '0.0.0.0',})
cherrypy.config.update({'server.socket_port': int(os.environ.get('PORT', '5000')),})
cherrypy.quickstart(Server())
The problem is, when I run my server with recharge_list it works, but then I have to terminate my server from terminal and re-start the server to execute the sms_list function.
By my understanding the object once created by Server class is able to execute only the first called function.
What should I edit in my code such that I can execute the functions without terminating the server.
By my understanding the object once created by Server class is able to execute only the first called function.
This is not so. Each time an HTTP request is provided, the web server calls the function associated to the URL of that request.
What should I edit in my code such that I can execute the functions without terminating the server.
In sms_list (and not in recharge_list), replace every instance of t_arr with t_sms_arr.

memcache doesn't work as expected

We have a small application for Google App Engine in Python, and we are using memcache. But memcache keys remain even after memcache.delete, and also memcache returns a number (0) when I expect it to return a string ("undefined"). Here is my code:
check_feature.py:
import sys
sys.path.insert(0, 'libs')
import webapp2
import json
from google.appengine.api import memcache
from models.shard_counter import GeneralCounterShard
from models.check_feature_limit import CheckFeatureLimit
class CheckFeatureHandler(webapp2.RequestHandler):
def get_number_of_users_enabled(self, feature_name):
"""
Get the number of users enabled for the given feature name.
"""
number_of_users_enabled_undefined = "undefined"
number_of_users_enabled = memcache.get(key=feature_name)
if (number_of_users_enabled is None):
check_feature_limit = None
check_feature_limits = CheckFeatureLimit.gql("WHERE feature_name=:1 ORDER BY last_modified DESC LIMIT 1", feature_name)
if (check_feature_limits.count() > 0):
check_feature_limit = check_feature_limits.get()
if (check_feature_limit):
number_of_users_enabled = check_feature_limit.number_of_users_enabled
if (number_of_users_enabled is None):
number_of_users_enabled = number_of_users_enabled_undefined
memcache.add(key=feature_name, value=number_of_users_enabled, time=3600)
if (number_of_users_enabled == number_of_users_enabled_undefined):
number_of_users_enabled = None
return number_of_users_enabled
admin.py:
import sys
sys.path.insert(0, 'libs')
import webapp2
import json
import requests
from google.appengine.ext.webapp import template
from google.appengine.api import memcache
from models.shard_counter import GeneralCounterShard
from models.check_feature_limit import CheckFeatureLimit
template.register_template_library("tags.tags")
class AdminHandler(webapp2.RequestHandler):
def get(self):
self.post()
def post(self):
params = {}
number_of_users_enabled_dict = {}
number_of_users_dict = {}
r = requests.get(url="http://jsons.[part_of_link_suppressed].com.s3.amazonaws.com/flags.json")
flags = json.loads(r.text)
if ((flags) and ("depending_on_counter" in flags) and (len(flags["depending_on_counter"]) > 0)):
for feature_name in flags["depending_on_counter"]:
check_feature_limit = None
check_feature_limits = CheckFeatureLimit.gql("WHERE feature_name=:1 ORDER BY last_modified DESC LIMIT 1", feature_name)
if (check_feature_limits.count() > 0):
check_feature_limit = check_feature_limits.get()
number_of_users_enabled = self.request.get(feature_name + "_number_of_users_enabled")
if (number_of_users_enabled):
number_of_users_enabled = int(number_of_users_enabled)
if (not(check_feature_limit)):
check_feature_limit = CheckFeatureLimit(feature_name=feature_name)
check_feature_limit.number_of_users_enabled = number_of_users_enabled
check_feature_limit.put()
memcache.delete(key=feature_name) # I don't think it works.
number_of_users_enabled = None
if (check_feature_limit):
number_of_users_enabled = check_feature_limit.number_of_users_enabled
if (not(number_of_users_enabled is None)):
number_of_users_enabled_dict[feature_name] = number_of_users_enabled
number_of_users = GeneralCounterShard.get_count(feature_name)
number_of_users_dict[feature_name] = number_of_users
params["depending_on_counter"] = flags["depending_on_counter"]
params["number_of_users_enabled_dict"] = number_of_users_enabled_dict
params["number_of_users_dict"] = number_of_users_dict
html = template.render("admin/admin.html", params)
self.response.out.write(html)
app = webapp2.WSGIApplication([
("/admin", AdminHandler)
], debug=True)
The values of test_counter_feature_1 (Number of users to enable) is 2, test_counter_feature_2 is 4 and test_counter_feature_3 is undefined (there is no object), but in the memcache the values are 2, 3 and 0 respectively, even after I save the form (and therefore the memcache should be deleted). What is the problem? I expect the value of test_counter_feature_3 to be "undefined", not 0. And the two other values should be deleted after saving the form.
OK, I found the problem. GeneralCounterShard also saved the same key to memcache, so I renamed the key and everything works now. The new key is feature_name + "_number_of_users_enabled", instead of feature_name like it was before. So I replaced all the calls to memcache with the new key, and now it works. Thank you!

Categories