Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pagination @adithya14255 @marudhu2004 #275

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ gunicorn
humanize
itsdangerous
jinja2
lxml
lxml_html_clean
markupsafe
maya
names
Expand Down
56 changes: 39 additions & 17 deletions saythanks/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from flask_qrcode import QRcode
from . import storage
from urllib.parse import quote
from lxml.html.clean import Cleaner
from lxml_html_clean import Cleaner
from markdown import markdown

cleaner = Cleaner()
Expand Down Expand Up @@ -85,13 +85,15 @@ def decorated(*args, **kwargs):

return decorated


# Application Routes
# ------------------


@app.route('/')
def index():
if 'search_str' in session:
session.pop('search_str', None)

return render_template('index.htm.j2',
callback_url=auth_callback_url,
auth_id=auth_id,
Expand All @@ -103,23 +105,42 @@ def index():
def inbox():
# Auth0 stored account information.
profile = session['profile']

# Grab the inbox from the database.
inbox_db = storage.Inbox(profile['nickname'])
is_enabled = storage.Inbox.is_enabled(inbox_db.slug)

# pagination
page = request.args.get('page', 1, type=int)
page_size = 25
# checking for invalid page numbers
if page < 0:
return render_template("404notfound.htm.j2")
data = inbox_db.notes(page, page_size)
if page > data['total_pages'] and data['total_pages']!=0:
return render_template("404notfound.htm.j2")
is_email_enabled = storage.Inbox.is_email_enabled(inbox_db.slug)
if request.method == "GET":

# handling search with pagination
if request.method == 'POST':
if 'clear' in request.form:
session.pop('search_str', None)
return redirect(url_for('inbox'))
else:
session['search_str'] = request.form['search_str']
# regular note set with pagination
if request.method == "GET" and 'search_str' not in session:
# Send over the list of all given notes for the user.
return render_template('inbox.htm.j2',
user=profile, notes=inbox_db.notes,
inbox=inbox_db, is_enabled=is_enabled,
is_email_enabled=is_email_enabled)
search_str = request.form['search_str']
user=profile, notes=data['notes'],
inbox=inbox_db, is_enabled=is_enabled,
is_email_enabled=is_email_enabled, page=data['page'],
total_pages=data['total_pages'], search_str="Search by message body or byline")
# reassessing data when search is used
if 'search_str' in session:
data = inbox_db.search_notes(session['search_str'], page, page_size)
return render_template('inbox.htm.j2',
user=profile, notes=inbox_db.search_notes(search_str),
is_email_enabled=is_email_enabled)

user=profile, notes=data['notes'],
is_email_enabled=is_email_enabled, page=data['page'],
total_pages=data['total_pages'], search_str=session['search_str'])


@app.route('/inbox/export/<format>')
Expand Down Expand Up @@ -210,8 +231,7 @@ def display_submit_note(inbox, topic):
if not storage.Inbox.does_exist(inbox):
abort(404)
elif not storage.Inbox.is_enabled(inbox):
abort(404)

abort(404)
fake_name = get_full_name()
topic_string = topic
if topic_string:
Expand All @@ -232,7 +252,10 @@ def share_note(uuid):
abort(404)

note = storage.Note.fetch(uuid)
return render_template('share_note.htm.j2', note=note)
note_body = note.body
for i in ['<div>', '<p>', '</div>', '</p>']:
note_body = note_body.replace(i, '')
return render_template('share_note.htm.j2', note=note, note_body=note_body)


@app.route('/inbox/archive/note/<uuid>', methods=['GET'])
Expand Down Expand Up @@ -278,7 +301,7 @@ def submit_note(inbox):
note = inbox_db.submit_note(body=body, byline=byline)
return redirect(url_for('thanks'))
# Strip any HTML away.

body = markdown(body)
body = remove_tags(body)
byline = Markup(request.form['byline']).striptags()
Expand Down Expand Up @@ -351,4 +374,3 @@ def callback_handling():
# Using nickname by default, can be changed manually later if needed.
storage.Inbox.store(nickname, userid, email)
return redirect(url_for('inbox'))

2 changes: 0 additions & 2 deletions saythanks/static/css/saythanks.css
Original file line number Diff line number Diff line change
Expand Up @@ -111,8 +111,6 @@ a.share {
animation: octocat-wave 560ms ease-in-out
}

#share {}

#message {}

#from {
Expand Down
10 changes: 10 additions & 0 deletions saythanks/static/css/skeleton.css
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,16 @@ hr {
border-top: 1px solid #E1E1E1;
}

.sharelinks{
display: flex;
gap:.2em;
}

.pagination{
display: flex;
justify-content: center;
gap:2em;
}

/* Clearing
–––––––––––––––––––––––––––––––––––––––––––––––––– */
Expand Down
62 changes: 48 additions & 14 deletions saythanks/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,7 +108,6 @@ def auth_id(self):
q = sqlalchemy.text("SELECT * FROM inboxes WHERE slug=:inbox")
r = conn.execute(q, inbox=self.slug).fetchall()
return r[0]['auth_id']

@classmethod
def is_linked(cls, auth_id):
q = sqlalchemy.text('SELECT * from inboxes where auth_id = :auth_id')
Expand Down Expand Up @@ -194,34 +193,69 @@ def myemail(self):
# print("myemail prop",emailinfo)
# return emailinfo

@property
def notes(self):
"""Returns a list of notes, ordered reverse-chronologically."""
q = sqlalchemy.text("SELECT * from notes where inboxes_auth_id = :auth_id and archived = 'f'")
r = conn.execute(q, auth_id=self.auth_id).fetchall()
def notes(self,page,page_size):
"""Returns a list of notes, ordered reverse-chronologically with pagination."""
offset = (page - 1) * page_size
count_query = sqlalchemy.text("SELECT COUNT(*) FROM notes WHERE inboxes_auth_id = :auth_id AND archived = 'f'")
total_notes = conn.execute(count_query, auth_id=self.auth_id).scalar()
query = sqlalchemy.text("""
SELECT * FROM notes
WHERE inboxes_auth_id = :auth_id AND archived = 'f'
ORDER BY timestamp DESC
LIMIT :limit OFFSET :offset
""")
result = conn.execute(query, auth_id=self.auth_id, limit=page_size, offset=offset).fetchall()

notes = [
Note.from_inbox(
self.slug,
n["body"], n["byline"], n["archived"], n["uuid"], n["timestamp"]
)
for n in r
for n in result
]
return notes[::-1]

return {
"notes": notes,
"total_notes": total_notes,
"page": page,
"total_pages": (total_notes + page_size - 1) // page_size # Calculate total pages
}

def search_notes(self, search_str):
"""Returns a list of notes, queried by search string "param" """
q = sqlalchemy.text("""SELECT * from notes where ( body LIKE '%' || :param || '%' or byline LIKE '%' || :param || '%' ) and inboxes_auth_id = :auth_id""")
r = conn.execute(q, param=search_str, auth_id=self.auth_id).fetchall()
def search_notes(self, search_str, page, page_size):
offset = (page - 1) * page_size

# Count total matching notes
count_query = sqlalchemy.text("""
SELECT COUNT(*) FROM notes
WHERE (body LIKE '%' || :param || '%' OR byline LIKE '%' || :param || '%')
AND inboxes_auth_id = :auth_id
""")
total_notes = conn.execute(count_query, param=search_str, auth_id=self.auth_id).scalar()

# Retrieve paginated notes
query = sqlalchemy.text("""
SELECT * FROM notes
WHERE (body LIKE '%' || :param || '%' OR byline LIKE '%' || :param || '%')
AND inboxes_auth_id = :auth_id
ORDER BY timestamp DESC
LIMIT :limit OFFSET :offset
""")
result = conn.execute(query, param=search_str, auth_id=self.auth_id, limit=page_size, offset=offset).fetchall()

notes = [
Note.from_inbox(
self.slug,
n["body"], n["byline"], n["archived"], n["uuid"], n["timestamp"]
)
for n in r
for n in result
]
return notes[::-1]

return {
"notes": notes,
"total_notes": total_notes,
"page": page,
"total_pages": (total_notes + page_size - 1) // page_size # Calculate total pages
}

def export(self, file_format):
q = sqlalchemy.text("SELECT * from notes where inboxes_auth_id = :auth_id and archived = 'f'")
Expand Down
12 changes: 12 additions & 0 deletions saythanks/templates/404notfound.htm.j2
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<h1>404 not found!</h1><br>
The page you requested does not exist
</body>
</html>
41 changes: 27 additions & 14 deletions saythanks/templates/inbox.htm.j2
Original file line number Diff line number Diff line change
Expand Up @@ -73,13 +73,15 @@
</p>

<form action="/inbox" method="POST">
<input type="text" style="font-size:14px" size=28 placeholder="Search by message body or byline" name="search_str">
<input type="text" style="font-size:14px" size=28 placeholder="{{search_str}}" name="search_str">
<button style="font-size:10px" type="submit">Search</button>
<button type="submit" name="clear" value="true">Clear</button>
</form>

<table>
<thead>
<tr>
<th id="share">Share URL</th>
<th id="share" style="padding:5px;">Share URL</th>
<th id="message">Message</th>
<th id="from">From</th>
<th id="timestamp">Timestamp</th>
Expand All @@ -88,32 +90,43 @@
<tbody>
{% for note in notes %}
<tr>
<td class="ellipsis">
<span>
<a class="share" href="{{ url_for('share_note', uuid=note.uuid)}}">🔗</a>
<a class="twitter-share-button" target="_blank" href="https://twitter.com/intent/tweet?text={{ note.body|quote + "%0A%0A- " + note.byline + "%0A%0A" + request.base_url[0:-6] + url_for('share_note', uuid=note.uuid) + "%0A" }}" data-url=" "></a> </br>
<iframe src="https://www.facebook.com/plugins/share_button.php?href={{ request.base_url[0:-6] + url_for('share_note', uuid=note.uuid) }}&layout=button&size=small&width=67&height=20&appId" width="67" height="20" style="border:none;overflow:hidden" scrolling="no" frameborder="0" allowfullscreen="true" allow="autoplay; clipboard-write; encrypted-media; picture-in-picture; web-share">
</iframe>
</span>
</td>

<td class="ellipsis"><a class="share" href="{{ url_for('share_note', uuid=note.uuid)}}">🔗</a></td>
<td class="ellipsis"><a href="{{ url_for('share_note', uuid=note.uuid)}}"><span>{{ note.body }}</span></a></td>
<td class="ellipsis"><span>— {{ note.byline }}</span></td>
<td class="ellipsis">{{ note.timestamp.strftime('%d-%h-%Y %H:%M:%S') }}</td>
<td class="ellipsis"><strong><a class="share" href="{{ url_for('archive_note', uuid=note.uuid)}}">♻</a></strong></td>
</tr>
{% endfor %}

{% if (page==total_pages or total_pages==0) and search_str=="Search by message body or byline" %}
<tr>
<td></td>
<td>Thanks for using SayThanks.io! :)</td>
<td>Kenneth Reitz & Team</td>
<td></td>
<td></td>
</tr>
{%endif%}
</tbody>
</table>

</table><div class="pagination">
{% if total_pages!=0 %}
<a style="text-decoration:none;" href="{{ url_for('inbox', page=1) }}"><<</a>
{% if page > 1 %}
<a style="text-decoration:none;" href="{{ url_for('inbox', page=page-1) }}">Previous</a>
{% else %}
<span>Previous</span>
{% endif %}
<span> {{ page }} of {{ total_pages }}</span>
{% if page < total_pages %}
<a style="text-decoration:none;" href="{{ url_for('inbox', page=page+1) }}">Next</a>
{% else %}
<p>Next</p>
{% endif %}
<a style="text-decoration:none;" href="{{ url_for('inbox', page=total_pages) }}">>></a>
{% elif search_str!="Search by message body or byline" %}
<span> No matches found!
{% endif %}
</div>
<br><br><br><br>
<h4>Manage your Inbox:</h4>

<p>Below are some rudimentary account management tools, available, to you, today, for free!</p>
Expand Down
6 changes: 5 additions & 1 deletion saythanks/templates/share_note.htm.j2
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,11 @@
<form action="../logout" method="POST">
<button type="submit" class="logoutLblPos" >Log Out</button>
</form>

<div class="sharelinks">
<a class="twitter-share-button" target="_blank" href="https://twitter.com/intent/tweet?text={{ note_body|quote + "%0A%0A- " + note.byline + "%0A%0A" + request.root_url + url_for('share_note', uuid=note.uuid)[1:] + "%0A" }}" data-url=" "></a> </br>
<iframe src="https://www.facebook.com/plugins/share_button.php?href={{ request.root_url + url_for('share_note', uuid=note.uuid)[1:] }}&layout=button&size=small&width=67&height=20&appId" width="67" height="20" style="border:none;overflow:hidden" scrolling="no" frameborder="0" allowfullscreen="true" allow="autoplay; clipboard-write; encrypted-media; picture-in-picture; web-share"></iframe>
</div>
<br>
<form class="form-horizontal" action="./{{ user }}/submit" method="post">


Expand Down