Hello I'm working on a script in Python that will connect to a db retrieve some information and send emails. I'have a problem with queries done with Psycopg.
I'would like to retrieve all the users where created_at = nb_days. My query work very good in navicat/pgadmin and I'have 53 records with the query :
select a.account_name, a.email, a.user_id, a.locale, a.firstname from v_accounts a where date(a.created_at) = date(current_date - interval '2 days')
But when I execute my script I have None as result of the query. This is my script class :
import psycopg2
class MyDatabase(object):
db_name='you'
user='will'
pw='never'
host='know'
port='it'
def __init__(self, db=db_name, user=user, password=pw, host=host, port=port):
"""Connection to db - creation of the cursor"""
try:
self.baseDonn = psycopg2.connect(host=host, port=port, database=db, user=user,password=password)
except Exception as err:
print('La connexion avec la base de données à échoué : Erreur détéctée : %s' % err)
else:
self.cursor=self.baseDonn.cursor() # Création du curseur
def get_data_from_accounts(self,nb_days):
''' Method that retrieves data from all accounts that were created nb_days before today '''
sql="select a.account_name,u.email, u.user_id,u.locale, u.firstname from accounts a inner join account_users au on a.account_id=au.account_id inner join users u on au.user_id=u.user_id where date(a.created_at) = date(current_date - interval '%s days');"
print(sql)
data=(nb_days,)
try:
records = self.cursor.execute(sql,data)
print('cursor-execute={}'.format(records))
except Exception as err:
print("La requete n'est pas passée, erreur={}".format(err))
else:
return records
This is the main part
from my_db import MyDatabase
database=MyDatabase()
# On va récupérer les données des nouveaux accounts d'y a un jours
days_ago_2=database.get_data_from_accounts(2)
for personne_1 in days_ago_2:
# data
#account_name=personne_1[0]
email=personne_1[1]
user_id=personne_1[2]
locale=personne_1[3]
firstname='' if personne_1[4] is None else personne_1[4]
language = locale.split('_')[1]
activation_token= database.get_activation_token(user_id)
subject = call_to_template2(firstname, language,activation_token)['subject']
content = call_to_template2(firstname, language,activation_token)['content']
print('EMAIL={} - ID={} - LANGUE={} - FIRSTNAME={}'.format(email, user_id, language, firstname))
#send_data(qui_envoie, email, subject, content, token, language)
And my error is on the line for personne_1 in days_ago_2: because None object is not iterable. And I've saw that the result of my query get_data_from_accounts(2) = None
cursor.execute allways returns None. You need to fetch the record set:
try:
self.cursor.execute(sql,data)
records = self.cursor.fetchall()
Related
I am coding a bullying reports web app in python with flask and a database in sqlite3 with 3 lists usuarios, victimas and testigos. Table usuarios is to list all the users (schools) registered and the table victimas is to show all the reports made by students that involve a SPECIFIC SCHOOL. For example: Show all reports from school "Gonza". So when a school logs in it can see all the reports made but the problem is there i can't link the school name with the victims list.
I think the problem is in the session[user_id] in line 162 and then on line 232 when i try to hechos = db.execute("SELECT * FROM victimas WHERE escuela = :escuela", escuela=session["user_id"]) it causes a bug.
I had two different error messages from the debugger that are stated below:
KeyError: 'user_id' in line 232 hechos = db.execute("SELECT * FROM
victimas WHERE escuela = :escuela", escuela=session["user_id"])
Sometimes this error also appears
NameError: name 'escuela' is not defined in line 241, in
tablatestigos return render_template("tablatestigos.html",
escuela=escuela)
#app.route("/regescuela", methods=["GET", "POST"])
def register():
session.clear()
if request.method == "POST":
if not request.form.get("username"):
return apology("No ha introducido un nombre de usuario!")
elif not request.form.get("password"):
return apology("No ha introducido una contraseña!")
elif request.form.get("password") != request.form.get("confirmation"):
return apology("Las contraseñas no coinciden.")
else:
usumayu = request.form.get("username")
return render_template("regescuela.html")
result = db.execute("INSERT INTO usuarios (username, hash) VALUES(:username, :hash)", username= request.form.get("username").upper(), hash=generate_password_hash(request.form.get("password")))
if not result:
return apology("Este usuario ya existe! Prueba con otro!")
session["user_id"] = result[0]["username"]
flash("Registrado!")
return redirect("/")
#app.route("/login", methods=["GET", "POST"])
def login():
"""Log user in"""
# Forget any user_id
session.clear()
# User reached route via POST (as by submitting a form via POST)
if request.method == "POST":
# Ensure username was submitted
if not request.form.get("username"):
return apology("Debe ingresar un nombre de usuario.", 403)
# Ensure password was submitted
elif not request.form.get("password"):
return apology("Debe ingresar una contraseña.", 403)
# Query database for username
rows = db.execute("SELECT * FROM usuarios WHERE username = :username",
username=request.form.get("username"))
# Ensure username exists and password is correct
if len(rows) != 1 or not check_password_hash(rows[0]["hash"], request.form.get("password")):
return apology("Usuario o contraseña incorrectos", 403)
# Remember which user has logged in
session["user_id"] = rows[0]["username"]
# Redirect user to home page
return redirect("/")
# User reached route via GET (as by clicking a link or via redirect)
else:
return render_template("login.html")
#app.route("/logout")
def logout():
"""Log user out"""
# Forget any user_id
session.clear()
# Redirect user to login form
return redirect("/")
#app.route("/victima", methods=["GET", "POST"])
def victima():
if request.method == "POST":
if not (request.form.get("nombreescuela" and "curso" and "box")):
return apology("Debes completar todo el formulario.")
if not (len(request.form.get("curso")) == 2):
return apology("Ingrese corectamente el curso (no mas de 2 digitos). El primero debe ser un numero y el segundo la letra de su division. Ejemplo: 5A | (si su colegio no tiene division ingrese el numero acompañado de una x. Ejemplo: 5x) | ")
if not ((request.form.get("curso")[0] == "1" or "2" or "3" or "4" or "5" or "6" or "7" or "8" or "9") and (request.form.get("curso")[1].isalpha())):
return apology("Ingrese el curso correctamente. No se aceptan mas de 2 digitos. El primero debe ser un numero y el segundo la letra de su division. Ejemplo: 5A | (si su colegio no tiene division ingrese el numero acompañado de una x. Ejemplo: 5x) | ")
db.execute("INSERT INTO victimas (escuela, curso, hecho) VALUES(:escuela, :curso, :hecho)", escuela=request.form.get(
"nombreescuela"), curso=request.form.get("curso").upper(), hecho=request.form.get("box"))
flash("Formulario enviado con éxito!")
return redirect("/")
else:
return render_template("victima.html")
#app.route("/testigo", methods=["GET", "POST"])
def testigo():
if request.method == "POST":
if not (request.form.get("nombreescuela" and "curso" and "victima" and "box")):
return apology("Debes completar todo el formulario.")
if not (len(request.form.get("curso")) == 2):
return apology("Ingrese corectamente el curso. No mas de 2 digitos. El primero debe ser un numero y el segundo la letra de su division. Ejemplo: 5A | (si su colegio no tiene division ingrese el numero acompañado de una x. Ejemplo: 5x) | ")
if not ((request.form.get("curso")[0] == "1" or "2" or "3" or "4" or "5" or "6" or "7" or "8" or "9") and (request.form.get("curso")[1].isalpha())):
return apology("Ingrese el curso correctamente. No se aceptan mas de 2 digitos. El primero debe ser un numero y el segundo la letra de su division. Ejemplo: 5A | (si su colegio no tiene division ingrese el numero acompañado de una x. Ejemplo: 5x) | ")
db.execute("INSERT INTO testigos (escuela, curso, victima, hecho) VALUES(:escuela, :curso, :victima, :hecho)", escuela=request.form.get(
"nombreescuela"), curso=request.form.get("curso").upper(), victima=request.form.get("victima"), hecho=request.form.get("box"))
flash("Formulario enviado con éxito!")
return redirect("/")
else:
return render_template("testigo.html")
#app.route("/reportesrecibidos", methods=["GET", "POST"])
#login_required
def reportesrecibidos():
return render_template("reportesrecibidos.html")
#app.route("/tablavictimas")
#login_required
def tablavictimas():
hechos = db.execute("SELECT * FROM victimas WHERE escuela = :escuela", escuela=session["user_id"])
return render_template("tablavictimas.html", escuela=escuela)
#app.route("/tablatestigos")
#login_required
def tablatestigos():
hechos = db.execute("SELECT * FROM testigos WHERE escuela = :escuela", escuela=session["user_id"])
return render_template("tablatestigos.html", escuela=escuela)
Error messages:
KeyError: 'user_id' in line 232 hechos = db.execute("SELECT * FROM
victimas WHERE escuela = :escuela", escuela=session["user_id"])
Sometimes this error also appears
NameError: name 'escuela' is not defined in line 241, in
tablatestigos return render_template("tablatestigos.html",
escuela=escuela)
You have this error:
NameError: name 'escuela' is not defined in line 241, in tablatestigos
return render_template("tablatestigos.html", escuela=escuela)
because escuela is not defined. I think the error message is very clear. Anyway, to solve this problem, you need to define escuela like this:
#app.route("/tablatestigos")
#login_required
def tablatestigos():
escuela = session["user_id"]
hechos = db.execute("SELECT * FROM testigos WHERE escuela = :escuela",
escuela=escuela)
return render_template("tablatestigos.html", escuela=escuela)
The second error:
KeyError: 'user_id' in line 232 hechos = db.execute("SELECT * FROM
victimas WHERE escuela = :escuela", escuela=session["user_id"])
is raised because session["user_id"] does not exist. There are many reasons for this: First, may be the user is not logged in. So you may have a problem in the decorator #login_required. Second, may be you did not define a secret_key for your application. In this case, you will lost your session content after every page refresh. To solve this issue, you need to define a secret_key for your application in the main file:
app = Flask('application')
app.secret_key = "some secret key"
Note that there are more secure solution to define a secret_key for your application but this is the easiest solution.
I have deployed a flask application on heroku but I am facing an UTF-8 validation error when trying to register a new user. Everything is working fine on local, so I don't know what is causing this.
Here is my code for the route:
# REGISTER
#bp.route('/register', methods = ['GET', 'POST'])
def register():
if current_user.is_authenticated:
return redirect(url_for('main.index'))
form = RegistrationForm()
if form.validate_on_submit():
user = User(username = form.username.data)
password = form.password.data
password_validation = password_check(password)
if not password_validation['password_ok']:
flash("Votre mot de passe n'est pas sécurisé !")
if password_validation['length_error']:
flash("Votre mot de passe doit comporter au moins 8 caractères !")
if password_validation['digit_error']:
flash("Votre mot de passe doit comporter au moins 1 chiffre !")
if password_validation['uppercase_error']:
flash("Votre mot de passe doit comporter au moins 1 majuscule !")
if password_validation['lowercase_error']:
flash("Votre mot de passe doit comporter au moins 1 minuscule !")
if password_validation['symbol_error']:
flash("Votre mot de passe doit comporter au moins 1 caractère spécial !")
return redirect(url_for('auth.register'))
user.set_password(password)
user.send_confirmation_email = datetime.now()
db.session.add(user)
db.session.commit()
send_confirmation_email(user.username)
flash('Félicitations, vous êtes désormais enregistré ! Veuillez consulter vos mails pour confirmer votre adresse mail !')
return redirect(url_for('auth.login'))
return render_template('auth/register.html', title = 'Inscription', form = form)
Here is my code for the form:
class RegistrationForm(FlaskForm):
username = StringField('Email', validators=[DataRequired(), Email()])
password = PasswordField('Mot de passe', validators=[DataRequired()])
password2 = PasswordField(
'Confirmation du mot de passe', validators=[DataRequired(), EqualTo('password')])
recaptcha = RecaptchaField()
submit = SubmitField('S\'enregistrer')
def validate_username(self, username):
user = User.query.filter_by(username=username.data).first()
is_client = client_mail(username.data)
if is_client == False:
raise ValidationError('Nous sommes désolé mais vous n\'êtes pas l\'un de nos clients.')
if user is not None:
if not user.email_confirmed:
send_confirmation_email(user.username)
raise ValidationError('Une nouveau mail de confirmation vous a été envoyé.')
else:
raise ValidationError('Cette addresse mail est déjà utilisée, veuillez en saisir une nouvelle.')
Anyone has an idea what is the problem here?
I had a look with the heroku logs --tail command but could not find any errors.
EDIT
Here is the code to check the password:
def password_check(password):
length_error = len(password) < 8
digit_error = re.search(r"\d", password) is None
uppercase_error = re.search(r"[A-Z]", password) is None
lowercase_error = re.search(r"[a-z]", password) is None
symbol_error = re.search(r"\W", password) is None
password_ok = not (length_error or digit_error or uppercase_error or lowercase_error or symbol_error)
return {
'password_ok': password_ok,
'length_error': length_error,
'digit_error': digit_error,
'uppercase_error': uppercase_error,
'lowercase_error': lowercase_error,
'symbol_error': symbol_error
}
After some more digging I finally found the problem.
It was in my client_mail() function: I was reading a CSV file without any encoding options.
with open(csv_file) as file:
csv_reader = csv.DictReader(file)
for row in csv_reader:
for (key, val) in row.items():
if client_company in val:
is_client = True
I added yield {unicode(key, 'utf-8'):unicode(value, 'utf-8') for key, value in row.iteritems()} after the first for statement and it's working fine now.
I work with Odoo 10.
In Front-End Development, I have different forms available today for all types of users. I would like to post only certain forms. In backend development, I create a boolean type field that will allow me to tell if this form will be visible to a public or authenticated user. The problem is that I do not know how to create a Python function to differentiate the two types of users with this boolean. Do I have to use the controller? Or rather do a function in my corresponding class? Here are the codes:
class :
# -*- coding: utf-8 -*-
from odoo import models, fields, api, http, exceptions
class InheritedWebsiteApplicationTemplate(models.Model):
# region Private attributes
_inherit = 'website.application.template'
# endregion
# region Fields declaration
user_not_connected = fields.Boolean(string="Formulaire pour utilisateur non connecté")
controller :
#http.route(['/my/web-demarche'], type='http', auth="public", method=['GET'], website=True)
# #http.route(['/my/web-demarche'], type='http', auth='user', method=['GET'], website=True)
def get_demarche(self, date_begin=None, date_end=None, **post):
"""Display all requests owned by the current user."""
template_name = 'grc.demarche'
context = self._prepare_portal_layout_values()
# On va chercher les demandes de l'utilisateur, le filtre se fait par un ir.rule
domain = []
archive_groups = self._get_archive_groups('website.application', domain)
if date_begin and date_end:
domain += [('create_date', '>=', date_begin), ('create_date', '<', date_end)]
domain += [('applicant_id', '=', request.env.uid)]
user_requests = request.env['website.application'].sudo().search(domain)
# On va chercher les modèles de demandes actifs (mais pas ceux sans demande multiple si déjà demandés)
request_templates = get_available_requests(request.env.user, user_requests)
if request.httprequest.method == 'POST':
if post.get('suivi', False):
record_ticket = request.env['website.application'].search([('name','=',str(post.get('suivi', False)))], limit=1)
if record_ticket:
return request.redirect('/my/web-demarche/' + str(record_ticket.name))
else:
context.update({'error_not_found': True})
context.update({
'website_requests': user_requests,
'request_templates': request_templates,
'archive_groups': archive_groups,
'date': date_begin
})
return request.render(template_name, context)
Result (template) :
The scenario is the following:
I have a database where I store users and I want to keep the data (database) and the logic (bussiness) layers completely isolated.
I've created several specific exceptions to display the correct message in the interface (Flask) layer. Such as UserNotFound, UserEmailUsed, UserNameTaken
All the code is in a single file cruduser.py
Conection to the database:
def connect():
'''Connect to the database'''
return MySQLdb.connect(
"localhost",
"root",
"****",
"myflaskapp",
cursorclass=MySQLdb.cursors.DictCursor
)
Get users by email and/or username (Returns a User object):
def get_user_by_email(user):
'''Receive a user object, check whether email is used, if not raise
UserNotFound else creates a user object and returns'''
with connect() as cursor:
cursor.execute(
'''SELECT * FROM users WHERE email = %s''',
[user.email]
)
user = cursor.fetchone()
if user is None:
raise UserNotFound
return User(**user)
def get_user_by_username(user):
'''Receive a user object, check whether username is used, if not raise
UserNotFound else creates a user object and returns'''
with connect() as cursor:
cursor.execute(
'''SELECT * FROM users WHERE username = %s''',
[user.username]
)
user = cursor.fetchone()
if user is None:
raise UserNotFound
return User(**user)
Now to check duplicates I created the following code when a new user signs up:
def create_user(user):
'''Receive a user object, check if it is available and creates a new user
and inserts it into the database'''
available_user(user)
with connect() as cursor:
cursor.execute(
"""INSERT INTO users(email, username, password) VALUES(%s,%s,%s)""",
(user.email, user.username, user.password)
)
def available_user(user):
'''Receive a user object, check email and username, if email is used, raise
UserEmailUsed if username is used raise UserNameTaken else returns True'''
try:
get_user_by_email(user)
raise UserEmailUsed
except UserNotFound:
pass
try:
get_user_by_username(user)
raise UserNameTaken
except UserNotFound:
pass
return True
My questions are:
Is this approach pythonic?
Is there a better way to write the available_user function?
Is it appropriate to use exceptions in this way?
Note: This is a learning project so I would like to keep as pure as possible, no flask plugins at this point.
I am not too experienced with custom Exceptions but it looks like both of them are raised in any case, in contrast to your comment (if). So I'd put this condition into the code, and maybe catch UserNotFound in a common except.
try:
if get_user_by_email(user) :
raise UserEmailUsed
if get_user_by_username(user) :
raise UserNameTaken
except UserNotFound:
pass # handled elsewhere?
https://docs.python.org/3/tutorial/errors.html#handling-exceptions
After think and try several solutions I came to this:
First, DB logic and Business Logic should be separated so there is a dbconnect.py file with the following code:
def connect():
'''Connect to the database'''
return psycopg2.pool.SimpleConnectionPool(
1,
30,
host='ec2-23-23-223-2.compute-1.amazonaws.com',
user='wmowxsiematqzx',
password='d46dff42de4d8af492900f33d322caa13a0e05174b2310eec2e3549295438ab1',
dbname='dfciqalcgr98ah'
)
#contextmanager
def get_cursor():
a = connect()
con = a.getconn()
con.autocommit = True
try:
yield con.cursor()
finally:
a.putconn(con)
def execute_query_void(query):
with get_cursor() as cursor:
cursor.execute(*query)
def execute_query_one(query):
with get_cursor() as cursor:
cursor.execute(*query)
result = cursor.fetchone()
return result
def execute_query_many(query):
with get_cursor() as cursor:
cursor.execute(*query)
result = cursor.fetchall()
return result
All possible scenarios are covered there.
Now the cruduser.py
def get_user_by_email(email):
'''Receive a user object, look for a user with the given email and
returns a user object or None'''
query = ('''SELECT email, username, password_hash, rolename FROM users
INNER JOIN roles ON users.idroles=roles.idroles
WHERE email = %s''',
[email])
user = execute_query_one(query)
if user is None:
return None
return User(**user)
def get_user_by_username(user):
'''Receive a user object, look for a user with the given username and
returns a user object or None'''
query = ('''SELECT email, username, password_hash, rolename FROM users
INNER JOIN roles ON users.idroles=roles.idroles
WHERE username = %s''',
[user.username])
user = execute_query_one(query)
if user is None:
return None
return User(**user)
And the available functionality is achieved with two functions:
def available_user(user):
'''Receive a user object, check email and username, if email is used, raise
UserEmailUsed if username is used raise UserNameTaken else returns True'''
users = get_users(user)
if len(users) == 0:
return True
for user_ in users:
if user.email == user_.email:
raise UserEmailUsed
if user.username == user_.username:
raise UserNameTaken
def get_users(user):
'''Receive a article object, check whether articlename is used, if not raise
articleNotFound else creates a article object and returns'''
query = ('''SELECT email, username
FROM users
WHERE username=%s OR email=%s;''',
(user.username, user.email))
users = execute_query_many(query)
return [User(**user) for user in users]
The reason why I switch between using get_user_by_email and get_user_by_username to get_users is for performance, the latter just uses two columns while the former returns a whole object after performing a Join.
Additionally the exception where renamed to a more explicit name and where moved from DB Logic to Business Logic file
I am writing software using the Twilio API, Flask, and Python to send and receive SMS messages. When I reply to a message, I also want to send out a message to a different phone number (thereby sending 2 messages in total, one to the original sender, and one to an entirely different individual).
I reply to an SMS using:
#app.route("/", methods=['GET', 'POST'])
def reply():
# Send other text
sendMessage(to_number, text)
# Send response
resp = twilio.twiml.Response()
resp.sms(response)
return str(resp)
Where the sendMessage function is:
from twilio.rest import TwilioRestClient
def sendMessage(to_number, text):
ACCOUNT_SID = "XXXXXXXXX"
AUTH_TOKEN = "XXXXXXXX"
twilioNumber = "XXXXXXXXX"
client = TwilioRestClient(ACCOUNT_SID, AUTH_TOKEN)
client.messages.create(
to=to_number,
from_=twilioNumber,
body=text,
)
But the sendMessage function does not actually send a text to the separate number (note the reply still works). Note also that the sendMessagefunction works fine when it is called outside of the reply function.
How should I go about sending two different texts to two different numbers in response to a text from only one of those numbers?
This worked for me to send the same message to multiple numbers:
from twilio.rest import TwilioRestClient
def sendMessage(text):
ACCOUNT_SID = "ACXXXXXXXX"
AUTH_TOKEN = "YYYYYYYYYY"
twilioNumber = "+15551235554"
client = TwilioRestClient(ACCOUNT_SID, AUTH_TOKEN)
NUMBERS = {
'Foo':'+15551235555',
'Bar':'+15551235556'
}
for name, number in NUMBERS.items():
message = client.messages.create(
to=number,
from_=twilioNumber,
body=text)
print message.sid
Maybe you can find a way to adapt this to your specific needs. Note that Twilio limits your outbound SMS to 1 SMS message segment per-second, per-number. More info on Rate-Limits
You can send multiple responses to a user reply by calling the MessagingResponse() method. I used a list to store multiple messages and then I looped over the list to send each message as it is shown below.
def incoming_message() -> str:
'''
Receive incoming messages
'''
# Get the message the user sent our Twilio number
incoming_message = request.values.get('Body', '').lower()
# Get the phone number of the person sending the text message
phone_number = request.values.get('From', None).replace('whatsapp:', '')
if validate_phone_number(phone_number):
# Get the message the user sent our Twilio number
resp = MessagingResponse()
# Determine the right reply for this message
# msg = resp.message()
# Query the database for the phone number
if incoming_message:
# deconcatenate the list of messages and send them as different messages
for message in conversations(phone_number, incoming_message):
msg = resp.message(message)
else:
pass
else:
msg.body("Lo sentimos, no se encuentra registrado en nuestro sistema. Por favor comuniquese con nosotros para poder ayudarlo.")
return str(resp)
def conversations(phone_number: str, incoming_message: str) -> list:
'''
Conversations with the user
'''
messages = list()
client = Client.query.filter_by(phone=phone_number).first()
if incoming_message:
match incoming_message:
case 'hola':
messages = [
f'¡Hola {client.name}! Gracias por hacer tu reservación con nosotros 😃', 'cómo podemos ayudarte?']
case 'adios':
messages.append(
f'¡Adios {client.name}! Esperamos verte pronto 😃')
case 'menu':
messages.append(
f'¡Hola {client.name}! Estos son los servicios que ofrecemos: \n 1. Ubicacion \n 2. Reservacion \n 3. Cancelar reservacion \n 4. Salir')
case _:
messages.append(
f'No pude entender tu respuesta 😟 Inténtalo nuevamente 👇🏼 o escribe menu para desplegar las opciones con las que podemos apoyarte.')
# return the list of messages in different messages
return messages
else:
pass