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

chore: Release v1.10.0 #6687

Merged
merged 29 commits into from Dec 22, 2019
Merged
Show file tree
Hide file tree
Changes from 28 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
d8a71db
chore: Remove cruft and simplify Dockerfile (#6633)
iamareebjamal Nov 28, 2019
58425a2
fix: checksum.py for PayTM (#6611)
kushthedude Nov 29, 2019
113ffcd
fix: Removing is_ticketing_enabled from event models (#6634)
kushthedude Nov 29, 2019
19c6051
chore: disable elasticsearch in .env.example (#6636)
me-diru Dec 1, 2019
b066b5b
chore: Remove egg fragment from json-api fork requirement
iamareebjamal Dec 1, 2019
c4c4f00
fix: Allow tickets to have same name as deleted ones (#6628)
codedsun Dec 3, 2019
6d098cb
chore(deps): update pyyaml requirement from ~=5.1 to ~=5.2 (#6637)
dependabot-preview[bot] Dec 4, 2019
e8b367f
fix: Use requests to fetch event and speaker images (#6639)
codedsun Dec 6, 2019
5e6cc2c
feat: management command to resize images of events and speakers (#6645)
codedsun Dec 7, 2019
23a8e17
fix: canceled order mail template changed (#6643)
codedsun Dec 8, 2019
7856bd8
fix: caught invalid url image exception in resize image tasks (#6648)
codedsun Dec 9, 2019
7071582
fix: changed safequery to normal query in resize image task (#6649)
codedsun Dec 9, 2019
80b47a6
chore(deps): update sqlalchemy-utils requirement from ~=0.35.0… (#6650)
dependabot-preview[bot] Dec 9, 2019
9a2375a
chore: Remove flask limiter warning (#6658)
iamareebjamal Dec 13, 2019
5a4ef10
feat: add created_at, modified_at in ticket_holder (#6660)
codedsun Dec 13, 2019
9ba29f5
chore: Populate flask shell context (#6663)
iamareebjamal Dec 13, 2019
85045c4
feat: Optimize marshamllow jsonapi link generation (#6656)
iamareebjamal Dec 14, 2019
ed07705
chore: Remove slow/unused aggregate queries from event schema (#6667)
iamareebjamal Dec 14, 2019
496e2e2
chore: Update heroku runtime to Python 3.7.5
iamareebjamal Dec 14, 2019
21d7635
Revert "feat: Optimize marshamllow jsonapi link generation (#6656)" (…
kushthedude Dec 21, 2019
12ed0a6
Revert "Revert "feat: Optimize marshamllow jsonapi link generation (#…
iamareebjamal Dec 21, 2019
d836d0f
fix: Order Post route view (#6683)
prateekj117 Dec 21, 2019
613c1c9
chore(deps): update geoip2 requirement from ~=2.9.0 to ~=3.0.0 (#6681)
dependabot-preview[bot] Dec 21, 2019
0b7b8d3
chore(deps): update celery requirement from ~=4.3 to ~=4.4 (#6670)
dependabot-preview[bot] Dec 21, 2019
f302b5b
chore(deps): update sqlalchemy requirement from ~=1.3.11 to ~=1… (#6671)
dependabot-preview[bot] Dec 21, 2019
7fc1b57
chore(deps): update coverage requirement from ~=4.5 to ~=5.0 (#6672)
dependabot-preview[bot] Dec 21, 2019
34921e4
feat: add job to delete ticket holders with no order id (#6662)
codedsun Dec 22, 2019
0210a01
chore: Release 1.9.0 (#6631) (#6686)
iamareebjamal Dec 22, 2019
f54d0c4
chore: Prepare for v1.10.0 (#6688)
iamareebjamal Dec 22, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion .env.example
Expand Up @@ -2,7 +2,7 @@ DATABASE_URL=postgresql://open_event_user:opev_pass@127.0.0.1:5432/oevent
INTEGRATE_SOCKETIO=false
TEST_DATABASE_URL=postgresql://open_event_user:opev_pass@127.0.0.1:5432/opev_test
APP_CONFIG=config.DevelopmentConfig
ENABLE_ELASTICSEARCH=true
ENABLE_ELASTICSEARCH=false
ELASTICSEARCH_HOST=localhost:9200

POSTGRES_USER=open_event_user
Expand Down
15 changes: 4 additions & 11 deletions Dockerfile
@@ -1,5 +1,4 @@
FROM python:3.7.4-alpine as base
LABEL maintainer="Niranjan Rajendran <me@niranjan.io>"
FROM python:3.7-alpine as base

####

Expand All @@ -9,25 +8,19 @@ WORKDIR /install

RUN apk update && \
apk add --virtual build-deps git gcc python3-dev musl-dev jpeg-dev zlib-dev libevent-dev file-dev libffi-dev openssl && \
apk add postgresql-dev && \
pip install setuptools
apk add postgresql-dev

ADD requirements.txt /requirements.txt
ADD requirements /requirements/

RUN wget https://bootstrap.pypa.io/ez_setup.py && python ez_setup.py

ENV PYTHONPATH /install/lib/python3.7/site-packages
RUN pip install --install-option="--prefix=/install" setuptools && \
LIBRARY_PATH=/lib:/usr/lib pip install --install-option="--prefix=/install" -r /requirements.txt
RUN pip install --prefix=/install --no-warn-script-location -r /requirements.txt

####

FROM base

COPY --from=builder /install /usr/local
RUN apk --no-cache add postgresql-dev ca-certificates libxslt jpeg zlib file libxml2 git && \
pip install git+https://github.com/fossasia/flask-rest-jsonapi.git@0.12.6.1#egg=flask-rest-jsonapi
RUN apk --no-cache add postgresql-libs ca-certificates libxslt jpeg zlib file libxml2

WORKDIR /data/app
ADD . .
Expand Down
10 changes: 8 additions & 2 deletions app/__init__.py
Expand Up @@ -18,6 +18,7 @@
from flask_login import current_user
from flask_jwt_extended import JWTManager
from flask_limiter import Limiter
from flask_limiter.util import get_ipaddr
from datetime import timedelta
from flask_cors import CORS
from flask_rest_jsonapi.errors import jsonapi_errors
Expand All @@ -40,7 +41,8 @@
from app.api.helpers.auth import AuthManager, is_token_blacklisted
from app.api.helpers.scheduled_jobs import send_after_event_mail, send_event_fee_notification, \
send_event_fee_notification_followup, change_session_state_on_event_completion, \
expire_pending_tickets, send_monthly_event_invoice, event_invoices_mark_due
expire_pending_tickets, send_monthly_event_invoice, event_invoices_mark_due, \
delete_ticket_holders_no_order_id
from app.models.event import Event
from app.models.role_invite import RoleInvite
from app.views.healthcheck import health_check_celery, health_check_db, health_check_migrations, check_migrations
Expand All @@ -49,14 +51,15 @@
from app.views.redis_store import redis_store
from app.views.celery_ import celery
from app.templates.flask_ext.jinja.filters import init_filters
from app.extensions import shell


BASE_DIR = os.path.dirname(os.path.abspath(__file__))

static_dir = os.path.dirname(os.path.dirname(__file__)) + "/static"
template_dir = os.path.dirname(__file__) + "/templates"
app = Flask(__name__, static_folder=static_dir, template_folder=template_dir)
limiter = Limiter(app)
limiter = Limiter(app, key_func=get_ipaddr)
env.read_envfile()


Expand Down Expand Up @@ -196,6 +199,8 @@ def create_app():
# redis
redis_store.init_app(app)

shell.init_app(app)

# elasticsearch
if app.config['ENABLE_ELASTICSEARCH']:
client.init_app(app)
Expand Down Expand Up @@ -270,6 +275,7 @@ def update_sent_state(sender=None, headers=None, **kwargs):
scheduler.add_job(expire_pending_tickets, 'cron', minute=45)
scheduler.add_job(send_monthly_event_invoice, 'cron', day=1, month='1-12')
scheduler.add_job(event_invoices_mark_due, 'cron', hour=5)
scheduler.add_job(delete_ticket_holders_no_order_id, 'cron', minute=5)
scheduler.start()


Expand Down
3 changes: 1 addition & 2 deletions app/api/auth.py
Expand Up @@ -13,7 +13,6 @@
current_user, create_access_token,
create_refresh_token, set_refresh_cookies,
get_jwt_identity)
from flask_limiter.util import get_remote_address
from healthcheck import EnvironmentDump
from sqlalchemy.orm.exc import NoResultFound

Expand Down Expand Up @@ -289,7 +288,7 @@ def resend_verification_email():
'3/hour', key_func=lambda: request.json['data']['email'], error_message='Limit for this action exceeded'
)
@limiter.limit(
'1/minute', key_func=get_remote_address, error_message='Limit for this action exceeded'
'1/minute', error_message='Limit for this action exceeded'
)
def reset_password_post():
try:
Expand Down
3 changes: 1 addition & 2 deletions app/api/custom/orders.py
@@ -1,6 +1,5 @@
from flask import Blueprint, jsonify, request
from flask_jwt_extended import current_user, jwt_required
from flask_limiter.util import get_remote_address
from sqlalchemy.orm.exc import NoResultFound


Expand Down Expand Up @@ -50,7 +49,7 @@ def ticket_attendee_authorized(order_identifier):
'5/minute', key_func=lambda: request.json['data']['user'], error_message='Limit for this action exceeded'
)
@limiter.limit(
'60/minute', key_func=get_remote_address, error_message='Limit for this action exceeded'
'60/minute', error_message='Limit for this action exceeded'
)
def resend_emails():
"""
Expand Down
4 changes: 2 additions & 2 deletions app/api/events.py
Expand Up @@ -56,8 +56,8 @@ def validate_event(user, modules, data):
if not user.can_create_event():
raise ForbiddenException({'source': ''},
"Please verify your Email")
elif data.get('is_ticketing_enabled', True) and not modules.ticket_include:
raise ForbiddenException({'source': '/data/attributes/is-ticketing-enabled'},
elif not modules.ticket_include:
raise ForbiddenException({'source': ''},
"Ticketing is not enabled in the system")
if data.get('can_pay_by_paypal', False) or data.get('can_pay_by_cheque', False) or \
data.get('can_pay_by_bank', False) or data.get('can_pay_by_stripe', False):
Expand Down
6 changes: 3 additions & 3 deletions app/api/helpers/checksum.py
Expand Up @@ -102,8 +102,8 @@ def __encode__(to_encode, iv, key):
# Pad
to_encode = __pad__(to_encode)
# Encrypt
c = AES.new(key, AES.MODE_CBC, iv)
to_encode = c.encrypt(to_encode)
c = AES.new(key.encode('UTF-8'), AES.MODE_CBC, iv.encode('UTF-8'))
to_encode = c.encrypt(to_encode.encode('UTF-8'))
# Encode
to_encode = base64.b64encode(to_encode)
return to_encode.decode("UTF-8")
Expand All @@ -113,7 +113,7 @@ def __decode__(to_decode, iv, key):
# Decode
to_decode = base64.b64decode(to_decode)
# Decrypt
c = AES.new(key, AES.MODE_CBC, iv)
c = AES.new(key.encode('UTF-8'), AES.MODE_CBC, iv.encode('UTF-8'))
to_decode = c.decrypt(to_decode)
if type(to_decode) == bytes:
# convert bytes array to str.
Expand Down
12 changes: 10 additions & 2 deletions app/api/helpers/files.py
Expand Up @@ -5,6 +5,7 @@
import urllib.parse
import urllib.request
import uuid
import requests

import PIL
from PIL import Image
Expand Down Expand Up @@ -77,7 +78,7 @@ def create_save_resized_image(image_file, basewidth=None, maintain_aspect=None,
if not image_file:
return None
filename = '{filename}.{ext}'.format(filename=get_file_name(), ext=ext)
data = urllib.request.urlopen(image_file).read()
data = requests.get(image_file).content
image_file = io.BytesIO(data)
try:
im = Image.open(image_file)
Expand Down Expand Up @@ -129,7 +130,14 @@ def create_save_image_sizes(image_file, image_sizes_type, unique_identifier=None
try:
image_sizes = ImageSizes.query.filter_by(type=image_sizes_type).one()
except NoResultFound:
image_sizes = ImageSizes(image_sizes_type, 1300, 500, True, 100, 75, 30, True, 100, 500, 200, True, 100)
image_sizes = ImageSizes(image_sizes_type, full_width=1300,
full_height=500, full_aspect=True, full_quality=80,
icon_width=75, icon_height=30, icon_aspect=True,
icon_quality=80, thumbnail_width=500, thumbnail_height=200,
thumbnail_aspect=True, thumbnail_quality=80, logo_width=500,
logo_height=200, icon_size_width_height=35, icon_size_quality=80,
small_size_width_height=50, small_size_quality=80,
thumbnail_size_width_height=500)

# Get an unique identifier from uuid if not provided
if unique_identifier is None:
Expand Down
11 changes: 7 additions & 4 deletions app/api/helpers/mail.py
Expand Up @@ -363,18 +363,21 @@ def send_email_to_attendees(order, purchaser_id, attachments=None):


def send_order_cancel_email(order):
cancel_msg = ''
if order.cancel_note:
cancel_msg = u"<br/>Message from the organizer: {cancel_note}".format(cancel_note=order.cancel_note)

send_email(
to=order.user.email,
action=TICKET_CANCELLED,
subject=MAILS[TICKET_CANCELLED]['subject'].format(
event_name=order.event.name,
invoice_id=order.invoice_number,
frontend_url=get_settings()['frontend_url']
),
html=MAILS[TICKET_CANCELLED]['message'].format(
event_name=order.event.name,
order_url=make_frontend_url('/orders/{identifier}'.format(identifier=order.identifier)),
cancel_note=order.cancel_note,
frontend_url=get_settings()['frontend_url']
frontend_url=get_settings()['frontend_url'],
cancel_msg=cancel_msg,
app_name=get_settings()['app_name']
)
)
11 changes: 11 additions & 0 deletions app/api/helpers/scheduled_jobs.py
Expand Up @@ -21,6 +21,7 @@
from app.models.session import Session
from app.models.ticket import Ticket
from app.models.ticket_fee import TicketFees, get_fee
from app.models.ticket_holder import TicketHolder

from app.settings import get_settings

Expand Down Expand Up @@ -155,6 +156,16 @@ def expire_pending_tickets():
db.session.commit()


def delete_ticket_holders_no_order_id():
from app import current_app as app
with app.app_context():
order_expiry_time = get_settings()['order_expiry_time']
TicketHolder.query.filter(TicketHolder.order_id == None, TicketHolder.deleted_at.is_(None),

Choose a reason for hiding this comment

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

comparison to None should be 'if cond is None:'

TicketHolder.created_at + datetime.timedelta(minutes=order_expiry_time)
< datetime.datetime.utcnow()).delete(synchronize_session=False)
db.session.commit()


def event_invoices_mark_due():
from app import current_app as app
with app.app_context():
Expand Down
12 changes: 7 additions & 5 deletions app/api/helpers/system_mails.py
Expand Up @@ -186,11 +186,13 @@
'recipient': 'User',
'subject': u'Your order for {event_name} has been cancelled ({invoice_id})',
'message': (
u"Hi,Your order for {event_name} has been cancelled has been cancelled by the organizer"
u"<br/>Please contact the organizer for more info" +
u"<br/>Message from the organizer: {cancel_note}"
u"<br/> <a href='{order_url}'>Click here</a> to view/download the invoice."
u"<br/>Login to manage the orders at {frontend_url} </em>"
u"Hello,"
u"<br/>your order for {event_name} has been cancelled by the organizer."
u"<br/>Please contact the organizer for more info." +
u"{cancel_msg}"
u"<br/>To manage orders please login to {frontend_url} and visit \"My Tickets\"."
u"<br/>Best regards,"
u"<br/>{app_name} Team"
)
},
EVENT_EXPORTED: {
Expand Down
12 changes: 6 additions & 6 deletions app/api/helpers/tasks.py
Expand Up @@ -126,7 +126,7 @@ def send_email_task_smtp(payload, smtp_config, headers=None):

@celery.task(base=RequestContextTask, name='resize.event.images', bind=True)
def resize_event_images_task(self, event_id, original_image_url):
event = safe_query(db, Event, 'id', event_id, 'event_id')
event = Event.query.get(event_id)
try:
logging.info('Event image resizing tasks started {}'.format(original_image_url))
uploaded_images = create_save_image_sizes(original_image_url, 'event-image', event.id)
Expand All @@ -135,7 +135,7 @@ def resize_event_images_task(self, event_id, original_image_url):
event.icon_image_url = uploaded_images['icon_image_url']
save_to_db(event)
logging.info('Resized images saved successfully for event with id: {}'.format(event_id))
except (urllib.error.HTTPError, urllib.error.URLError):
except (requests.exceptions.HTTPError, requests.exceptions.InvalidURL):
logging.exception('Error encountered while generating resized images for event with id: {}'.format(event_id))


Expand All @@ -152,7 +152,7 @@ def resize_user_images_task(self, user_id, original_image_url):
user.icon_image_url = uploaded_images['icon_image_url']
save_to_db(user)
logging.info('Resized images saved successfully for user with id: {}'.format(user_id))
except (urllib.error.HTTPError, urllib.error.URLError):
except (requests.exceptions.HTTPError, requests.exceptions.InvalidURL):
logging.exception('Error encountered while generating resized images for user with id: {}'.format(user_id))


Expand All @@ -166,13 +166,13 @@ def sponsor_logos_url_task(self, event_id):
sponsor.logo_url = new_logo_url
save_to_db(sponsor)
logging.info('Sponsor logo url successfully generated')
except(urllib.error.HTTPError, urllib.error.URLError):
except(requests.exceptions.HTTPError, requests.exceptions.InvalidURL):
logging.exception('Error encountered while logo generation')


@celery.task(base=RequestContextTask, name='resize.speaker.images', bind=True)
def resize_speaker_images_task(self, speaker_id, photo_url):
speaker = safe_query(db, Speaker, 'id', speaker_id, 'speaker_id')
speaker = Speaker.query.get(speaker_id)
try:
logging.info('Speaker image resizing tasks started for speaker with id {}'.format(speaker_id))
uploaded_images = create_save_image_sizes(photo_url, 'speaker-image', speaker_id)
Expand All @@ -181,7 +181,7 @@ def resize_speaker_images_task(self, speaker_id, photo_url):
speaker.icon_image_url = uploaded_images['icon_image_url']
save_to_db(speaker)
logging.info('Resized images saved successfully for speaker with id: {}'.format(speaker_id))
except (urllib.error.HTTPError, urllib.error.URLError):
except (requests.exceptions.HTTPError, requests.exceptions.InvalidURL):
logging.exception('Error encountered while generating resized images for event with id: {}'.format(speaker_id))


Expand Down
11 changes: 11 additions & 0 deletions app/api/schema/__init__.py
@@ -0,0 +1,11 @@
# Monkey Patch Marshmallow JSONAPI
from marshmallow_jsonapi.flask import Relationship


def serialize(self, attr, obj, accessor=None):
if self.include_resource_linkage or self.include_data:
return super(Relationship, self).serialize(attr, obj, accessor)
return self._serialize(None, attr, obj)


Relationship.serialize = serialize
7 changes: 0 additions & 7 deletions app/api/schema/events.py
Expand Up @@ -63,23 +63,17 @@ def validate_timezone(self, data, original_data):
owner_name = fields.Str(allow_none=True)
is_map_shown = fields.Bool(default=False)
has_owner_info = fields.Bool(default=False)
has_sessions = fields.Bool(default=0, dump_only=True)
has_speakers = fields.Bool(default=0, dump_only=True)
owner_description = fields.Str(allow_none=True)
is_sessions_speakers_enabled = fields.Bool(default=False)
privacy = fields.Str(default="public")
state = fields.Str(validate=validate.OneOf(choices=["published", "draft"]), allow_none=True, default='draft')
ticket_url = fields.Url(allow_none=True)
code_of_conduct = fields.Str(allow_none=True)
schedule_published_on = fields.DateTime(allow_none=True)
is_ticketing_enabled = fields.Bool(default=False)
is_featured = fields.Bool(default=False)
is_ticket_form_enabled = fields.Bool(default=True)
payment_country = fields.Str(allow_none=True)
payment_currency = fields.Str(allow_none=True)
tickets_available = fields.Float(dump_only=True)
tickets_sold = fields.Float(dump_only=True)
revenue = fields.Float(dump_only=True)
paypal_email = fields.Str(allow_none=True)
is_tax_enabled = fields.Bool(default=False)
is_billing_info_mandatory = fields.Bool(default=False)
Expand All @@ -100,7 +94,6 @@ def validate_timezone(self, data, original_data):
pentabarf_url = fields.Url(dump_only=True)
ical_url = fields.Url(dump_only=True)
xcal_url = fields.Url(dump_only=True)
average_rating = fields.Float(dump_only=True)
refund_policy = fields.String(dump_only=True,
default='All sales are final. No refunds shall be issued in any case.')
is_stripe_linked = fields.Boolean(dump_only=True, allow_none=True, default=False)
Expand Down
2 changes: 1 addition & 1 deletion app/api/schema/orders.py
Expand Up @@ -117,7 +117,7 @@ def initial_values(self, data):
type_="event")

event_invoice = Relationship(attribute='invoice',
self_view='v1.order_invoice',
self_view='v1.order_event_invoice',
self_view_kwargs={'order_identifier': '<identifier>'},
related_view='v1.event_invoice_detail',
related_view_kwargs={'id': '<id>'},
Expand Down
2 changes: 1 addition & 1 deletion app/api/schema/users.py
Expand Up @@ -100,7 +100,7 @@ class Meta:
type_='feedback')
event_invoice = Relationship(
attribute='event_invoice',
self_view='v1.user_event_invoice',
self_view='v1.user_event_invoices',
self_view_kwargs={'id': '<id>'},
related_view='v1.event_invoice_list',
related_view_kwargs={'user_id': '<id>'},
Expand Down
3 changes: 0 additions & 3 deletions app/api/tickets.py
Expand Up @@ -41,9 +41,6 @@ def before_post(self, args, kwargs, data):
deleted_at=None)) > 0:
raise ConflictException({'pointer': '/data/attributes/name'}, "Ticket already exists")

if get_count(db.session.query(Event).filter_by(id=int(data['event']), is_ticketing_enabled=False)) > 0:
raise MethodNotAllowed({'parameter': 'event_id'}, "Ticketing is disabled for this Event")

def before_create_object(self, data, view_kwargs):
"""
before create method to check if paid ticket has a paymentMethod enabled
Expand Down