Skip to content

Commit

Permalink
Midnight release 🌃 (#700)
Browse files Browse the repository at this point in the history
* Fixed flake8 (#672)

* Added Event permissions to Jubkom temporarily (#680)

* Feat(event)/contact person (#685)

* altered Event model: contact_person with foregin key to an user added. Updated Event serializer and wrote a test for creating event with contact person

* small changes in event serializer

* formating

* feat(payments): create payment system (#675)

* Created model, started views

* fixed merge

* fixed merge

* started on paid event factory and model test

* added model tests and integration test for creating paid event

* added field to Event serializer

* Related Manager Error When testing

* Paid Event Done, Order started on

* Formatted

* change

* Order Done

* removed print

* added .env

* debugging registration tests

* added possibilty for adding a paid event without a price, test finished

* Fixed test

* started on update test of paid event

* created update test for paid event

* started on adding celery task for removing unpaid  orders

* A good days work

* finished test_not_paid_order_is_kicked_of_event_after_timeout

* added unit test for order task

* started on vipps callback

* made test for checking if vipps callback updates order status

* started on test for forcing vipps payment

* must change check of is_paid_event

* fixed check for paid event in registration

* fixed deletion of events such that the paid event also gets deleted, and made a test for it

* bugfixes

* changed event endpoint for deleting event. Now orders also get deleted

* started on viewset for orders, and made test for retrieving order for an user at a given event

* need to fix bug with payment tokens

* fixed bug with expire date for orders

* fixed new order bug

* must fix config for celery always_eager

* fixed elways eager

* fixed update of order after payment

* checks if user already has a paid order

* fixed updating from paid event to not paid event and vice versa

* fixed failing tests, and put all vipps urls and config variables in env so they are easy to change for production

* removed secret keys etc...

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* removed conf update from celery.app

* fixed paid_information property in event model

* fixed .local file

* fixed .local file

* updated order model for cascade

* formated

* fixed flake8 errors

* added migration

* Fixed migrations

* fixed migrations

* fixed order factory

* refactored code for PR

* refactored code

* altered order model to allow an order not to get deleted when a related event or user is deleted

* small changes from PR

* empty commit

---------

Co-authored-by: Lea Raknes <[email protected]>
Co-authored-by: Mads Nylund <[email protected]>
Co-authored-by: Mads Nylund <[email protected]>

* Added Fondet to allowed and fixed typo (#691)

* removed celery task_always_eager local variable (#694)

* September Update 🎉 (#695)

* Fixed flake8 (#672)

* Added Event permissions to Jubkom temporarily (#680)

* Feat(event)/contact person (#685)

* altered Event model: contact_person with foregin key to an user added. Updated Event serializer and wrote a test for creating event with contact person

* small changes in event serializer

* formating

* feat(payments): create payment system (#675)

* Created model, started views

* fixed merge

* fixed merge

* started on paid event factory and model test

* added model tests and integration test for creating paid event

* added field to Event serializer

* Related Manager Error When testing

* Paid Event Done, Order started on

* Formatted

* change

* Order Done

* removed print

* added .env

* debugging registration tests

* added possibilty for adding a paid event without a price, test finished

* Fixed test

* started on update test of paid event

* created update test for paid event

* started on adding celery task for removing unpaid  orders

* A good days work

* finished test_not_paid_order_is_kicked_of_event_after_timeout

* added unit test for order task

* started on vipps callback

* made test for checking if vipps callback updates order status

* started on test for forcing vipps payment

* must change check of is_paid_event

* fixed check for paid event in registration

* fixed deletion of events such that the paid event also gets deleted, and made a test for it

* bugfixes

* changed event endpoint for deleting event. Now orders also get deleted

* started on viewset for orders, and made test for retrieving order for an user at a given event

* need to fix bug with payment tokens

* fixed bug with expire date for orders

* fixed new order bug

* must fix config for celery always_eager

* fixed elways eager

* fixed update of order after payment

* checks if user already has a paid order

* fixed updating from paid event to not paid event and vice versa

* fixed failing tests, and put all vipps urls and config variables in env so they are easy to change for production

* removed secret keys etc...

* Update CHANGELOG.md

* Update CHANGELOG.md

* Update CHANGELOG.md

* removed conf update from celery.app

* fixed paid_information property in event model

* fixed .local file

* fixed .local file

* updated order model for cascade

* formated

* fixed flake8 errors

* added migration

* Fixed migrations

* fixed migrations

* fixed order factory

* refactored code for PR

* refactored code

* altered order model to allow an order not to get deleted when a related event or user is deleted

* small changes from PR

* empty commit

---------

Co-authored-by: Lea Raknes <[email protected]>
Co-authored-by: Mads Nylund <[email protected]>
Co-authored-by: Mads Nylund <[email protected]>

* Added Fondet to allowed and fixed typo (#691)

* removed celery task_always_eager local variable (#694)

---------

Co-authored-by: Thomas Svendal <[email protected]>
Co-authored-by: Mads Nylund <[email protected]>
Co-authored-by: Lea Raknes <[email protected]>
Co-authored-by: Mads Nylund <[email protected]>

* fix: pliz 💀

* chore: remove fucker file

---------

Co-authored-by: Thomas Svendal <[email protected]>
Co-authored-by: Mads Nylund <[email protected]>
Co-authored-by: Lea Raknes <[email protected]>
Co-authored-by: Mads Nylund <[email protected]>
  • Loading branch information
5 people authored Sep 15, 2023
1 parent 6ab8032 commit 6a6df4f
Show file tree
Hide file tree
Showing 51 changed files with 975 additions and 21 deletions.
4 changes: 2 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@

## Neste versjon

## Versjon 2022.11.17

- ✨ **Spørreskjemaer** NOK medlemmer kan lage spørreskjema.
- âš¡ **Bruker** NÃ¥ kan ikke HS lenger endre eller slette brukere.
- ⚡ **Mails** Nå logger vi på eposttjeneren kun en gang per batch med epost som sendes.
- 🦟 **Bøter** Nå skal bilder på bøter ikke lengre forsvinne.
- ✨ **Betalte arrangementer med Vipps betaling**. Det kan nå opprettes arrangementer som krever betaling for å melde seg på. Denne betalingen betales via Vipps.
- ⚡ **Nyheter** Fondesforvalere kan nå lage nyheter.

## Versjon 2022.10.13

Expand Down
1 change: 1 addition & 0 deletions app/celery.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
result_serializer="json",
timezone=settings.TIME_ZONE,
enable_utc=True,
task_always_eager=False,
)


Expand Down
3 changes: 2 additions & 1 deletion app/common/enums.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,9 @@ def admin(cls):

class Groups(ChoiceEnum):
TIHLDE = "TIHLDE"
REDAKSJONEN = "redaksjonen"
JUBKOM = "JubKom"
REDAKSJONEN = "Redaksjonen"
FONDET = "Forvaltningsgruppen"


class AppModel(ChoiceEnum):
Expand Down
20 changes: 20 additions & 0 deletions app/content/migrations/0053_event_contact_person.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
# Generated by Django 4.0.8 on 2023-09-01 06:29

from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion


class Migration(migrations.Migration):

dependencies = [
('content', '0052_event_rules_and_photo_in_user'),
]

operations = [
migrations.AddField(
model_name='event',
name='contact_person',
field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='contact_events', to=settings.AUTH_USER_MODEL),
),
]
15 changes: 15 additions & 0 deletions app/content/models/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,15 @@ class Event(BaseModel, OptionalImage, BasePermissionModel):
related_name="events",
)

contact_person = models.ForeignKey(
User,
blank=True,
null=True,
default=None,
on_delete=models.SET_NULL,
related_name="contact_events",
)

favorite_users = models.ManyToManyField(
User, related_name="favorite_events", blank=True
)
Expand Down Expand Up @@ -82,6 +91,12 @@ def website_url(self):
def expired(self):
return self.end_date <= yesterday()

@property
def is_paid_event(self):
return hasattr(self, "paid_information") and (
self.paid_information is not None or not len(self.paid_information)
)

@property
def list_count(self):
"""Number of users registered to attend the event"""
Expand Down
4 changes: 2 additions & 2 deletions app/content/models/news.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from django.conf import settings
from django.db import models

from app.common.enums import AdminGroup
from app.common.enums import AdminGroup, Groups
from app.common.permissions import BasePermissionModel
from app.util.models import BaseModel, OptionalImage

Expand All @@ -18,7 +18,7 @@ class News(BaseModel, OptionalImage, BasePermissionModel):
)
body = models.TextField()

write_access = AdminGroup.all()
write_access = [*AdminGroup.all(), Groups.FONDET]

class Meta:
verbose_name_plural = "News"
Expand Down
63 changes: 60 additions & 3 deletions app/content/serializers/event.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@
PriorityPoolCreateSerializer,
PriorityPoolSerializer,
)
from app.content.serializers.user import DefaultUserSerializer
from app.group.models.group import Group
from app.group.serializers.group import SimpleGroupSerializer
from app.payment.models.paid_event import PaidEvent
from app.payment.serializers.paid_event import PaidEventCreateSerializer


class EventSerializer(serializers.ModelSerializer):
Expand All @@ -21,6 +24,10 @@ class EventSerializer(serializers.ModelSerializer):
survey = serializers.PrimaryKeyRelatedField(many=False, read_only=True)
organizer = SimpleGroupSerializer(read_only=True)
permissions = DRYPermissionsField(actions=["write", "read"], object_only=True)
paid_information = serializers.SerializerMethodField(
required=False, allow_null=True
)
contact_person = DefaultUserSerializer(read_only=True, required=False)

class Meta:
model = Event
Expand Down Expand Up @@ -52,8 +59,19 @@ class Meta:
"enforces_previous_strikes",
"permissions",
"priority_pools",
"paid_information",
"contact_person",
)

def get_paid_information(self, obj):
if not obj.is_paid_event:
return None

paid_event = PaidEvent.objects.get(event=obj)
if paid_event:
return PaidEventCreateSerializer(paid_event).data
return None

def validate_limit(self, limit):
"""
Validate that the event limit is greater or equal to 0 and
Expand Down Expand Up @@ -102,6 +120,7 @@ class Meta:

class EventCreateAndUpdateSerializer(BaseModelSerializer):
priority_pools = PriorityPoolCreateSerializer(many=True, required=False)
paid_information = PaidEventCreateSerializer(required=False)

class Meta:
model = Event
Expand All @@ -126,21 +145,45 @@ class Meta:
"start_registration_at",
"title",
"priority_pools",
"paid_information",
"is_paid_event",
"contact_person",
)

def to_internal_value(self, data):
data.setdefault("paid_information", {})
return super().to_internal_value(data)

def create(self, validated_data):
priority_pools_data = validated_data.pop("priority_pools", [])

paid_information_data = validated_data.pop("paid_information", None)
event = super().create(validated_data)

self.set_priority_pools(event, priority_pools_data)

if len(paid_information_data):
self.set_paid_information(event, paid_information_data)

return event

def update(self, instance, validated_data):
priority_pools_data = validated_data.pop("priority_pools", None)

paid_information_data = validated_data.pop("paid_information", None)
event = super().update(instance, validated_data)
if paid_information_data and not event.is_paid_event:
PaidEvent.objects.create(
event=event,
price=paid_information_data["price"],
paytime=paid_information_data["paytime"],
)

if event.is_paid_event and not len(paid_information_data):
paid_event = PaidEvent.objects.get(event=event)
if paid_event:
paid_event.delete()
event.paid_information = None

if len(paid_information_data):
self.update_paid_information(event, paid_information_data)

if priority_pools_data:
self.update_priority_pools(event, priority_pools_data)
Expand All @@ -152,13 +195,27 @@ def update_priority_pools(self, event, priority_pools_data):
event.priority_pools.all().delete()
self.set_priority_pools(event, priority_pools_data)

def update_paid_information(self, event, paid_information_data):
event.paid_information.price = paid_information_data["price"]
event.paid_information.paytime = paid_information_data["paytime"]
event.paid_information.save()

@staticmethod
def set_priority_pools(event, priority_pool_data):
for priority_pool in priority_pool_data:
groups = priority_pool.get("groups")
priority_pool = PriorityPool.objects.create(event=event)
priority_pool.groups.add(*groups)

@staticmethod
def set_paid_information(event, paid_information_data):
price = paid_information_data.get("price")
paytime = paid_information_data.get("paytime")
paid_information = PaidEvent.objects.create(
event=event, price=price, paytime=paytime
)
paid_information.save()


class EventStatisticsSerializer(BaseModelSerializer):
has_attended_count = serializers.SerializerMethodField()
Expand Down
21 changes: 21 additions & 0 deletions app/content/serializers/registration.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,16 @@
)
from app.forms.enums import EventFormType
from app.forms.serializers.submission import SubmissionInRegistrationSerializer
from app.payment.enums import OrderStatus
from app.payment.serializers.order import OrderSerializer


class RegistrationSerializer(BaseModelSerializer):
user_info = UserListSerializer(source="user", read_only=True)
survey_submission = serializers.SerializerMethodField()
has_unanswered_evaluation = serializers.SerializerMethodField()
order = serializers.SerializerMethodField(required=False)
has_paid_order = serializers.SerializerMethodField(required=False)

class Meta:
model = Registration
Expand All @@ -26,6 +30,8 @@ class Meta:
"created_at",
"survey_submission",
"has_unanswered_evaluation",
"order",
"has_paid_order",
)

def get_survey_submission(self, obj):
Expand All @@ -35,6 +41,21 @@ def get_survey_submission(self, obj):
def get_has_unanswered_evaluation(self, obj):
return obj.user.has_unanswered_evaluations_for(obj.event)

def get_order(self, obj):
order = obj.event.orders.filter(user=obj.user).first()
if order:
return OrderSerializer(order).data
return None

def get_has_paid_order(self, obj):
for order in obj.event.orders.filter(user=obj.user):
if (
order.status == OrderStatus.CAPTURE
or order.status == OrderStatus.RESERVE
or order.status == OrderStatus.SALE
):
return True


class PublicRegistrationSerializer(BaseModelSerializer):
user_info = serializers.SerializerMethodField()
Expand Down
5 changes: 0 additions & 5 deletions app/content/tests/test_registration_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,6 @@ def test_swap_users_when_event_is_full(
event=event_with_priority_pool, user=user_in_priority_pool
)

print("event.limit")
print(event_with_priority_pool.limit)
print(event_with_priority_pool.registrations.all())

registration_not_in_priority_pool.refresh_from_db()

assert not registration_in_priority_pool.is_on_wait
Expand Down Expand Up @@ -400,7 +396,6 @@ def test_bump_user_from_wait_when_event_is_full_does_not_increments_limit(
Tests that event limit is not incremented
when an admin attempts to bump a user up from the wait list when the event is full.
"""
print(event_with_priority_pool.registrations.all())
registration = RegistrationFactory(event=event_with_priority_pool)
limit = event_with_priority_pool.limit
registration.is_on_wait = False
Expand Down
2 changes: 0 additions & 2 deletions app/content/tests/test_strike_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,6 @@ def test_strike_is_active_or_not_with_freeze_through_winter_holiday(
mock_now.return_value = today
strike = StrikeFactory.build(created_at=created_at)

print(strike.expires_at)

assert strike.active == expected_result


Expand Down
66 changes: 66 additions & 0 deletions app/content/util/event_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import os
import uuid
from datetime import datetime, timedelta

from app.payment.enums import OrderStatus
from app.payment.models.order import Order
from app.payment.tasks import check_if_has_paid
from app.payment.util.payment_utils import (
get_new_access_token,
initiate_payment,
)


def create_payment_order(event, request, registration):
"""
Checks if event is a paid event
and creates a new Vipps payment order.
"""

if event.is_paid_event:
access_token = os.environ.get("PAYMENT_ACCESS_TOKEN")
expires_at = os.environ.get("PAYMENT_ACCESS_TOKEN_EXPIRES_AT")
if not access_token or datetime.now() >= datetime.fromtimestamp(
int(expires_at)
):
(expires_at, access_token) = get_new_access_token()
os.environ.update({"PAYMENT_ACCESS_TOKEN": access_token})
os.environ.update({"PAYMENT_ACCESS_TOKEN_EXPIRES_AT": str(expires_at)})

prev_orders = Order.objects.filter(event=event, user=request.user)
has_paid_order = False

for order in prev_orders:
if (
order.status == OrderStatus.CAPTURE
or order.status == OrderStatus.RESERVE
or order.status == OrderStatus.SALE
):
has_paid_order = True
break

if not has_paid_order:

paytime = event.paid_information.paytime

expire_date = datetime.now() + timedelta(
hours=paytime.hour, minutes=paytime.minute, seconds=paytime.second
)

# Create Order
order_id = uuid.uuid4()
amount = int(event.paid_information.price * 100)
res = initiate_payment(amount, str(order_id), event.title, access_token)
payment_link = res["url"]
order = Order.objects.create(
order_id=order_id,
user=request.user,
event=event,
payment_link=payment_link,
expire_date=expire_date,
)
order.save()
check_if_has_paid.apply_async(
args=(order.order_id, registration.registration_id),
countdown=(paytime.hour * 60 + paytime.minute) * 60 + paytime.second,
)
Loading

0 comments on commit 6a6df4f

Please sign in to comment.