trying to applying DRY principle to model methods in django - python

I have these two methods that are identical except in name and one variable, and it really bugs me, but no matter what I do, I can't figure out how make it so that I just pass a variable into a method in django. These are the two methods, I can post the model if it's needed but I'm fairly sure all the info that is needed is in here, but for clarity, the two model fields are 'launch', 'staff_trials' and 'published' all three are just dates, all other variables are created in the method:
#property
def progress_launch(self):
timeline = self.launch - self.published.date()
current = self.launch - datetime.now().date()
if timeline < current:
percentage == 100
else:
percentage = 100 - round((current/timeline) * 100)
min_bar = 1
max_bar = 100
if percentage is not None:
if percentage < min_bar:
return min_bar
elif percentage > max_bar:
return percentage
else:
percentage = max_bar
return percentage
#property
def progress_trials(self):
timeline = self.staff_trials - self.published.date()
current = self.staff_trials - datetime.now().date()
if timeline < current:
percentage == 100
else:
percentage = 100 - round((current/timeline) * 100)
min_bar = 1
max_bar = 100
if percentage is not None:
if percentage < min_bar:
return min_bar
elif percentage > max_bar:
return percentage
else:
percentage = max_bar
return percentage
I tried to do this:
def progress_launch(self):
return percent(trials)
def progress_trials(self):
return percent(launch)
def percent(_progress)
timeline = _progress - self.published.date()
current = _progress - datetime.now().date()
if timeline < current:
percentage == 100
else:
percentage = 100 - round((current/timeline) * 100)
min_bar = 1
max_bar = 100
if percentage is not None:
if percentage < min_bar:
return min_bar
elif percentage > max_bar:
return percentage
else:
percentage = max_bar
return percentage
But of course it didn't work. The two methods work fine, it just looks terrible and this particular model is getting rather large as it is. I appreciate this is more likely an OOP issue (Which is why I started to learn django in the first place, to learn OOP), as that is where I am struggling still unfortunately. Any help in getting this code refactored in a better way would be very much appreciated. I can't post any of the error messages as there were too many of them, but they were all about variables not being defined.

Just check what model you got using isinstance and use an appropriate field:
#property
def progress(self):
if isinstance(self, LaunchModel):
start = self.launch
else:
start = self.staff_trials
timeline = start - self.published.date()
current = start - datetime.now().date()
# ... etc
You can condense this into a single line:
start = self.launch if isinstance(self, LaunchModel) else self.staff_trials

Related

python efficient if statements conditions [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 4 months ago.
Improve this question
I created a sample program for 3 data points however, I have many data points and need more efficient code to run. The logic is I am comparing every Pi with its next P(i+1) and post comparing all the differences, I am selecting the max value and taking its relevant BSPi & SSPi.
additional condition is if P[i] is greater than p[i+1]; it should be greater than 50.
sp1=100
sp2=150
sp3=200
sp4=250
p1=90
p2=40
p3=120
p4=150
if p1-p2>=0:
d1=p1-p2-50
bsp1=sp2
ssp1=sp1
else:
d1=p2-p1
bsp1=sp1
ssp1=sp2
if p2-p3>=0:
d2=p2-p3-50
bsp2=sp3
ssp2=sp2
else:
d2=p3-p2
bsp2=sp2
ssp2=sp3
if p3-p4>=0:
d3=p3-p4-50
bsp3=sp4
ssp3=sp3
else:
d3=p4-p3
bsp3=sp3
ssp3=sp3
data = {'d1': d1,'d2': d2, 'd3': d3,}
max_data=max(data, key=data.get)
if max_data=='d1':
bsp=bsp1
ssp=ssp1
elif max_data=='d2':
bsp=bsp2
ssp=ssp2
else:
bsp=bsp3
ssp=ssp3
print(bsp)
print(ssp)
Create an array to contain sp = [sp1, ... , spn]
Create an array to contain p = [p1, ... , pn]
Initialize a value max = 0
Initialize a value index = 0
Iterate through range of p (given that len(p) > 1), calculate the difference of i and i+1 elements. If the difference is greater than max, store the difference and store the index.
In the end, store into bsp = sp[index] and ssp = sp[index+1]
def find_max_in_data(p, sp):
max_diff = 0
index = 0
for i in range(len(p)-1):
diff = abs(p[i+1] - p[i])
if diff > max_diff:
max_diff = diff
index = i
ssp = sp[index]
bsp = sp[index+1]
EDIT: For more particular code,
def find_max_in_data(p, sp):
max_diff = 0
bspindex = 0
sspindex = 0
for i in range(len(p)-1):
if p[i] - p[i+1] >= 0:
diff = p[i] - p[i+1] - 50
if diff > max_diff:
max_diff = diff
bspindex = i+1
sspindex = i
else:
diff = p[i+1] - p[i]
if diff > max_diff:
max_diff = diff
bspindex = i
sspindex = i+1
ssp = sp[sspindex]
bsp = sp[bspindex]
I suggest a different approach.
Rather than defining many discrete variables, write a class that incorporates all of the associated variables for a "data point". Something like this (with full property, str and repr functionality):
class DataPoint:
def __init__(self, p, sp):
self._p = p
self._sp = sp
self._ssp = None
self._bsp = None
self._d = None
#property
def p(self):
return self._p
#property
def sp(self):
return self._sp
#property
def ssp(self):
return self._ssp
#ssp.setter
def ssp(self, v):
self._ssp = v
#property
def bsp(self):
return self._bsp
#bsp.setter
def bsp(self, v):
self._bsp = v
#property
def d(self):
return self._d
#d.setter
def d(self, v):
self._d = v
def __repr__(self):
return f'{self.p=} {self.sp=} {self.ssp=} {self.bsp=} {self.d=}'
def __str__(self):
return f'p={self.p}, sp={self.sp}, ssp={self.ssp}, bsp={self.bsp}, d={self.d}'
Using this structure you can build a list of classes like this:
data_points = []
data_points.append(DataPoint(90, 100))
data_points.append(DataPoint(40, 150))
data_points.append(DataPoint(120, 200))
data_points.append(DataPoint(150, 250))
Note how the p and sp values relate to the ones in the question.
Now that we have a list of these classes we can write a single piece of code to process them and populate them with appropriate values.
for dpa, dpb in zip(data_points, data_points[1:]):
if dpa.p - dpb.p >= 0:
dpa.d = dpa.p - dpb.p - 50
dpa.bsp = dpb.sp
dpa.ssp = dpa.sp
else:
dpa.d = dpb.p - dpa.p
dpa.bsp = dpa.sp
dpa.ssp = dpb.sp
print(dpa)
This gives the following output:
p=90, sp=100, ssp=100, bsp=150, d=0
p=40, sp=150, ssp=200, bsp=150, d=80
p=120, sp=200, ssp=250, bsp=200, d=30
Note that there are just 3 outputs. This is as expected due to the way the comparisons are carried out.

How do you query #hybrid_property with an aggregate function in SQLAlchemy?

here is my sanerio:
i have a model named Order:
i have some fields like that:
#hybrid_property
def final_total(self):
total = self.subtotal
if self.final_discount_amount is not None:
total -= self.final_discount_amount
if self.final_tax_cost is not None:
total += self.final_tax_cost
if self.final_tip is not None:
total += self.final_tip
if self.service_fee is not None:
total += self.service_fee
return total
#final_total.expression
def final_total(cls):
return func.sum(cls.final_total).label("final_total")
i want to get final_total of a record from database using query obj.
here is my query:
session.query(Order.final_total).filter_by(order.id=1).first()
i am getting error like:
python3.8/site-packages/sqlalchemy/ext/hybrid.py", line 1090, in _expr
return ExprComparator(cls, expr(cls), self)
RecursionError: maximum recursion depth exceeded
It is possible to do it using hybrid_property but you can face huge performance problem because this final_total calculation will have to be done during query runtime...
Better option is to add new column final_total to you model (and db) and make this calculation when any of elements (e.g. final_tip, etc.) is changed and the result save in this new column.
However, if hybrid property performance is enough for you, this is implementation that should work:
from sqlalchemy import func
#hybrid_property
def final_total(self):
total = self.subtotal
if self.final_discount_amount is not None:
total -= self.final_discount_amount
if self.final_tax_cost is not None:
total += self.final_tax_cost
if self.final_tip is not None:
total += self.final_tip
if self.service_fee is not None:
total += self.service_fee
return total
#final_total.expression
def final_total(cls):
return (
cls.subtotal +
func.coalesce(cls.final_discount_amount, 0) +
func.coalesce(cls.service_fee, 0) +
func.coalesce(cls.final_tax_cost, 0) +
func.coalesce(cls.final_tip, 0)
)

QDial ange per revolution

I'm trying to make an Application with PyQt5, Python 3.7.3 using a Raspberry pi4B and a 5 inch touch screen.
The thing is that I need to make a QDial, but I want it to make more than one revolution if it goes from min range to max range. For example, if the Qdial has range from 0 to 500, I want it to make 100 points per revolution, so you have to do a full rotation 5 times to go from the min value to the max value.
This is what I've tried:
`
from PyQt5.QtWidgets import *
import sys
class Window(QWidget):
def __init__(self):
QWidget.__init__(self)
layout = QGridLayout()
self.setLayout(layout)
self.dial = QDial()
self.dial.setMinimum(0)
self.dial.setMaximum(100)
self.dial.setValue(40)
self.dial.valueChanged.connect(self.sliderMoved)
self.dial.setWrapping(True)
self.text=QLabel()
layout.addWidget(self.dial)
layout.addWidget(self.text)
self.isHigher=False
def sliderMoved(self):
print("Dial value = %i" % (self.dial.value()))
self.text.setText(str(self.dial.value()))
if(self.dial.value()==100 and self.isHigher==False):
self.higher_range()
self.isHigher=True
if(self.dial.value()==100 and self.isHigher==True):
self.lower_range()
self.isHigher=False
def higher_range(self):
self.dial.setRange(100,200)
self.dial.setValue(105)
def lower_range(self):
self.dial.setRange(0,100)
self.dial.setValue(95)
app = QApplication(sys.argv)
screen = Window()
screen.show()
sys.exit(app.exec_())
`
But this doesn't work, It keeps changing from 95 to 105 and viceversa.
QDial is a pretty peculiar control. While it's still supported, it's poorly implemented, and I believe it's by choice: due to its nature, it's really hard to add more features. I had quite an amount of experience with it, and I know it's not an easy element to deal with.
One of its issues is that it represents a monodimensional range but, visually and UI speaking, it is a bidimensional object.
What you're trying to achieve is possible, but consider that an UI element should always display its state in a clear way and have a corresponding proper behavior; that's the only way UI can tell the user the state. Physical dials don't have this issue: you also have a tactile response that tells you when the gear reaches its end.
From my experience I could tell you that you should avoid it as much as possible: it seems a nice and intuitive widget, but in reality it's very difficult to get a proper result that is actually intuitive to the user. There are some instances for which it makes sense to use it (in my case, representation of a physical knob of an electronic musical instrument). I suggest you to do some research on skeumorphism and UX aspects.
That said, this is a possible raw implementation. I've overridden some aspects (most importantly, the valueChanged signal, for naming consistency), but for a proper implementation you should do much more work (and testing).
The trick is to set the range based on the number of "revolutions": if the maximum is 500 and 5 revolutions are chosen, then the dial will have an actual maximum of 100. Then, whenever the value changes, we check whether previous value was below or above the minimum/maximum of the actual range, and change the revolution count accordingly.
Two important notes:
since QDial inherits from QAbstractSlider, it has a range(minimum, maximum + 1), and since the division could have some rest, the "last" revolution will have a different range;
I didn't implement the wheel event, as that requires further inspection and choosing the appropriate behavior depending on the "previous" value and revolution;
class SpecialDial(QDial):
_cycleValueChange = pyqtSignal(int)
def __init__(self, minimum=0, maximum=100, cycleCount=2):
super().__init__()
assert cycleCount > 1, 'cycles must be 2 or more'
self.setWrapping(True)
self.cycle = 0
self.cycleCount = cycleCount
self._minimum = minimum
self._maximum = maximum
self._normalMaximum = (maximum - minimum) // cycleCount
self._lastMaximum = self._normalMaximum + (maximum - minimum) % self._normalMaximum
self._previousValue = super().value()
self._valueChanged = self.valueChanged
self.valueChanged = self._cycleValueChange
self._valueChanged.connect(self.adjustValueChanged)
self.setRange(0, self._normalMaximum)
def value(self):
return super().value() + self._normalMaximum * self.cycle
def minimum(self):
return self._minimum
def maximum(self):
return self._maximum()
def dialMinimum(self):
return super().minimum()
def dialMaximum(self):
return super().maximum()
def adjustValueChanged(self, value):
if value < self._previousValue:
if (value < self.dialMaximum() * .3 and self._previousValue > self.dialMaximum() * .6 and
self.cycle + 1 < self.cycleCount):
self.cycle += 1
if self.cycle == self.cycleCount - 1:
self.setMaximum(self._lastMaximum)
elif (value > self.dialMaximum() * .6 and self._previousValue < self.dialMaximum() * .3 and
self.cycle > 0):
self.cycle -= 1
if self.cycle == 0:
self.setMaximum(self._normalMaximum)
new = self.value()
if self._previousValue != new:
self._previousValue = value
self.valueChanged.emit(self.value())
def setValue(self, value):
value = max(self._minimum, min(self._maximum, value))
if value == self.value():
return
block = self.blockSignals(True)
self.cycle, value = divmod(value, self._normalMaximum)
if self.dialMaximum() == self._normalMaximum and self.cycle == self.cycleCount - 1:
self.setMaximum(self._lastMaximum)
elif self.dialMaximum() == self._lastMaximum and self.cycle < self.cycleCount - 1:
self.setMaximum(self._normalMaximum)
super().setValue(value)
self.blockSignals(block)
self._previousValue = self.value()
self.valueChanged.emit(self._previousValue)
def keyPressEvent(self, event):
key = event.key()
if key in (Qt.Key_Right, Qt.Key_Up):
step = self.singleStep()
elif key in (Qt.Key_Left, Qt.Key_Down):
step = -self.singleStep()
elif key == Qt.Key_PageUp:
step = self.pageStep()
elif key == Qt.Key_PageDown:
step = -self.pageStep()
elif key in (Qt.Key_Home, Qt.Key_End):
if key == Qt.Key_Home or self.invertedControls():
if super().value() > 0:
self.cycle = 0
block = self.blockSignals(True)
super().setValue(0)
self.blockSignals(block)
self.valueChanged.emit(self.value())
else:
if self.cycle != self.cycleCount - 1:
self.setMaximum(self._lastMaximum)
self.cycle = self.cycleCount - 1
if super().value() != self._lastMaximum:
block = self.blockSignals(True)
super().setValue(self._lastMaximum)
self.blockSignals(block)
self.valueChanged.emit(self.value())
return
else:
super().keyPressEvent(event)
return
if self.invertedControls():
step *= -1
current = self.value()
new = max(self._minimum, min(self._maximum, current + step))
if current != new:
super().setValue(super().value() + (new - current))
class Window(QWidget):
def __init__(self):
QWidget.__init__(self)
layout = QGridLayout()
self.setLayout(layout)
self.dial = SpecialDial()
self.dial.valueChanged.connect(self.sliderMoved)
self.text=QLabel()
layout.addWidget(self.dial)
layout.addWidget(self.text)
def sliderMoved(self):
self.text.setText(str(self.dial.value()))
I strongly suggest you to take your time to:
consider is this is really what you want, since, as said, this kind of control can be very tricky from the UX perspective;
carefully read the code and understand its logics;

What is the minimum number of case (or if/else) statements required to calculate all unknown values corresponding to properties of an object?

Consider a right-angle triangle, which has the properties
Hypotenuse (side)
Adjacent (side)
Opposite (side)
Area
Given any 2 of these properties, it is always possible to calculate the value of the other 2. My question relates to what the most efficient/elegant way of doing this is.
At present, the only way of doing this that I can think of is to use (4C2)*2 = 12 case statements, each relating to a possible combination of inputsa that may be provided.
For example, using python you might have something like
class RightAngleTriangle():
def __init__(this, propertyType1, propertyValue1, propertyType2, propertyValue2):
this.adjacent = 0
this.opposite = 0
this.hypotenuse = 0
this.area = 0
if (propertyType1 == "adjacent" and propertyType2 == "opposite"):
this.adjacent = propertyValue1
this.opposite = propertyValue2
this.hypotenuse = (propertyValue1**2 + propertyValue2**2)**0.5
this.area = (propertyValue1 * propertyValue2)/2
elif (propertyType1 == "opposite" and propertyType2 == "adjacent"):
this.adjacent = propertyValue2
this.opposite = propertyValue1
this.hypotenuse = (propertyValue1**2 + propertyValue2**2)**0.5
this.area = (propertyValue1 * propertyValue2)/2
elif (propertyType1 == "adjacent" and propertyType2 == "hypotenuse"):
this.adjacent = propertyValue1
this.hypotenuse = propertyValue2
this.opposite = (propertyValue2**2 + propertyValue1**2)**0.5
this.area = (this.opposite * this.adjacent)/2
...and so on...
You could then create your triangle object, and print its four properties, using code (in this case python) like the below.
t1 = RightAngleTriangle("adjacent", 10, "opposite", 12)
print(t1.adjacent)
print(t1.opposite)
print(t1.hypotenuse)
print(t1.area)
This is hideous. Is there a more eligant solution to this problem?
Yes, at least two - one using args and one using key word args. So:
class RightAngleTriangle():
def __init__(self, *args):
self.adjacent = 0
self.opposite = 0
self.hypotenuse = 0
self.area = 0
for property_type, property_value in zip(args[::2], args[1::2]):
setattr(self, property_type, property_value)
if not self.adjacent:
# calculate
elif not self.opposite:
# calculate
elif not self.hypotenuse:
# calculate
self.area = (this.opposite * this.adjacent) / 2
This would work with your current input, but let's agree - it's still not very elegant solution. So, let's use kwargs:
class RightAngleTriangle():
def __init__(self, adjacent=0, opposite=0, hypotenuse=0):
self.adjacent = adjacent
self.opposite = opposite
self.hypotenuse = hypotenuse
self.area = 0
if not self.adjacent:
# calculate
elif not self.opposite:
# calculate
elif not self.hypotenuse:
# calculate
self.area = (this.opposite * this.adjacent) / 2
And now you can simply call this code as:
t1 = RightAngleTriangle(adjacent=10, opposite=12)

Python imported code taking long to run...why?

Here is the code from one python file that I import into another file...
class Crop():
def water(self):
print('not')
def harvest(self):
print('not')
def __init__(self):
self.height = 0
class Corn(Crop):
def water(self):
self.height = self.height + 2
def harvest(self):
if self.height >= 9:
return 1
else:
return 0
class Wheat(Crop):
def water(self):
self.height = self.height + 1
def harvest(self):
if self.height >= 5:
return 1
else:
return 0
class Irrigator():
def __init__(self, load):
self.load = load
def irrigate(self, field):
while self.load > 0:
self.load = self.load - 1
field.rain()
I take the above code and then I import it into another python file...
from farmhelper import *
from random import *
# Field for holding the crops.
class Field():
def rain(self):
for i in range(len(self.plants)):
self.plants[i].water()
def __init__(self, size, crop):
self.plants = [0] * size
for i in range(size):
self.plants[i] = crop()
class Combine():
def harvest(self, field):
quantity = 0
for i in range(len(field.plants)):
quantity += field.plants[i].harvest()
return quantity
# Create fields with 10,000 of each crop
cornField = Field(10000, Corn)
wheatField = Field(10000, Wheat)
# Create irrigators for each field
cornIrrigator = Irrigator(20000)
wheatIrrigator = Irrigator(500)
# Create a combine for harvesting
combine = Combine()
# 90 days ~3 months of growth
for i in range(90):
# Low chance of rain
if randint(0, 100) > 95:
print("It rained")
cornField.rain()
wheatField.rain()
# Always run the irrigators. Since they are never
# refilled they will quickly run out
cornIrrigator.irrigate(cornField)
wheatIrrigator.irrigate(wheatField)
# Gather the crops - DONE
earsOfCorn = combine.harvest(cornField)
headsOfWheat = combine.harvest(wheatField)
# Print the result - DONE
print("Grew", earsOfCorn, "ears of corn")
print("and", headsOfWheat, "heads of wheat")
But for some reason it takes around 2 to 3 minutes for the code to run. I believe there is a problem with the latter code posted. If anyone has a solution, lemme know!
A more efficient design would be to not model every single plant as a separate instance. As it stands you perform the exact same operation on each plant of a field. Just give the Field a size attribute and a crop attribute thus modelling just that one plant per field and multiply any size related outputs by size.
Something along the lines of this:
class Field():
def rain(self):
self.crop.water()
def __init__(self, size, crop):
self.size = size
self.crop = crop()
class Combine():
def harvest(self, field):
quantity = field.crop.harvest() * field.size
return quantity

Categories

Resources