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

feat: Single endpoint for creating creating an order #6635

Merged
merged 16 commits into from Jan 5, 2020

Conversation

prateekj117
Copy link
Member

Fixes #6320

Checklist

  • I have read the Contribution & Best practices Guide and my PR follows them.
  • My branch is up-to-date with the Upstream development branch.
  • The unit tests pass locally with my changes
  • I have added tests that prove my fix is effective or that my feature works
  • I have added necessary documentation (if appropriate)
  • All the functions created/modified in this PR contain relevant docstrings.

@prateekj117 prateekj117 changed the title Single endpoint for creating creating an order. feat: Single endpoint for creating creating an order. Nov 30, 2019
@codecov
Copy link

codecov bot commented Nov 30, 2019

Codecov Report

Merging #6635 into development will increase coverage by 0.23%.
The diff coverage is 93.38%.

Impacted file tree graph

@@               Coverage Diff               @@
##           development    #6635      +/-   ##
===============================================
+ Coverage        65.15%   65.39%   +0.23%     
===============================================
  Files              299      300       +1     
  Lines            15319    15315       -4     
===============================================
+ Hits              9981    10015      +34     
+ Misses            5338     5300      -38
Impacted Files Coverage Δ
app/models/event.py 78.7% <ø> (-0.23%) ⬇️
app/api/routes.py 100% <ø> (ø)
app/api/helpers/system_mails.py 100% <ø> (ø) ⬆️
app/views/blueprints.py 58.2% <ø> (ø)
app/api/schema/events.py 91.33% <ø> (-0.46%) ⬇️
app/api/schema/users.py 100% <ø> (ø) ⬆️
app/api/tickets.py 47.16% <0%> (-1.38%) ⬇️
app/api/helpers/csv_jobs_util.py 88.88% <100%> (+2.92%) ⬆️
tests/all/integration/api/helpers/test_errors.py 92.85% <100%> (-3.58%) ⬇️
tests/all/unit/api/helpers/test_exceptions.py 93.33% <100%> (ø)
... and 75 more

Continue to review full report at Codecov.

Legend - Click here to learn more
Δ = absolute <relative> (impact), ø = not affected, ? = missing data
Powered by Codecov. Last update 98108a2...5559090. Read the comment docs.

if result.errors:
return jsonify(result.errors)
for ticket in tickets:
ticket_info = db.session.query(Ticket).filter_by(id=int(ticket['id']), deleted_at=None).first()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do this in a single query filter(ids in ticket_ids, event_id=event_id, deleted_at=None)

db.session.add(attendee)
db.session.commit()
db.session.refresh(attendee)
attendee_list.append(attendee.id)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Append attendee directly

error="Ticket with id {} belongs to a different Event.".format(ticket['id']))
if (ticket_info.quantity - get_count(db.session.query(TicketHolder.id).filter_by(
ticket_id=int(ticket['id']), deleted_at=None))) < ticket['quantity']:
return jsonify(status='Order Unsuccessful', error='Ticket already sold out.')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These checks should be in calculate amount function itself

for ticket_amount in range(ticket['quantity']):
attendee = TicketHolder(**result[0], event_id=int(data['event_id']), ticket_id=int(ticket['id']))
db.session.add(attendee)
db.session.commit()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Commit once after the loop

db.session.refresh(order)
order_tickets = {}
for attendee_id in attendee_list:
holder = TicketHolder.query.filter(TicketHolder.id == attendee_id).one()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need to refetch the ticket holder

for attendee_id in attendee_list:
holder = TicketHolder.query.filter(TicketHolder.id == attendee_id).one()
holder.order_id = order.id
save_to_db(holder)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Again, commit once after the loop


for ticket in order_tickets:
od = OrderTicket(order_id=order.id, ticket_id=ticket, quantity=order_tickets[ticket])
save_to_db(od)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

commit once after loop

save_to_db(od)

order.quantity = order.tickets_count
return jsonify("Order successful.")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Newly created order should be returned

@iamareebjamal
Copy link
Member

As asked previously, never use update branch feature unless just before merging. The triggers and CI is ran again without any change for no reason and maintainers get a notification thinking that something has changed

@prateekj117
Copy link
Member Author

@iamareebjamal Done. I am thinking to refactor the whole calculate-order function in another PR.

if result.errors:
return jsonify(result.errors)
ticket_ids = [ticket['id'] for ticket in tickets]
ticket_info_list = db.session.query(Ticket).filter(Ticket.id.in_(ticket_ids)).all()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will allow deleted tickets as well. And as previously instructed, event filtering should be done here itself

@prateekj117
Copy link
Member Author

@iamareebjamal Done the required changes.

if not args:
data = request.get_json()
else:
data = json.loads(args[0])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As mentioned previously, this should not be done

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No benefit of merging an intermediate state PR

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@iamareebjamal I will start working on that PR as soon as this gets merged. I wanted to keep them separate as this PR already contains a big change. And the second change is more of a refactor.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's why it can be taken in this PR only. It's not a huge change compared to what has been done already. It makes no sense to break something first and then fix later in a separate PR

for ticket_information in ticket_info_list:
if ticket_information.id == int(ticket['id']):
ticket_info = ticket_information
break
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What is this doing?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You have unnecessariy made an O(n) function to ne O(n^2)

@prateekj117
Copy link
Member Author

@iamareebjamal Did the changes. Please review.

ticket_ids = [int(ticket['id']) for ticket in tickets]
ticket_info_list = db.session.query(Ticket).filter(Ticket.id.in_(ticket_ids)).filter_by(event_id=data['event_id'],
deleted_at=None).all()
ticket_ids_found = [ticket_information.id for ticket_information in ticket_info_list]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
ticket_ids_found = [ticket_information.id for ticket_information in ticket_info_list]
ticket_ids_found = {ticket_information.id for ticket_information in ticket_info_list}

if result.errors:
return jsonify(result.errors)
ticket_ids = [int(ticket['id']) for ticket in tickets]
ticket_info_list = db.session.query(Ticket).filter(Ticket.id.in_(ticket_ids)).filter_by(event_id=data['event_id'],
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not tickets or ticket_list

ticket_ids_found = [ticket_information.id for ticket_information in ticket_info_list]
tickets_not_found = list(set(ticket_ids) - set(ticket_ids_found))
if tickets_not_found:
return jsonify(status='Order Unsuccessful', error='Ticket with id {} was not found.'.format(tickets_not_found))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There can be multiple tickets not found. Also, send event ID as well. Ticket 324 may exist but not under the event requested

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@iamareebjamal I am sending the list of all the tickets not found. Regarding example of ticket 324, I am already passing event_id as well which fetching the tickets, so those tickets will also throw tickets not found.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will throw: Ticket with ID 324, 325, 326 was not found

Everyone wants to see: Tickets with ID 324, 325, 326 were not found in Event 432

Hence, again #6635 (comment)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It will throw: Ticket with ID 324, 325, 326 was not found in Event 432

Everyone wants to see: Tickets with ID 324, 325, 326 were not found in Event 432

return jsonify(status='Order Unsuccessful', error='Ticket with id {} was not found.'.format(tickets_not_found))
for ticket in tickets:
ticket_info = None
for ticket_information in ticket_info_list:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nested for loop = Automatically not approved. There is no need of nested for loop here

ticket_ids_found = [ticket_information.id for ticket_information in ticket_info_list]
tickets_not_found = list(set(ticket_ids) - set(ticket_ids_found))
if tickets_not_found:
return jsonify(status='Order Unsuccessful', error='Ticket with id {} was not found.'.format(tickets_not_found))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will send HTTP status code as 200, which means OK. Which means order was created successfully

ticket_info = ticket_information
if (ticket_info.quantity - get_count(db.session.query(TicketHolder.id).filter_by(
ticket_id=int(ticket['id']), deleted_at=None))) < ticket['quantity']:
return jsonify(status='Order Unsuccessful', error='Ticket already sold out.')
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will send HTTP status code as 200, which means OK. Which means order was created successfully

json_api_attendee = {"data": {"attributes": data['attendee'], "type": "attendee"}}
result = schema.load(json_api_attendee)
if result.errors:
return jsonify(result.errors)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will send HTTP status code as 200, which means OK. Which means order was created successfully

@prateekj117
Copy link
Member Author

@iamareebjamal Done.

@iamareebjamal
Copy link
Member

#6635 (comment)

@prateekj117
Copy link
Member Author

@iamareebjamal Did those changes.

@iamareebjamal
Copy link
Member

Read again

@@ -213,5 +223,5 @@ def calculate_order_amount(tickets, discount_code):
'ticket_fee': round(ticket_fee, 2),
'sub_total': round(sub_total, 2)
})
return dict(tax_included=tax_included, total_amount=round(total_amount, 2), total_tax=round(total_tax, 2),
total_discount=round(total_discount, 2), tickets=ticket_list)
return jsonify(dict(tax_included=tax_included, total_amount=round(total_amount, 2), total_tax=round(total_tax, 2),
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should not return JSON response. This function should have no idea about request or response or anything. This function does not know that it is part of flask application. This should be a pure python function, otherwise what was the point of extracting it out?

@@ -134,7 +135,16 @@ def create_onsite_attendees_for_order(data):
del data['on_site_tickets']


def calculate_order_amount(tickets, discount_code):
def calculate_order_amount(data):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't change the function signature, it was fine. This should take tickets and discount code only.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Create a wrapper function if you want to

@@ -214,4 +224,4 @@ def calculate_order_amount(tickets, discount_code):
'sub_total': round(sub_total, 2)
})
return dict(tax_included=tax_included, total_amount=round(total_amount, 2), total_tax=round(total_tax, 2),
total_discount=round(total_discount, 2), tickets=ticket_list)
total_discount=round(total_discount, 2), tickets=ticket_list)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

continuation line over-indented for visual indent

@@ -1,16 +1,17 @@
import logging
from datetime import timedelta, datetime, timezone

from flask import render_template
from flask import render_template, jsonify

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'flask.jsonify' imported but unused

@@ -1,22 +1,26 @@
from flask import Blueprint, jsonify, request
import json

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'json' imported but unused

iamareebjamal
iamareebjamal previously approved these changes Dec 14, 2019
@iamareebjamal iamareebjamal changed the title feat: Single endpoint for creating creating an order. feat: Single endpoint for creating creating an order Dec 14, 2019
@niranjan94
Copy link
Member

Codacy Here is an overview of what got changed by this pull request:

Issues
======
+ Solved 2
- Added 2
           

Complexity increasing per file
==============================
- app/api/custom/orders.py  10
- app/api/helpers/order.py  2
         

See the complete overview on Codacy

@iamareebjamal iamareebjamal merged commit e758cfc into fossasia:development Jan 5, 2020
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging this pull request may close these issues.

API for order creation with attendee info using a single endpoint
4 participants