Subqueryload does not work when using slice - sqlalchemy

I have written a code in python using sqlalchemy fetching data from MySQL. When the page is 1 (start is 0 and end is 10), everything works fine but when the page is 2 (start is 10 and end is 20), the db_salon and db_user_paymant is null.
I tried using join and joinedload but still it does not work.
def get_user_bookings(db: Session, user_id: int, page: int):
start = (page - 1) * 10
end = (page * 10)
db_user_bookings = db.query(DbBasketMain).options(
subqueryload("db_basket_services"), subqueryload("db_salon"),
subqueryload("db_user_payment")).filter(
DbBasketMain.user_id == user_id, DbBasketMain.salon_id != None).slice(
start, end).all()
for each_booking in db_user_bookings:
each_booking.salon_name = each_booking.db_salon.name
for each_service in each_booking.db_basket_services:
if (each_service.barber_id):
db_barber = get_barber_by_id(
db=db, barber_id=each_service.barber_id)
each_service.barber_name = db_barber.name + " " + db_barber.family
each_service.barber_image = get_barber_profile_image(
db=db, barber_id=each_service.barber_id)
return db_user_bookings

When using slice, It is a must to use order_by.
Changing to order_by(DbBasketMain.id).slice(start, end) solved the problem.

Related

Python3 compare data from DB and write the answer back to DB

[Introduction]
I am currently creating a web application in Python 3.7.4 over CGI. (the web server does not support wsgi)
The application is a simple survey were users answer questions into a carousel form.
Answers are written in the DB (MySql) according to the respondentID.
No problems until now. Everything is working fine.
However, I have been asked to insert a logic into the survey and display results according to it.
[Objective]
Lets say I have 30 questions and users can answer 0, 1 or 2 for each of them.
Answers are processed only when the user complete the survey.
At completion, data are stored as integer inside DB as si001, si002, si003,....si030
If there are values inside the DB, I would like to count how many of each possible answer have been recorded. For example, how many 0s, how many 1s, how many 2s.
The results will be recorded inside the DB in different columns. (simaru, sisankaku, sibatsu)
What I would like to do is something similar to the code below. (code is obviously wrong)
I know the SELECT status from the DB can be summarized in one cursor.execute only, but for the time being (logic details are yet to be completed) I would like to left it as it is.
[CODE]
#app.route('/results1', methods=['GET', 'POST'])
def results1():
# Check survey completion status
cursor = mysql.connection.cursor(MySQLdb.cursors.DictCursor)
cursor.execute('SELECT * FROM private_survey WHERE privateid = %s', (session['username'],))
account = cursor.fetchone()
if account['surveystatus'] == 'Available' or account['surveystatus'] == 'Started':
# Check survey status, if available redirect to noresults
return redirect(url_for('noresults'))
else:
# Survey already completed, show results1
cursor = mysql.connection.cursor(MySQLdb.cursors.DictCursor)
cursor.execute('SELECT si001, si002, si003, si004, si005, si006, si007, si008, si009, si010, si011, si012, si013, si014, si015, si016, si017, si018, si019, si020, si021, si022, si023, si024, si025, si026, si027, si028, si029, si030 FROM private_survey INNER JOIN private_survey_answers ON private_survey.surveyid=private_survey_answers.surveyid WHERE private_survey.privateid=%s', (session['username'],))
answer1 = cursor.fetchone()
maru = 0
sankaku = 0
batsu = 0
simaru = 0
sisankaku = 0
sibatsu = 0
for x in answer1:
if x == 2:
simaru = maru + 1
if x == 1:
sisankaku = sankaku + 1
if x == 0:
sibatsu = batsu + 1
cursor.execute('UPDATE private_survey_answers INNER JOIN private_survey ON private_survey.surveyid=private_survey_answers.surveyid SET simaru = %s, sisankaku = %s, sibatsu = %s WHERE private_survey.privateid = %s', (simaru, sisankaku, sibatsu, session['username'],))
mysql.connection.commit()
return render_template('results1.html', answer1=answer1, account=account)
Any help or idea would be very appreciated.
I solved the problem using the count function.
The solution I found is both able to display the count in real time and write it in the DB for CSV download, etc..
#app.route('/results1', methods=['GET', 'POST'])
def results1():
# Check survey completion status
cursor = mysql.connection.cursor(MySQLdb.cursors.DictCursor)
cursor.execute('SELECT * FROM private_survey WHERE privateid = %s', (session['username'],))
account = cursor.fetchone()
if account['surveystatus'] == 'Available' or account['surveystatus'] == '開始':
# Check survey status, if available redirect to noresults
return redirect(url_for('noresults'))
else:
# Survey already completed, show results1
cursor = mysql.connection.cursor(MySQLdb.cursors.DictCursor)
cursor.execute('SELECT si001, si002, si003, si004, si005, si006, si007, si008, si009, si010, si011, si012, si013, si014, si015, si016, si017, si018, si019, si020, si021, si022, si023, si024, si025, si026, si027, si028, si029, si030 FROM private_survey INNER JOIN private_survey_answers ON private_survey.surveyid=private_survey_answers.surveyid WHERE private_survey.privateid=%s', (session['username'],))
answer1 = cursor.fetchone()
cnt = [answer1['si001'], answer1['si002'], answer1['si003'], answer1['si004'], answer1['si005']]
cnt.count(2)
cnt.count(1)
cnt.count(0)
cursor.execute('UPDATE private_survey_answers INNER JOIN private_survey ON private_survey.surveyid=private_survey_answers.surveyid SET simaru = %s, sisankaku = %s, sibatsu = %s WHERE private_survey.privateid = %s', (cnt.count(2), cnt.count(1), cnt.count(0), session['username'],))
mysql.connection.commit()
return render_template('results1.html', answer1=answer1, account=account, cnt=cnt)

Python Flask paginate formulation

I'm learning Flask Web Development book , and in Chapter 13 , it's about "comments for the blog post"
The route function of post is as below , the book said "when page = -1" , it will caculate how much comments totally , and to be divided "FLASKY_COMMENTS_PER_PAGE" , then it can know how many pages totally and decided which is the last page that you will go to.
But what confused me is that why the "(post.comments.count()" need to subtract 1 ???
i.e. If the comments quantity is 22 , then I added 1 comment
The caculation should be (23-1)//FLASKY_COMMENTS_PER_PAGE + 1 ???
I really don't know why I should subtract 1....
#main.route('/post/<int:id>')
def post(id):
post = Post.query.get_or_404(id)
form = CommentForm()
if form.validate_on_submit():
comment = Comment(body = form.body.data, post = post, author = current_user._get_current_object())
db.session.add(comment)
flash('Your comment has been published.')
return redirect(url_for('.post',id = post.id, page = -1))
page = request.args.get('page',1,type=int)
if page == -1:
page = (post.comments.count()-1)//current_app.config['FLASKY_COMMENTS_PER_PAGE']+1
pagination = post.comments.order_by(Comment.timestamp.asc()).paginate(
page,per_page = current_app.config['FLASKY_COMMENTS_PER_PAGE'],
error_out = False)
comments = pagination.items
return render_template('post.html',posts=[post],form = form,comments=comments,pagination = pagination)
Let's see this line:
page = (post.comments.count()-1)//current_app.config['FLASKY_COMMENTS_PER_PAGE']+1
let FLASKY_COMMENTS_PER_PAGE be 10. The page numbering starts from 1. Without the subtracting when there are 9 comments: 9//10 + 1 = 0 + 1 = 1 which is still good, but when you got 10 comments: 10//10 + 1 = 1 + 1 = 2. So you got 2 pages instead of 1. That's why you need to subtract 1 from the total comments.

creating mysql table from dymanic list python

I have a list of strings that change dynamically. I need to create a MySQL table where each string in the list is a name of a column.Note: I have found some examples using sqlalchemy but it didn't help me at all.
here is my try:
f = open(filepath,"r")
pluginoutput= f.read()
pluginoptojson = json.loads(pluginoutput)
columnsnames = (pluginoptojson["columns"])
countcolumns = len(pluginoptojson["columns"])
count = 0
lst = []
for name in columnsnames:
if count < countcolumns:
lst.append(str(name))
count +=1
lst.append("caseid")
createsqltable = """CREATE TABLE IF NOT EXISTS %s""" + for t in columnsnames: """ """ (test)
c.execute(createsqltable)
conn.commit()
c.close()
conn.close()
my head is about to explode of thinking. any help will be appreciated.
first thanks alot to Irnzcig.
I just tested his recommendation and it worked like charm. the code is:
pluginoutput= f.read()
pluginoptojson = json.loads(pluginoutput)
columnsnames = (pluginoptojson["columns"])
countcolumns = len(pluginoptojson["columns"])
count = 0
lst = []
for name in columnsnames:
if count < countcolumns:
lst.append(str(name))
count +=1
lst.append("caseid")
table_name = "test1"
createsqltable = """CREATE TABLE IF NOT EXISTS """ + table_name + " (" + " VARCHAR(50),".join(lst) + " VARCHAR(50))"
c.execute(createsqltable)
conn.commit()
c.close()
conn.close()
gc.collect()

importing complex data from csv to mysql table

I am trying to import data to a table. Basically its a MCQs. All my questions are having superscripts and subscripts, for example X2 , and log52....
I have more than 2000 records, i have to import it. But after importing it comes in plain format, not taking powers. My DB is MYSQL (UTF-8)
Here is the example data
If log5 2, log5 (2x - 5) and   log 5(2x - 7/2)  are in AP , then x is equal to
after impoting it looks like above, but actually it should be
If log5 2, log5 (2x - 5) and   log 5(2x - 7/2)  are in AP , then x is equal to
Somebody plz suggest me how to do it
Here's a quick fix for the Subscripts:
Sub log_Script()
Dim cel As Range, rng As Range
Dim i&, k&
Dim myText$, findText$, curStr$
Set cel = Range("A1")
'myText = cel.Value
For i = 1 To Len(cel.Value)
k = k + 1
curStr = Mid(cel.Value, i, 1)
If curStr <> " " Then
findText = findText + curStr
ElseIf curStr = " " Then
findText = ""
End If
Debug.Print findText
If findText = "log" Then
If Mid(cel.Value, i + 1, 1) = " " Then
With cel.Characters(Start:=k + 2, Length:=1).Font
.Subscript = True
End With
Else
With cel.Characters(Start:=k + 1, Length:=1).Font
.Subscript = True
End With
End If
End If
Next i
End Sub
This will go through a range (set currently to be A1:A10), and for each cell, it'll look for log then take the next number and make it subscript. (Note: This is assuming all logs will have base < 10, let me know if that's not necessarily the case).
I could probably make this better, if you can post a few rows or cells from your CSV so I can see what the formatting is exacly like. (Or screenshot a part of your data, that 'd work too).

How can I refer to the main query's table in a nested subquery?

I have a table named passive than contains a list of timestamped events per user. I want to fill the attribute duration, which correspond to the time between the current row's event and the next event done by this user.
I tried the following query:
UPDATE passive as passive1
SET passive1.duration = (
SELECT min(UNIX_TIMESTAMP(passive2.event_time) - UNIX_TIMESTAMP(passive1.event_time) )
FROM passive as passive2
WHERE passive1.user_id = passive2.user_id
AND UNIX_TIMESTAMP(passive2.event_time) - UNIX_TIMESTAMP(passive1.event_time) > 0
);
This returns the error message Error 1093 - You can't specify target table for update in FROM.
In order to circumvent this limitation, I tried to follow the structure given in https://stackoverflow.com/a/45498/395857, which uses a nested subquery in the FROM clause to create an implicit temporary table, so that it doesn't count as the same table we're updating:
UPDATE passive
SET passive.duration = (
SELECT *
FROM (SELECT min(UNIX_TIMESTAMP(passive2.event_time) - UNIX_TIMESTAMP(passive.event_time))
FROM passive, passive as passive2
WHERE passive.user_id = passive2.user_id
AND UNIX_TIMESTAMP(passive2.event_time) - UNIX_TIMESTAMP(passive1.event_time) > 0
)
AS X
);
However, the passive table in the nested subquery doesn't refer to the same passive as in the main query. Because of that, all rows have the same passive.duration value. How can I refer to the main query's passive in the nested subquery? (or maybe are there some alternative ways to structure such a query?)
Try Like this....
UPDATE passive as passive1
SET passive1.duration = (
SELECT min(UNIX_TIMESTAMP(passive2.event_time) - UNIX_TIMESTAMP(passive1.event_time) )
FROM (SELECT * from passive) Passive2
WHERE passive1.user_id = passive2.user_id
AND UNIX_TIMESTAMP(passive2.event_time) - UNIX_TIMESTAMP(passive1.event_time) > 0
)
;
We can use a Python script to circumvent the issue:
'''
We need an index on user_id, timestamp to speed up
'''
#!/usr/bin/python
# -*- coding: utf-8 -*-
# Download it at http://sourceforge.net/projects/mysql-python/?source=dlp
# Tutorials: http://mysql-python.sourceforge.net/MySQLdb.html
# http://zetcode.com/db/mysqlpython/
import MySQLdb as mdb
import datetime, random
def main():
start = datetime.datetime.now()
db=MySQLdb.connect(user="root",passwd="password",db="db_name")
db2=MySQLdb.connect(user="root",passwd="password",db="db_name")
cursor = db.cursor()
cursor2 = db2.cursor()
cursor.execute("SELECT observed_event_id, user_id, observed_event_timestamp FROM observed_events ORDER BY observed_event_timestamp ASC")
count = 0
for row in cursor:
count += 1
timestamp = row[2]
user_id = row[1]
primary_key = row[0]
sql = 'SELECT observed_event_timestamp FROM observed_events WHERE observed_event_timestamp > "%s" AND user_id = "%s" ORDER BY observed_event_timestamp ASC LIMIT 1' % (timestamp, user_id)
cursor2.execute(sql)
duration = 0
for row2 in cursor2:
duration = (row2[0] - timestamp).total_seconds()
if (duration > (60*60)):
duration = 0
break
cursor2.execute("UPDATE observed_events SET observed_event_duration=%s WHERE observed_event_id = %s" % (duration, primary_key))
if count % 1000 == 0:
db2.commit()
print "Percent done: " + str(float(count) / cursor.rowcount * 100) + "%" + " in " + str((datetime.datetime.now() - start).total_seconds()) + " seconds."
db.close()
db2.close()
diff = (datetime.datetime.now() - start).total_seconds()
print 'finished in %s seconds' % diff
if __name__ == "__main__":
main()