Skip to content

Commit

Permalink
Merge branch 'development' into feature/flask-cli
Browse files Browse the repository at this point in the history
  • Loading branch information
hongquan committed May 28, 2022
2 parents 6cb0468 + 9aa9157 commit bb0ea57
Show file tree
Hide file tree
Showing 117 changed files with 29,612 additions and 3,168 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ repos:
- id: pyupgrade
args: [--py38-plus]
- repo: https://github.com/hadialqattan/pycln
rev: 0.0.1
rev: v1.1.0
hooks:
- id: pycln
args: [--config=pyproject.toml]
Expand Down
6 changes: 3 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@ FROM base as builder

RUN apk update && \
apk add --virtual build-deps make git g++ python3-dev musl-dev jpeg-dev zlib-dev libevent-dev file-dev libffi-dev openssl && \
apk add postgresql-dev
apk add postgresql-dev libxml2-dev libxslt-dev
# PDF Generation: weasyprint (libffi-dev jpeg-dev already included above)
RUN apk add --virtual gdk-pixbuf-dev

ENV POETRY_HOME=/opt/poetry \
POETRY_VIRTUALENVS_IN_PROJECT=true \
POETRY_NO_INTERACTION=1

ENV PATH="$POETRY_HOME/bin:$PATH"

RUN set -eo pipefail; wget -O - https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python -
RUN set -eo pipefail; wget -O - https://install.python-poetry.org | python -

WORKDIR /opt/pysetup

Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# Open Event Server test
# Open Event Server

![Open Event Server](/docs/images/open-event-server.png)

[![GitHub release](https://img.shields.io/github/release/fossasia/open-event-server.svg)](https://github.com/fossasia/open-event-server/releases/latest)
[![Build Status](https://travis-ci.org/fossasia/open-event-server.svg?branch=development)](https://travis-ci.org/fossasia/open-event-server)
[![CircleCI Build Staus Badge](https://img.shields.io/circleci/build/github/fossasia/open-event-server?label=CircleCI%20Build)](https://www.circleci.com/gh/fossasia/open-event-server)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/1ac554483fac462797ffa5a8b9adf2fa?style=flat-square)](https://www.codacy.com/app/fossasia/open-event-server)
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/f5036c0e23b44270ad24397e338b8412)](https://www.codacy.com/gh/fossasia/open-event-server/dashboard?utm_source=github.com&utm_medium=referral&utm_content=fossasia/open-event-server&utm_campaign=Badge_Grade)
[![Codecov branch](https://codecov.io/gh/fossasia/open-event-server/branch/development/graph/badge.svg?style=flat-square)](https://codecov.io/gh/fossasia/open-event-server)
[![Gitter](https://img.shields.io/badge/chat-on%20gitter-ff006f.svg?style=flat-square)](https://gitter.im/fossasia/open-event-server)
[![Reviewed by Hound](https://img.shields.io/badge/Reviewed_by-Hound-8E64B0.svg)](https://houndci.com)
Expand Down Expand Up @@ -199,7 +199,7 @@ Clone the repo and set up the server according to the steps listed. Make sure yo

```
# Install Poetry
curl -sSL https://raw.githubusercontent.com/python-poetry/poetry/master/get-poetry.py | python -
curl -sSL https://install.python-poetry.org | python -
source ~/.profile
# Install Python dependencies
Expand Down
44 changes: 42 additions & 2 deletions app/api/custom/orders.py
Original file line number Diff line number Diff line change
@@ -1,23 +1,29 @@
import os

from datetime import datetime


from flask import Blueprint, jsonify, make_response, request
from flask.helpers import send_from_directory
from flask_jwt_extended import current_user, jwt_required
from sqlalchemy.orm.exc import NoResultFound

from app.api.custom.schema.order_amount import OrderAmountInputSchema
from app.api.helpers.db import safe_query
from app.api.helpers.db import safe_query, save_to_db
from app.api.helpers.errors import ForbiddenError, NotFoundError, UnprocessableEntityError
from app.api.helpers.mail import send_email_to_attendees
from app.api.helpers.order import calculate_order_amount, create_pdf_tickets_for_holder
from app.api.helpers.order import calculate_order_amount, create_pdf_tickets_for_holder, on_order_completed
from app.api.helpers.permission_manager import has_access
from app.api.orders import validate_attendees
from app.api.schema.orders import OrderSchema
from app.extensions.limiter import limiter
from app.models import db
from app.models.order import Order
from app.models.order import OrderTicket
from app.models.ticket import Ticket
from app.models.ticket_holder import TicketHolder
from app.api.helpers.payment import StripePaymentsManager


order_blueprint = Blueprint('order_blueprint', __name__, url_prefix='/v1/orders')
ticket_blueprint = Blueprint('ticket_blueprint', __name__, url_prefix='/v1/tickets')
Expand Down Expand Up @@ -144,6 +150,12 @@ def create_order():
db.session.commit()
order.populate_and_save()

order_tickets = OrderTicket.query.filter_by(order_id=order.id).all()
for order_ticket in order_tickets:
ticket_info = ticket_map[order_ticket.ticket.id]
order_ticket.price = ticket_info.get('price')
save_to_db(order_ticket)

return OrderSchema().dump(order)


Expand Down Expand Up @@ -171,3 +183,31 @@ def ticket_attendee_pdf(attendee_id):
if not os.path.isfile(file_path):
create_pdf_tickets_for_holder(ticket_holder.order)
return send_from_directory('../', file_path, as_attachment=True)

@order_blueprint.route('/<string:order_identifier>/verify', methods=['POST'])
def verify_order_payment(order_identifier):

order = Order.query.filter_by(identifier=order_identifier).first()

if order.payment_mode == 'stripe':
try:
payment_intent = StripePaymentsManager.retrieve_payment_intent(order.event, order.stripe_payment_intent_id)
except Exception as e:
raise e

if payment_intent['status'] == 'succeeded':
order.status = 'completed'
order.completed_at = datetime.utcnow()
order.paid_via = payment_intent['charges']['data'][0]['payment_method_details']['type']
order.transaction_id = payment_intent['charges']['data'][0]['balance_transaction']
if payment_intent['charges']['data'][0]['payment_method_details']['type'] == 'card' :
order.brand = payment_intent['charges']['data'][0]['payment_method_details']['card']['brand']
order.exp_month = payment_intent['charges']['data'][0]['payment_method_details']['card']['exp_month']
order.exp_year = payment_intent['charges']['data'][0]['payment_method_details']['card']['exp_year']
order.last4 = payment_intent['charges']['data'][0]['payment_method_details']['card']['last4']
save_to_db(order)

on_order_completed(order)


return jsonify({ 'payment_status': order.status})
2 changes: 1 addition & 1 deletion app/api/custom/schema/order_amount.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
class TicketSchema(Schema):
id = fields.Integer(required=True)
quantity = fields.Integer(default=1)
price = fields.Float()
price = fields.Float(allow_none=True)


class OrderAmountInputSchema(Schema):
Expand Down
6 changes: 2 additions & 4 deletions app/api/data_layers/ChargesLayer.py
Original file line number Diff line number Diff line change
Expand Up @@ -50,15 +50,13 @@ def create_object(self, data, view_kwargs):

# charge through stripe
if order.payment_mode == 'stripe':
if not data.get('stripe'):
raise UnprocessableEntityError({'source': ''}, "stripe token is missing")
if not order.event.can_pay_by_stripe:
raise ConflictError(
{'': ''}, "This event doesn't accept payments by Stripe"
)

success, response = TicketingManager.charge_stripe_order_payment(
order, data['stripe']
success, response = TicketingManager.create_payment_intent_for_order_stripe(
order
)
data['status'] = success
data['message'] = response
Expand Down
49 changes: 43 additions & 6 deletions app/api/groups.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
from app.models.role import Role
from app.models.user import User
from flask import request
from flask_jwt_extended import current_user
from flask_rest_jsonapi import ResourceDetail, ResourceList, ResourceRelationship
from flask_rest_jsonapi.exceptions import ObjectNotFound

from app.api.bootstrap import api
from app.api.helpers.db import safe_query_kwargs
from app.api.helpers.errors import ForbiddenError
from app.api.helpers.permission_manager import has_access
from app.api.helpers.permission_manager import has_access, is_logged_in
from app.api.helpers.permissions import jwt_required
from app.api.schema.groups import GroupSchema

Expand All @@ -17,6 +20,23 @@
from app.models.users_groups_role import UsersGroupsRoles


def is_owner_or_organizer(group, user):
"""
Checks if the user is admin, owner or organizer of group
"""
is_admin = user.is_staff
is_owner = group.user == user
is_organizer = False
organizer_role = Role.query.filter_by(name='organizer').first()
if organizer_role:
is_organizer = bool(
UsersGroupsRoles.query.filter_by(
group_id=group.id, role_id=organizer_role.id, accepted=True
).all()
)
return is_admin or is_owner or is_organizer


class GroupListPost(ResourceList):
"""
Create and List Groups
Expand All @@ -37,6 +57,10 @@ def before_create_object(self, data, view_kwargs):
if not has_access('is_owner', event_id=event):
raise ForbiddenError({'source': ''}, "Event owner access required")

def after_create_object(self, group, data, view_kwargs):
if data.get('banner_url'):
start_image_resizing_tasks(group, data['banner_url'])

schema = GroupSchema
decorators = (jwt_required,)
methods = [
Expand All @@ -47,6 +71,7 @@ def before_create_object(self, data, view_kwargs):
'model': Group,
'methods': {
'before_create_object': before_create_object,
'after_create_object': after_create_object,
},
}

Expand Down Expand Up @@ -121,15 +146,20 @@ def before_update_object(self, group, data, view_kwargs):
:return:
"""

user = User.query.filter_by(id=current_user.id).one()
if not is_logged_in() or not is_owner_or_organizer(group, user):
raise ForbiddenError(
{'source': 'user_id'}, "Group owner or organizer access required"
)

for event in data.get('events', []):
if not has_access('is_owner', event_id=event):
raise ForbiddenError({'source': ''}, "Event owner access required")

decorators = (
api.has_permission(
'is_user_itself', methods="PATCH,DELETE", fetch="user_id", model=Group
),
)
if data.get('banner_url'):
start_image_resizing_tasks(group, data['banner_url'])

decorators = (jwt_required,)
schema = GroupSchema
methods = ["GET", "PATCH", "DELETE"]
data_layer = {
Expand Down Expand Up @@ -158,3 +188,10 @@ class GroupRelationship(ResourceRelationship):
'session': db.session,
'model': Group,
}


def start_image_resizing_tasks(group, banner_url):
group_id = str(group.id)
from .helpers.tasks import resize_group_images_task

resize_group_images_task.delay(group_id, banner_url)
20 changes: 13 additions & 7 deletions app/api/helpers/calendar/ical.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
from urllib.parse import quote

import pytz
from app.api.helpers.utilities import remove_html_tags
from flask import jsonify
from flask_jwt_extended import current_user
from icalendar import Calendar, Event
from icalendar import Calendar, Event, Timezone
from sqlalchemy import or_
from sqlalchemy.orm import joinedload

Expand All @@ -24,14 +25,19 @@ def to_ical(event, include_sessions=False, my_schedule=False, user_id=None):
event_component.add('dtstart', event.starts_at_tz)
event_component.add('dtend', event.ends_at_tz)
event_component.add('location', event.normalized_location)
event_component.add('description', event.description)
event_component.add('description', remove_html_tags(event.description))
if event.has_coordinates:
event_component.add('geo', (event.latitude, event.longitude))
if event.owner_description:
event_component.add('organizer', event.owner_description)
event_component.add('organizer', remove_html_tags(event.owner_description))

cal.add_component(event_component)

timezone = Timezone()
timezone.add('TZID', event.timezone)

cal.add_component(timezone)

if include_sessions:
sessions_query = (
Session.query.filter_by(event_id=event.id)
Expand Down Expand Up @@ -66,17 +72,17 @@ def to_ical(event, include_sessions=False, my_schedule=False, user_id=None):
else ""
)
session_link_heading = (
"Join using link: " + session_video_url + "<br/>"
" Join using link: " + session_video_url + " "
if session_video_url
else ""
)
session_description = (
" "
+ "Room: "
+ session.microlocation.name
+ "<br/>"
+ " "
+ session_link_heading
+ "<br/>"
+ " "
+ session.short_abstract
)
session_component = Event()
Expand All @@ -95,7 +101,7 @@ def to_ical(event, include_sessions=False, my_schedule=False, user_id=None):
session_component.add(
'dtend', session.ends_at.astimezone(pytz.timezone(event.timezone))
)
session_component.add('description', session_description)
session_component.add('description', remove_html_tags(session_description))
session_component.add('url', event.site_link + '/session/' + str(session.id))

cal.add_component(session_component)
Expand Down
Loading

0 comments on commit bb0ea57

Please sign in to comment.