Pass QuerySet object id to model to get self - python

I am trying to trigger a method in my models to run by hitting a url.When it hits it returns and error saying "unsupported operand type(s) for -: 'datetime.datetime' and 'NoneType'".
views.py
from django.shortcuts import render
from rest_framework import viewsets
from .serializers import EntrySerializer
from .models import Entry
class EntryView(viewsets.ModelViewSet):
serializer_class = EntrySerializer
queryset = Entry.objects.all()
entryId = Entry.objects.filter(end_time__isnull=True).values('id')
# returns <QuerySet [{'id': 8}]>
for id in entryId:
entryIdNum = id['id']
#returns 8
entry = Entry()
def ToggleView(request):
entry.toggle_paused()
entry.save()
models.py
from django.db import models
from django.db.models import F, ExpressionWrapper, fields
from django.utils import timezone
from dateutil.relativedelta import relativedelta
from decimal import Decimal
class Entry(models.Model):
start_time = models.DateTimeField()
end_time = models.DateTimeField(blank=True, null=True, db_index=True)
seconds_paused = models.PositiveIntegerField(default=0)
pause_time = models.DateTimeField(blank=True, null=True)
comments = models.TextField(blank=True)
date_updated = models.DateTimeField(auto_now=True)
hours = models.DecimalField(max_digits=11, decimal_places=5, default=0)
def _str_(self):
return self.TUID
#property
def total_hours(self):
"""
Determined the total number of hours worked in this entry
"""
total = self.get_total_seconds() / 3600.0
# in case seconds paused are greater than the elapsed time
if total < 0:
total = 0
return total
#property
def is_paused(self):
"""
Determine whether or not this entry is paused
"""
return bool(self.pause_time)
def pause(self):
"""
If this entry is not paused, pause it.
"""
print('Pause Hit')
if not self.is_paused:
self.pause_time = timezone.now()
def pause_all(self):
"""
Pause all open entries
"""
entries = self.user.timepiece_entries.filter(end_time__isnull=True)
for entry in entries:
entry.pause()
entry.save()
def unpause(self, date=None):
print('Unpause Hit')
if self.is_paused:
if not date:
date = timezone.now()
delta = date - self.pause_time
self.seconds_paused += delta.seconds
self.pause_time = None
def toggle_paused(self):
"""
Toggle the paused state of this entry. If the entry is already paused,
it will be unpaused; if it is not paused, it will be paused.
"""
print('Toggle Pause Hit')
if self.is_paused:
self.unpause()
else:
self.pause()
def check_overlap(self, entry_b, **kwargs):
"""Return True if the two entries overlap."""
consider_pause = kwargs.get('pause', True)
entry_a = self
# if entries are open, consider them to be closed right now
if not entry_a.end_time or not entry_b.end_time:
return False
# Check the two entries against each other
start_inside = entry_a.start_time > entry_b.start_time \
and entry_a.start_time < entry_b.end_time
end_inside = entry_a.end_time > entry_b.start_time \
and entry_a.end_time < entry_b.end_time
a_is_inside = entry_a.start_time > entry_b.start_time \
and entry_a.end_time < entry_b.end_time
b_is_inside = entry_a.start_time < entry_b.start_time \
and entry_a.end_time > entry_b.end_time
overlap = start_inside or end_inside or a_is_inside or b_is_inside
if not consider_pause:
return overlap
else:
if overlap:
max_end = max(entry_a.end_time, entry_b.end_time)
min_start = min(entry_a.start_time, entry_b.start_time)
diff = max_end - min_start
diff = diff.seconds + diff.days * 86400
total = entry_a.get_total_seconds() + entry_b.get_total_seconds() - 1
if total >= diff:
return True
return False
def is_overlapping(self):
if self.start_time and self.end_time:
entries = self.user.timepiece_entries.filter(
Q(end_time__range=(self.start_time, self.end_time)) |
Q(start_time__range=(self.start_time, self.end_time)) |
Q(start_time__lte=self.start_time, end_time__gte=self.end_time)
)
totals = entries.aggregate(max=Max('end_time'), min=Min('start_time'))
totals['total'] = 0
for entry in entries:
totals['total'] = totals['total'] + entry.get_total_seconds()
totals['diff'] = totals['max'] - totals['min']
totals['diff'] = totals['diff'].seconds + \
totals['diff'].days * 86400
if totals['total'] > totals['diff']:
return True
else:
return False
else:
return None
def save(self, *args, **kwargs):
self.hours = Decimal('%.5f' % round(self.total_hours, 5))
super(Entry, self).save(*args, **kwargs)
def get_total_seconds(self):
"""
Determines the total number of seconds between the starting and
ending times of this entry. If the entry is paused, the end_time is
assumed to be the pause time. If the entry is active but not paused,
the end_time is assumed to be now.
"""
start = self.start_time
end = self.end_time
if not end:
if self.is_paused:
end = self.pause_time
print(end)
print('------------------')
else:
end = timezone.now()
delta = end - start
if self.is_paused:
# get_paused_seconds() takes elapsed time into account, which we do not want
# in this case, so subtract seconds_paused instead to account for previous pauses
seconds = delta.seconds - self.seconds_paused
else:
seconds = delta.seconds - self.get_paused_seconds()
return seconds + (delta.days * 86400)
I know the error is comming from the method get_total_seconds(). When self.start_time is called it returns Entry object (None). How can I pass entryIdNum or entryId from views to the model so it knows what self is?

Related

How to check if 2 elements are same in python OOP?

class Course:
def __init__(self, name, classroom, instructor, day, start_time, end_time):
self.name = name
self.classroom = classroom
self.instructor = instructor
self.day = day
self.start_time = start_time
self.end_time = end_time
class Schedule:
def __init__(self):
self.courses = []
program = [["9","10","11","12","13","14","15","16","17"],["9","10","11","12","13","14","15","16","17"],["9","10","11","12","13","14","15","16","17"],["9","10","11","12","13","14","15","16","17"],["9","10","11","12","13","14","15","16","17"]]
def add_course(self, course):
self.courses.append(course)
def print_schedule(self):
days = ["Monday","Tuesday","Wednesday","Thursday","Friday"]
program = [["9","10","11","12","13","14","15","16","17"],["9","10","11","12","13","14","15","16","17"],["9","10","11","12","13","14","15","16","17"],["9","10","11","12","13","14","15","16","17"],["9","10","11","12","13","14","15","16","17"]]
for course in self.courses:
for j in range(course.start_time-9,course.end_time-8):
program[days.index(course.day)][j] += f" {course.name} class from {course.instructor} at {course.classroom}"
for i in range(len(days)):
print(days[i],":")
for k in program[i]:
print(k)
schedule = Schedule()
schedule.add_course(Course("Physics","MED A11","James","Monday",9,11))
schedule.add_course(Course("Logic Design","EEB 4105","Jack","Wednesday",9,10))
schedule.add_course(Course("Logic Design","EEB 4205","Jack","Wednesday",15,17))
schedule.print_schedule()
Here I wanted to create an weekly schedule, I want it to write something when two classes collide. So their self.day need to be same and the times need to intersect.
For times I can do something like
time = {for i in range(start_time,end_time+1)}
if time1.intersection(time2) != 0:
#...
But I don't know how to reach 2 different Course elements at the same time. Also would be great if you have any suggestions for this code.
You can add a method to the Course class that checks it against another Course to see if they collide. When you add a course, have it loop through the existing courses to see if it collides with the existing courses in your Schedule.
class Course:
def __init__(self, name, classroom, instructor, day, start_time, end_time):
self.name = name
self.classroom = classroom
self.instructor = instructor
self.day = day
self.start_time = start_time
self.end_time = end_time
def check_collision(self, other):
if self.day == other.day:
if other.start_time < self.start_time < other.end_time:
return True
if other.start_time < self.end_time < other.end_time:
return True
if self.start_time < other.start_time < self.end_time:
return True
if self.start_time < other.end_time < self.end_time:
return True
return False
class Schedule:
def __init__(self):
self.courses = []
self.program = [
["9","10","11","12","13","14","15","16","17"],
["9","10","11","12","13","14","15","16","17"],
["9","10","11","12","13","14","15","16","17"],
["9","10","11","12","13","14","15","16","17"],
["9","10","11","12","13","14","15","16","17"],
]
def add_course(self, course):
for c in self.courses:
if course.check_collision(c):
print(f'New course has collision with course: {c.name} on {c.day}: {c.start_time}-{c.end_time}')
break
else:
self.courses.append(course)
def print_schedule(self):
days = ["Monday","Tuesday","Wednesday","Thursday","Friday"]
program = [x.copy() for x in self.program]
for course in self.courses:
for j in range(course.start_time-9,course.end_time-8):
program[days.index(course.day)][j] += f" {course.name} class from {course.instructor} at {course.classroom}"
for i in range(len(days)):
print(days[i],":")
for k in program[i]:
print(k)
So based on your Code, what you can do is define a compare function for the class Schedule, you make a Schedule object to hold many courses. So if you want to access a course within a Schedule you need to do schedule.courses[i]. But I suggest you add the following function to the courses class to solve your problem.
def compare_course(self, other_course):
my_time = {i for i in range(start_time, end_time+1)}
other_time = {i for i in range(other_course.start_time, other_course.end_time+1)}
if my_time.intersection(other_course.time) != set():
return "Course Collides"
else:
return "Course does not collide"

Access object class Python in List

I have a question about accessing the object class from the list in python, I have compared my cases to the questions that were on stack overflow but that didn't work. I present the problem as follows
Class Booking has the properties start_date, end_date
from datetime import *
class Booking:
def __init__(self):
self.year = int(input("Enter the Year: "))
self.month = int(input("Enter the Month: "))
self.day = int(input("Enter the Day: "))
self.start_date = datetime.now()
self.end_date = datetime(self.year, self.month, self.day)
Class Room is abstract class
I create a booking(list) to store the object of the Booking class which is initialized when the Room class is initialized.
from abc import *
from booking import Booking
class Room(ABC):
bookings = []
def __init__(self, price, capacity):
self.booking = Booking()
self.price = price
self.capacity = capacity
self.bookings.append(self.booking)
#abstractmethod
def is_booked(self, start_date, end_date):
pass
SingleBed inherits Room(ABC) with price = 150 and capachity =2
from room import Room
class Singlebed(Room):
def __init__(self):
super(Singlebed, self).__init__(150, 2)
def is_booked(self, start_date, end_date):
if start_date >= end_date:
print("EndDate must be greater than StartDate")
is_Booked = False
for check_booking in self.bookings:
is_Booked = check_booking... (I wants to access objects.end_date in class Booking is saved in bookings)
single1 = Singlebed()
single2 = Singlebed()
single3 = Singlebed()
single4 = Singlebed()
single5 = Singlebed()
My question: How do I access object.end_date which is initialized with single1, single2, ...
You are iterating over your self.bookings so "check_booking" is your Booking instance and contains the attribute start_date, end_date, year, month, day
def is_booked(self, start_date, end_date):
if start_date >= end_date:
print("EndDate must be greater than StartDate")
is_Booked = False
for check_booking in self.bookings:
is_Booked = check_booking.start_date

How to filter Django object to get top X number of objects with highest property value

So I have a class called Hero with 150 objects. Each object has a property Winrate. I want to get the top 12 heros based on winrate.
class Hero(models.Model):
hero_name = models.CharField(max_length=20, default = 'Dota 2 Hero')
hero_id = models.IntegerField()
def __str__(self):
return str(self.hero_id)
def get_winrate(self):
wins = len(Match.objects.filter(heros_won = Hero.objects.get(hero_id = self.hero_id)))
losses = len(Match.objects.filter(heros_lost = Hero.objects.get(hero_id = self.hero_id)))
if wins + losses != 0:
return round((wins / (wins + losses)),2)
else:
return 0
winrate = property(get_winrate)
I tried alot of filters but couldn't get it to work.
I would make winrate an attribute of your Hero class as following.
class Hero(models.Model):
hero_name = models.CharField(max_length=20, default = 'Dota 2 Hero')
hero_id = models.IntegerField()
winrate = models.IntegerField()
def _get_winrate(self):
wins = len(Match.objects.filter(heros_won = Hero.objects.get(hero_id = self.hero_id)))
losses = len(Match.objects.filter(heros_lost = Hero.objects.get(hero_id = self.hero_id)))
if wins + losses != 0:
return round((wins / (wins + losses)),2)
else:
return 0
def save(*args, **kwargs):
self.winrate = self._getwinrate()
return super().save(*args, **kwargs)
Then you'll be able to order your request.
super_heroes = Hero.objects.order_by('-winrate')[:12]
EDIT: you shouldn't use len() on a queryset but count() like this:
wins = Match.objects.filter(heros_won=self.pk).count()
Why don't you use the natural primary key instead of this hero_id?

Django: cannot get an Model instance from ForwardManyToOneDescriptor (ForeignKey)

I have the following code in accounts/signals/__init__.py:
from django.db.models.signals import post_save
from django.dispatch import receiver
from orders.models import Order
from accounts.models import Balance
#receiver(post_save, sender=Order)
def update_referral_balance(sender, **kwargs):
if len(sender.user.referrals_set.all()):
# TODO: Add referralTransaction
new_referral_revenue = sender.user.referrals_set.get().revenue
revenue_from_trade = \
new_referral_revenue - sender.old_referral_revenue
balance, created = \
Balance.objects.get(user=sender.user, currency=sender.currency)
balance.balance += revenue_from_trade
balance.save()
Now, when running tests I am getting the following
error:======================================================================
ERROR: test_orders_with_approved_payments (payments.tests.test_views.PaymentReleaseTestCase)
----------------------------------------------------------------------
Traceback (most recent call last):
File "/pipeline/source/payments/tests/test_views.py", line 75, in setUp
self.order.save()
File "/pipeline/source/orders/models.py", line 63, in save
super(Order, self).save(*args, **kwargs)
File "/usr/local/lib/python3.5/site-packages/safedelete/models.py", line 64, in save
super(Model, self).save(**kwargs)
File "/usr/local/lib/python3.5/site-packages/django/db/models/base.py", line 708, in save
force_update=force_update, update_fields=update_fields)
File "/usr/local/lib/python3.5/site-packages/django/db/models/base.py", line 745, in save_base
update_fields=update_fields, raw=raw, using=using)
File "/usr/local/lib/python3.5/site-packages/django/dispatch/dispatcher.py", line 192, in send
response = receiver(signal=self, sender=sender, **named)
File "/pipeline/source/accounts/signals/__init__.py", line 9, in update_referral_balance
if len(sender.user.referral_set.all()):
AttributeError: 'ForwardManyToOneDescriptor' object has no attribute 'referral_set'
And indeed, when running through it in debugger, I see that the sender.user attribute is something of instance ForwardManyToOneDescriptor:
ipdb> pprint(sender.__dict__['user'].__dict__)
{'cache_name': '_user_cache',
'field': <django.db.models.fields.related.ForeignKey: user>}
What am I doing wrong?
EDIT: My Order Model:
class Order(TimeStampedModel, SoftDeletableModel, UniqueFieldMixin):
USD = "USD"
RUB = "RUB"
EUR = "EUR"
BUY = 1
SELL = 0
TYPES = (
(SELL, 'SELL'),
(BUY, 'BUY'),
)
# Todo: inherit from BTC base?, move lengths to settings?
order_type = models.IntegerField(choices=TYPES, default=BUY)
amount_cash = models.DecimalField(max_digits=12, decimal_places=2)
amount_btc = models.DecimalField(max_digits=18, decimal_places=8)
currency = models.ForeignKey(Currency)
payment_window = models.IntegerField(default=settings.PAYMENT_WINDOW)
user = models.ForeignKey(User, related_name='orders')
is_paid = models.BooleanField(default=False)
is_released = models.BooleanField(default=False)
is_completed = models.BooleanField(default=False)
is_failed = models.BooleanField(default=False)
unique_reference = models.CharField(
max_length=settings.UNIQUE_REFERENCE_LENGTH, unique=True)
admin_comment = models.CharField(max_length=200)
payment_preference = models.ForeignKey('payments.PaymentPreference',
default=None,
null=True)
class Meta:
ordering = ['-created_on']
def save(self, *args, **kwargs):
self.unique_reference = \
self.gen_unique_value(
lambda x: get_random_string(x),
lambda x: Order.objects.filter(unique_reference=x).count(),
settings.UNIQUE_REFERENCE_LENGTH
)
self.convert_coin_to_cash()
if 'is_completed' in kwargs and\
kwargs['is_completed'] and\
not self.is_completed:
self.old_referral_revenue = \
self.user.referral_set.get().revenue
super(Order, self).save(*args, **kwargs)
def convert_coin_to_cash(self):
self.amount_btc = Decimal(self.amount_btc)
queryset = Price.objects.filter().order_by('-id')[:2]
price_sell = [price for price in queryset if price.type == Price.SELL]
price_buy = [price for price in queryset if price.type == Price.BUY]
# Below calculation affect real money the client pays
assert all([len(price_sell),
price_sell[0].price_usd,
price_buy[0].price_rub,
price_buy[0].price_eur])
assert all([len(price_buy),
price_buy[0].price_usd,
price_buy[0].price_rub,
price_buy[0].price_eur])
# TODO: Make this logic more generic,
# TODO: migrate to using currency through payment_preference
# SELL
self.amount_cash = Decimal(self.amount_btc)
if self.order_type == Order.SELL and self.currency.code == Order.USD:
self.amount_cash *= price_buy[0].price_usd
elif self.order_type == Order.SELL and self.currency.code == Order.RUB:
self.amount_cash *= price_buy[0].price_rub
elif self.order_type == Order.SELL and self.currency.code == Order.EUR:
self.amount_cash *= price_buy[0].price_eur
# BUY
if self.order_type == Order.BUY and self.currency.code == Order.USD:
self.amount_cash *= price_sell[0].price_usd
elif self.order_type == Order.BUY and self.currency.code == Order.RUB:
self.amount_cash *= price_sell[0].price_rub
elif self.order_type == Order.BUY and self.currency.code == Order.EUR:
self.amount_cash *= price_sell[0].price_eur
self.amount_cash = money_format(self.amount_cash)
#property
def is_buy(self):
return self.order_type
#property
def payment_deadline(self):
"""returns datetime of payment_deadline (creation + payment_window)"""
# TODO: Use this for pay until message on 'order success' screen
return self.created_on + timedelta(minutes=self.payment_window)
#property
def expired(self):
"""Is expired if payment_deadline is exceeded and it's not paid yet"""
# TODO: validate this business rule
# TODO: Refactor, it is unreasonable to have different standards of
# time in the DB
return (timezone.now() > self.payment_deadline) and\
(not self.is_paid) and not self.is_released
#property
def payment_status_frozen(self):
"""return a boolean indicating if order can be updated
Order is frozen if it is expired or has been paid
"""
# TODO: validate this business rule
return self.expired or \
(self.is_paid and
self.payment_set.last() and
self.payment_set.last().
payment_preference.
payment_method.is_internal)
#property
def withdrawal_address_frozen(self):
"""return bool whether the withdraw address can
be changed"""
return self.is_released
#property
def has_withdraw_address(self):
"""return a boolean indicating if order has a withdraw adrress defined
"""
# TODO: Validate this business rule
return len(self.address_set.all()) > 0
#property
def withdraw_address(self):
addr = None
if self.has_withdraw_address:
addr = self.transaction_set.first().address_to.address
return addr
def __str__(self):
return "{} {} {} BTC {} {}".format(self.user.username or
self.user.profile.phone,
self.order_type,
self.amount_btc,
self.amount_cash,
self.currency)
The sender argument is the model class the signal has connected to. As you can see from the signals docs, in post_save the instance is passed in a separate argument unsurprisingly called instance.
You should write your handler like this:
#receiver(post_save, sender=Order)
def update_referral_balance(sender, instance, **kwargs):
if len(instance.user.referrals_set.all()):
etc, changing sender to instance throughout.

Why does my class raise an AttributeError?

I might be blind, but I really can't see why this class fails with:
AttributeError: NextSunday instance has no attribute 'trigger'
from datetime import datetime
from datetime import time
from datetime import timedelta
class NextSunday():
def __init__(self, trigger=None):
"""
Get date of, and number of days until, next Sunday.
Arguments:
- `trigger`: Add an extra week if less than trigger number of days.
"""
self.str = u'%s (%s days)' % (self.date().date(), self.no_days())
self.trigger = trigger
def __unicode__(self):
return self.str
def __str__(self):
return unicode(self).encode('utf-8')
def __repr__(self):
return '<Next Sunday: ' + self.str + '>'
def no_days(self):
"""Get date of next sunday. """
days = None
for i in range(7):
dt = datetime.now() + timedelta(days=i)
if dt.weekday() == 6:
days = i
# Add another week if there are less days left then trigger
if self.trigger:
if days < self.trigger:
days += 7
return days
def date(self):
# The datetime obj contains the next sunday, but also the current time
dt_of_next_sun = datetime.now() + timedelta(days=self.no_days())
# Get the whole day
date_of_next_sun = datetime.combine(dt_of_next_sun.date(),
time(23, 59))
return date_of_next_sun
You need to switch these
self.str = u'%s (%s days)' % (self.date().date(), self.no_days())
self.trigger = trigger
like this
self.trigger = trigger
self.str = u'%s (%s days)' % (self.date().date(), self.no_days())
because otherwise the no_days method is called before the self.trigger attribute is created. This is bad because the no_days method tries to read the value of the self.trigger attribute:
if self.trigger:
if days < self.trigger:

Categories