Given some code like this:
# coding: utf-8
import datetime
from django.db import models
from django.contrib.auth.models import User
from django.contrib.sites.models import Site
class Premium(models.Model):
"""Access to Premium Features™®."""
end = models.DateField()
user = models.ForeignKey(User)
site = models.ForeignKey(Site)
def get_ending_premiums():
"""Get a queryset of all Premiums for which a user has none following."""
tomorrow = datetime.date.today() + datetime.timedelta(days=1)
future_premiums = Premium.objects.filter(end__gt=tomorrow).values('user', 'site')
return Premium.objects.filter(end=tomorrow).exclude(
# Would love if something like this actually worked...
user_and_site__in=future_premiums,
)
How can I complete get_ending_premiums()? One of the key things is I want Premiums only when there isn't another one that ends later, but on a per-site basis. So if a user has another Premium on groceries.com, the one about to end tomorrow doesn't get returned, but if they don't have another Premium on officesupplies.com, that one does get returned.
(Note the line with with the comments before it doesn’t actually work... that’s the part I need to complete.)
I can work out how to do this outside the ORM but I’d really prefer an ORM solution, as we’re planning on switching database vendors in a few months, so I’m trying to avoid raw SQL as much as possible.
Here’s a test for the behavior I’d like to get:
class PremiumTest(TestCase):
def test_gets_ending_premiums(self):
today = date(2020, 6, 5)
tomorrow = today + timedelta(days=1)
next_year = today + timedelta(days=366)
groceries = Site.objects.create(domain='groceries.com')
catvids = Site.objects.create(domain='catvids.com')
dave = User.objects.create_user('dave')
sally = User.objects.create_user('sally')
Premium.objects.create(user=dave, site=groceries, end=tomorrow)
Premium.objects.create(user=dave, site=groceries, end=next_year)
Premium.objects.create(user=dave, site=catvids, end=tomorrow)
Premium.objects.create(user=sally, site=groceries, end=tomorrow)
Premium.objects.create(user=sally, site=catvids, end=tomorrow)
Premium.objects.create(user=sally, site=catvids, end=next_year)
ending_premiums = get_ending_premiums(today)
ending = set((p.user, p.site) for p in ending_premiums)
self.assertNotIn((dave, groceries), ending)
self.assertIn((dave, catvids), ending)
self.assertIn((sally, groceries), ending)
self.assertNotIn((sally, catvids), ending)
self.assertEqual(2, len(ending_premiums))
I've come up with this... It's got some raw SQL but it still returns a QuerySet with normal QuerySet methods (although it uses the apparently deprecated QuerySet.extra() method)
def get_ending_premiums(day=None):
"""Get a queryset of Premiums for which a user has none following."""
if day is None:
day = date.today()
tomorrow = day + timedelta(days=1)
ending_premiums = Premium.objects.filter(
end=tomorrow,
).extra(
where=['NOT EXISTS (SELECT NULL FROM premium_premium child where premium_premium.site_id = site_id AND premium_premium.user_id = user_id AND end > %s )'],
params=[tomorrow],
)
return ending_premiums
Still wondering if there isn’t a better way...
Related
I have a function that gets queryset results from the database and does some manipulations.
When I get the results of that list, somehow the order is changed.
And exactly this is the function that makes the order change: schedules = list(set(schedule_list) - set(excluded_schedules))
So I will explain exactly:
I want to display the availability of a professional for booking an appointment. This professional has a list of available slots.
When the visitor loads the professional profile page, Django makes a query to get all time slots of the professional, and then gets all the existing appointments, then proceeds to remove the booked schedules from the total schedules, to display the rest (the available schedules). So far so good?
So, the code is the following (edited for sensitive information):
def get_home_schedules(date, professional):
day = get_day_by_date(date)
try:
schedules = Schedule.objects.filter(professional=professional, day=day, stype="Home").order_by('timefrom')
excluded_schedules = []
schedule_list = []
for s in schedules:
new_appointments = s.appointments.filter(schedule=s, date=date, status='New')
confirmed_appointments = s.appointments.filter(schedule=s, date=date, status='Confirmed')
appointments = chain(new_appointments,confirmed_appointments)
schedule_list.append(s)
if appointments:
for a in appointments:
excluded_schedules.append(a.schedule)
schedules = list(set(schedule_list) - set(excluded_schedules))
return schedules
except:
return None
The schedule model is:
class Schedule(models.Model):
professional = models.ForeignKey(d, on_delete=models.CASCADE)
timefrom = models.CharField(max_length=5, choices=HOURTIME, default="00:00")
timeto = models.CharField(max_length=5, choices=HOURTIME, default="00:00")
day = models.CharField(max_length=8, choices=DAYS, default="Monday")
stype = models.CharField(max_length=10, choices=APPOINTMENT_TYPE, default='Office')
posted = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
class Meta:
verbose_name = "Availability"
verbose_name_plural = "Availabilities"
constraints = [
models.UniqueConstraint(fields=['professional', 'timefrom', 'day'], name='unique schedule')
]
def __str__(self):
return '%s (%s - %s - %s)' % (self.professional, self.day, self.timefrom, self.timeto)
The appointment types are: Office, Online, Home
The issue I have is that I have 3 different functions, that get the available schedules, one for each appointment type, and the function works without any problems in the Office and Online types, but has the issue of order in the Home type.
The order might be because of the hour, please tell me if it's true or not (even thought I do not think it's the case because the order doesn't get messed up in the other cases).
When you do
schedules = list(set(schedule_list) - set(excluded_schedules))
the order is lost (since sets are unordered).
You can just re-sort afterwards (and with that, get rid of the .order_by() since it doesn't matter):
schedules = sorted(
set(schedule_list) - set(excluded_schedules),
key=lambda s: s.timefrom,
)
You can also optimize this to exactly 2 queries with
day = get_day_by_date(date)
schedules = Schedule.objects.filter(professional=professional, day=day, stype="Home").order_by('timefrom')
conflicting_appointment_schedule_ids = set(Appointment.objects.filter(
schedule__in=schedules,
status__in=["New", "Confirmed"],
date=date,
).values_list("schedule_id", flat=True))
return [sched for sched in schedules if sched.id not in conflicting_appointment_schedule_ids]
and I'm sure a single query would be possible too.
A python set is an unordered collection of objects, meaning that the order might change when you create the list of schedules like below:
schedules = list(set(schedule_list) - set(excluded_schedules))
You should delete the .order_by('timefrom') when retrieving the initial queryset (as the order will not be guaranteed anyways), and add it in the return statement. For example:
def get_home_schedules(date, professional):
day = get_day_by_date(date)
try:
schedules = Schedule.objects.filter(professional=professional, day=day, stype="Home")
excluded_schedules = []
schedule_list = []
for s in schedules:
new_appointments = s.appointments.filter(schedule=s, date=date, status='New')
confirmed_appointments = s.appointments.filter(schedule=s, date=date, status='Confirmed')
appointments = chain(new_appointments,confirmed_appointments)
schedule_list.append(s.id)
if appointments:
for a in appointments:
excluded_schedules.append(a.schedule.id)
schedule_ids = list(set(schedule_list) - set(excluded_schedules))
return Schedule.objects.filter(id__in=schedule_ids).order_by('timefrom')
except:
return None
I have a Django model called Attendance that has the clock in and clock in times of an employee along with the status of that entry, to see whether it's authorized or not. I then, am making another model called Payroll. I want this to check inside the Attendance entries to see all the Authorized entries and then do some action on them. How do I check all the status fields for all the entries in Attendance?
EDIT: Updated to better elaborate my question.
To better elaborate my question, this is how I've setup my Attendance model:
class CWorkAttendance(models.Model):
AUTO_ATT = "AU"
MANUAL_ATT = "MA"
WORK_ENTRY_TYPES = (
(AUTO_ATT, "Auto-Attendance"),
(MANUAL_ATT, "Manual-Attendance"),
)
AUTHORIZED = "AU"
UNAUTHORIZED = "UA"
WORK_ENTRY_STATUSES = (
(AUTHORIZED, "Athorized"),
(UNAUTHORIZED, "Un-Authorized"),
)
#Thank you motatoes
def face_locations_in(self, instance):
now = datetime.datetime.now()
return "attendance/{}/{}/in".format(instance.work_employee, now.strftime("%Y/%m/%d"))
def face_locations_out(self, instance):
now = datetime.datetime.now()
return "attendance/{}/{}/out".format(instance.work_employee, now.strftime("%Y/%m/%d"))
work_employee = models.ForeignKey('CEmployees', on_delete=models.CASCADE,)
work_start_time = models.DateTimeField()
work_end_time = models.DateTimeField(null=True)
work_duration = models.IntegerField(null=True)
work_entry_type = models.CharField(max_length=2,choices=WORK_ENTRY_TYPES)
work_entry_status = models.CharField(max_length=2, choices=WORK_ENTRY_STATUSES, default=WORK_ENTRY_STATUSES[1][0])
employee_face_captured_in = models.ImageField(upload_to=face_locations_in,)#////////
employee_face_captured_out = models.ImageField(upload_to=face_locations_out,)
If you look closely at the work_entry_status, it's a choice CharField that will contain the status of the entry (UNAUTHORIZED by default).
I want to create a Payroll model that will check for all the rows in the CWorkAttendance model and check their work_entry_status fields to see if they are Authorized, which is what I want to learn how to do.
If those fields are authorized, I want the grab the row's work_employee, work_duration and also some details from the original CEmployees row for the employee.
This is what I want my Payslip/Payroll model to look like:
class Payslip(models.Model):
GENERATED = "GEN"
CONFIRMED = "CON"
PAYSLIP_STATUS = (
(GENERATED, "Generated-UNSAVED"),
(CONFIRMED, "Confirmed-SAVED"),
)
payslip_number = models.IntegerField()#MM/YY/AUTO_GENERATED_NUMBER(AUTO_INCREMENT)
payslip_employee = models.ForeignKey('CEmployees', on_delete=models.CASCADE,)#Choose the employee from the master table CEmployees
payslip_generation_date = models.DateTimeField(default=datetime.datetime.now())#Date of the payroll generation
payslip_total_hours = models.IntegerField()#Total hours that the employee worked
payslip_from_date = models.DateField()"""The date from when the payslip will be made. The payslip will be manual for now, so generate it after choosing a a date to generate from."""
payslip_total_basic_seconds = models.IntegerField()#Total seconds the employee worked
payslip_total_ot_seconds = models.IntegerField()#Total overtime seconds the employee worked
payslip_basic_hourly_rate = models.IntegerField()#The basic hourly rate of the employee mentioned here. Take from the master employees table.
payslip_basic_ot_rate = models.IntegerField()#Taking the basic overtime rate from the master table
payslip_total_amount = models.FloatField()#The total amount of the payslip
payslip_entry_status = models.CharField(max_length=3, default=GENERATED)#The status of the pay slip.
Thanks,
Not sure if I understand your requirements well, so let me know if I misunderstood.
# `employee` is the work_employee in question
# if you don't want to filter by employee, remove `work_employee=employee`
attendances = CWorkAttendance.objects.filter(work_entry_status=CWorkAttendance.AUTHORIZED, work_employee=employee)
for attendances in attendances:
# do things with this attendance record
attendance.work_duration
attendance.employee
# ....
Update
Since you would like to do it manually, I would suggest having a separate view to generate the Payslip. The important thing is to know the date_from and the date_to for this payslip. I imagine that it is the managers who would have access to this view, so you would need the proper access controls set for it. I also think you need to have a payslip_to_date even if you are going to generate it until the current date, which will be useful for record keeping. I assume you have that column in the code below.
views.py:
from django.views import View
class GeneratePayslip(View):
"""
make sure you have the right access controls set to this view
"""
def post(self, request, **kwargs):
employee_id = kwags.POST.get("employee_id")
date_from = kwargs.POST.get("from_date")
date_to = kwargs.POST.get("to_date")
# we fetch all the objects within range
attendances = CWorkAttendance.objects.filter( \
work_entry_status=CWorkAttendance.AUTHORIZED, \
work_employee_id=employee_id, \
work_start_time__gte=date_from, \
work_end_time__lte=date_to \
)
hours = 0
for attendance in attendances:
# perform calculations to compute total sum and rate
pass
# create the payslip object here ..
# redirect to a success page and return
If you wanted to do it automatically later on, you may want to generate payslips automatically, once a month. For that you could use something like Celery to have periodic tasks that run in the background, for each employee. If this is the case you could move the above code to a file such as utils.py. you can create a method which takes employee_id, from_date, to_date, and then generate the payslip object, returning the payslip_id to the calling method
Assume I have model like this:
class Account(models.Model):
balance = models.IntegerField()
debt = models.IntegerField()
history = HistoricalRecords()
I'm using django-simple-history to get instance of the model as it would have existed at the provided date and time:
inst = Account.history.as_of(datetime.datetime.now().date)
It's working fine, but I want to get instance where balance field is represented as it would have existed at the provided date and time, and then debt field will be most recent of that date. I don't know if this is possible, didn't find anything about that.
The history ORM will return back a model based off of the one you submitted, as it existed at that point in time.
account = Account.objects.create(balance=1, debt=1)
account.save()
history_obj = account.history.last()
print(history_obj.debt) # returns 1
account.debt = 222
account.save()
new_history_obj = account.history.last()
print(new_history_obj.debt) # returns 222
Assuming you're using the Account.history.as_of() method to return the history object that you intend to be reading from, you could do this:
yesterday = datetime.datetime.now() - datetime.timedelta(days=1)
history_obj = Account.history.as_of(yesterday)
print(history_obj.debt) # returns not the current debt, but the debt as-of yesterday
Unless I'm misunderstanding what you're hoping to accomplish, you could just do this with what you have in your question:
inst = Account.history.as_of(datetime.datetime.now().date)
print(inst.debt)
I'm making a website using django.
class Member(models.Model):
...
end_date = models.DateField(blank=True, default=(datetime.now() + timedelta(days=30)))
Membership_status = models.IntegerField(blank=True, null=True, default=1) # 1 = active, 0=deactivate, 2=refund
What I want to do is comparing the end_date field to today.date every 1.a.m. and if today's day < end_date, Membership_status field is changed to 0 automatically.
I heard I should use django-kronos(https://github.com/jgorset/django-kronos).
But I can't understand the using method.
Is there anyone can tell me details how I implement what I want?
Any help will be very helpful to me, thanks!
First of all, this is not an answer to your original query, but merely a suggestion for your future,
Never pass a function call into your field defaults. If you did, the function would be evaluated at the time of your migrations. If you look into the migration files you can see for sure. Instead wrap it in a function and pass that as a callable.
Eg:
from django.utils import timezone
def TODAY():
return timezone.now().date()
def NEXT_MONTH_DAY():
return TODAY() + timedelta(days=30)
Now, in your models,
class Member(models.Model):
...
end_date = models.DateField(blank=True, default=NEXT_MONTH_DAY)
This way the function NEXT_MONTH_DAY is called whenever an instance of Member is created.
EDIT:
For your original query, I haven't tested the code, but I suppose you are looking for maybe something like this,
import kronos
#kronos.register('0 1 * * *')
def the_task():
for member in Member.objects.all():
if TODAY() == member.end_date:
member.Membership_status = 0
member.save()
Currently working on a project with TurboGears2 and ToscaWidgets2. I have a form setup with a few static fields, name, date, and point of contact information. Inside this form I have added a sub form where the user can dynamically add numerous entries in a GrowingGridLayout. The form, its layout, and submitting information is all well and good but I'm having a hard time figuring out how to capture the information from the GrowingGridLayout once it's passed on for saving. Guess the main points are, how do I know how many entries were included in the form?
Included the code for the form:
class OnrampForm(twf.Form):
title = "Onramp Submission Form"
class child(twd.CustomisedTableForm):
onramp_name = twf.TextField(validator=twc.Required)
class Destinations (twd.GrowingGridLayout):
environment = twf.SingleSelectField(label='Environment', validator=twc.Validator(required=True), options=[<OPTIONS>])
location = twf.SingleSelectField(validator=twc.Required, label='Location', options=[<OPTIONS>])
jms_type = twf.SingleSelectField(label='JMS Type', validator=twc.Validator(required=True), options=[<OPTIONS>])
subscription_type = twf.SingleSelectField(label='Subscription Type', validator=twc.Validator(required=True), options=[<OPTIONS>])
onramp_status = twf.SingleSelectField(prompt_text='Status', options=['Initial Release', 'Update'], validator=twc.Required)
current_date = datetime.date.today()
need_by_date = twd.CalendarDatePicker(validators=[twc.Required, twc.DateTimeValidator])
need_by_date.default = current_date + datetime.timedelta(days=30)
organization = twf.TextField(validator=twc.Required)
poc_name = twf.TextField(validator=twc.Required)
poc_email = twf.EmailField(validator=twc.EmailValidator)
poc_phone = twf.TextField(validator=twc.Required)
poc_address = twf.TextField()
poc_city = twf.TextField()
poc_state = twf.TextField()
onramp_form = twf.FileField()
submit = twf.SubmitButton(value="Submit")
action = "/print_args"
submit = ""
If you controller #validates against the form you should get the data into the Destination parameter which should be a list of dictionaries.
Also I just noticed you have two nested forms, that's something that might confuse TW2 pretty much. What you wanted to do is probably have OnrampForm inherit CustomisedForm and then have child inherit TableLayout. See http://turbogears.readthedocs.org/en/latest/cookbook/TwForms.html#displaying-forms
PS: note that need_by_date.default = current_date + datetime.timedelta(days=30) will always return 30 days from when the server started as you are actually storing a current_date = datetime.date.today() class variable that gets computed when the module is imported and no more.
You should use default = Deferred(lambda: datetime.date.today() + datetime.timedelta(days=30)) to achieve that