Django rendering a number as a 5-stars rating - django

I'm building a rating system for my website. It's currently working well but I would like to improve the esthetical aspect of the system. I would like to be able to take the rating from the database and display it as a 5-stars rating. Also, if it's not overly complicated, I would like to be able to click on stars to record the rating in the database, rather than writing a number.
I'm quite new to web development. In particular, I have no experience with javascript (I only did tutorials found on internet), which I think is required to implement the functionality I'm searching for, so please gives me a little example with your response in order to make me able to understand.
For rendering the rating as stars, I have no idea how to do it. For recording the rating as stars, I thought about two solutions :
1) Using django star-ratings but I don't think I have the capabilities required to understand how it works. I already made a post to ask for help and examples about this app but I received no help so I guess I should forget this.
2) Using a form with some appropriate widget to render an IntegerInput as a 5-stars rating.
For the second solution, I already have the code, I now need a widget to replace 'Stars' in the code below but I'm not sure how to do it. Can someone help me ?
models.py
class Avis(models.Model):
note = models.IntegerField()
forms.py
class AvisForm(forms.ModelForm):
class Meta:
model = Avis
fields = ['note']
widgets = {'note': forms.NumberInput(attrs={'class': 'Stars'})}
labels = {'note': 'Note /5'}
hmtl for recording
<form method="post" action="">
{% csrf_token %}
{% for field in form %}
<div class="fieldWrapper form-group">
{{ field.label_tag }}
{{ field }}
</div>
{% endfor %}
<input type="submit" class="btn btn-lg btn-outline-primary btn-block btn-login text-uppercase font-
weight-bold mb-2" value="Envoyer mon avis" />
</form>
hmtl for displaying
{{ avis.note }}
Thanks in advance !
EDIT (my code so far for the ratings storing) :
views.py
def avis(request, id): # view for displaying and storing the form
commande = get_object_or_404(Commande, id=id)
if request.method == "POST":
form = AvisForm(request.POST)
if form.is_valid():
avis = form.save(commit = False)
avis.commande = commande
avis.save()
commande.has_avis = True
commande.save()
if commande.plat.chef.nb_avis==0:
commande.plat.chef.rating = avis.note
else:
commande.plat.chef.rating = (commande.plat.chef.rating*commande.plat.chef.nb_avis + avis.note)/(commande.plat.chef.nb_avis + 1)
commande.plat.chef.nb_avis += 1
commande.plat.chef.save()
messages.success(request, 'Votre avis a été correctement envoyé !')
return redirect(mes_commandes)
else:
form = AvisForm()
return render(request, 'actualites/avis.html', locals())
def avis2(request, id): # view for recording the rating
avis = get_object_or_404(Avis, id=id)
rating = request.POST.get('rating')
avis.note = rating
avis.save()
messages.success(request, 'Votre avis a été correctement envoyé !')
return redirect(mes_commandes)
html
<form method="post" action="">
{% csrf_token %}
{% for field in form %}
<div class="fieldWrapper form-group">
{{ field.label_tag }}
{{ field }}
</div>
{% endfor %}
<input type="submit" class="btn btn-lg btn-outline-primary btn-block btn-login text-uppercase font-weight-bold mb-2" value="Envoyer mon avis" />
</form>
<div class="rating rating2">
★
★
★
★
★
</div>
<script>
$(".rating a").on('click', function(e){
let value = $(this).data('value');
$.ajax({
url: "{% url 'avis2' %}",
type: 'POST',
data: {'rating': value},
success: function (d){
// some processing
}
})
});
</script>
forms.py
class AvisForm(forms.ModelForm):
class Meta:
model = Avis
fields = ['commentaire']
widgets = {'commentaire': forms.Textarea(attrs={'class': 'form-control'})}

I will try to answer your both of the question. For getting the rating, your can render stars and add a JS click event to that.
Code (HTML, CSS) source: https://codepen.io/GeoffreyCrofte/pen/jEkBL
$(".rating a").on('click', function(e){
let value = $(this).data('value');
$.ajax({
url: "some_url",
type: 'POST',
data: {'rating': value},
success: function (d){
// some processing
}
})
});
.rating {
width: 300px;
margin: 0 auto 1em;
font-size: 45px;
overflow:hidden;
}
.rating input {
float: right;
opacity: 0;
position: absolute;
}
.rating a,
.rating label {
float:right;
color: #aaa;
text-decoration: none;
-webkit-transition: color .4s;
-moz-transition: color .4s;
-o-transition: color .4s;
transition: color .4s;
}
.rating label:hover ~ label,
.rating input:focus ~ label,
.rating label:hover,
.rating a:hover,
.rating a:hover ~ a,
.rating a:focus,
.rating a:focus ~ a {
color: orange;
cursor: pointer;
}
.rating2 {
direction: rtl;
}
.rating2 a {
float:none
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<div class="rating rating2">
★
★
★
★
★
</div>
For Rendering the rating, you need to calculate the value first. You need to determine the number of person who rated 5, 3, ... 1 stars. Lets say there are 100's 5 star rating, 70 * 4 star, 50 * 3 star, 30 * 2 and 20 * 1 star rating. So you can determine rating by:
sum of rating / total rating
So it will be (100 * 5 + 70 * 4 + 50 * 3 + 30 * 2 + 20 * 1) / 100 + 70 + 50 + 30 + 20
So the final rating will be: 3.74
To get the width percentage: (3.74 * 100) / 5 = 74.8
Here 5 refers to total number of stars, here I am assuming that rating will be based on the scale of 5.
For rendering you will need different HTML and CSS.
Code source: https://codepen.io/Bluetidepro/pen/GkpEa
.star-ratings-css {
unicode-bidi: bidi-override;
color: #c5c5c5;
font-size: 25px;
height: 25px;
width: 100px;
margin: 0 auto;
position: relative;
padding: 0;
text-shadow: 0px 1px 0 #a2a2a2;
}
.star-ratings-css-top {
color: #e7711b;
padding: 0;
position: absolute;
z-index: 1;
display: block;
top: 0;
left: 0;
overflow: hidden;
}
.star-ratings-css-bottom {
padding: 0;
display: block;
z-index: 0;
}
.star-ratings-sprite {
background: url("https://s3-us-west-2.amazonaws.com/s.cdpn.io/2605/star-rating-sprite.png") repeat-x;
font-size: 0;
height: 21px;
line-height: 0;
overflow: hidden;
text-indent: -999em;
width: 110px;
margin: 0 auto;
}
.star-ratings-sprite-rating {
background: url("https://s3-us-west-2.amazonaws.com/s.cdpn.io/2605/star-rating-sprite.png") repeat-x;
background-position: 0 100%;
float: left;
height: 21px;
display: block;
}
<div class="star-ratings-css">
<div class="star-ratings-css-top" style="width: 74.8%">
<span>★</span><span>★</span><span>★</span><span>★</span><span>★</span></div>
<div class="star-ratings-css-bottom"><span>★</span><span>★</span><span>★</span><span>★</span><span>★</span></div>
</div>
You need to pass width from your view and in the HTML, you need to access it.
<div class="star-ratings-css-top" style="width: {{ width }}%">. I tried these code snippet, working for me and should work for you as well :)

Related

Check how many users answered keeps returning first value

I am trying to see how many users answered each option however it will give me the result of how many people answered the first option. Result object is where all answered are saved to.
Do I need to make a loop in the html? Would this not work as you cant call Result in to html and filter it ? How would I go around doing this. I see the counter is staying to the first value and keeps looping for each option, but I am confused on a fix for this.
HTML
<!DOCTYPE html>
<html>
<head>
<title>Admin Panel</title>
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.6.1/css/all.css" integrity="sha384-gfdkjb5BdAXd+lj+gudLWI+BXq4IuLW5IT+brZEZsLFm++aCMlF1V92rMkPaX4PP" crossorigin="anonymous">
<style>
body,
html {
margin: 0;
padding: 0;
height: 100%;
background: #7abecc !important;
}
.user_card {
width: 350px;
margin-top: auto;
margin-bottom: auto;
background: #74cfbf;
position: relative;
display: flex;
justify-content: center;
flex-direction: column;
padding: 10px;
box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
-webkit-box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
-moz-box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
border-radius: 5px;
}
.form_container {
margin-top: 20px;
}
#form-title{
color: #fff;
}
#questionTitle {
text-align: left;
}
.login_btn {
width: 100%;
background: #33ccff !important;
color: white !important;
}
.login_btn:focus {
box-shadow: none !important;
outline: 0px !important;
}
.login_container {
padding: 0 2rem;
}
.input-group-text {
background: #f7ba5b !important;
color: white !important;
border: 0 !important;
border-radius: 0.25rem 0 0 0.25rem !important;
}
.input_user,
.input_pass:focus {
box-shadow: none !important;
outline: 0px !important;
}
#messages{
background-color: grey;
color: #fff;
padding: 10px;
margin-top: 10px;
}
</style>
</head>
<body>
<div class="container h-100">
<div class="d-flex justify-content-center h-100">
<div class="user_card">
<div>(-- <br/></div>
<div class="d-flex justify-content-center">
<h3 id="form-title">Question Statistics</h3>
</div>
<div class="d-flex justify-content-center form_container">
<form method="POST" action="">
{% csrf_token %}
<div class="input-group mb-3">
<h6 id="questionTitle"> {{question.QuestionText}} </h6>
</div>
<div> <h7> The Amount Of Users Who Answered Were: {{counter}} </h7> </div>
</br>
{% for option in options %}
<label for="{{ option.OptionID }}">{{ option.OptionText }} <br> This Option Was Chosen by: {{counter2}} Users</label> <br>
{% endfor %}
<div class="d-flex justify-content-center mt-3 login_container">
<input class="btn login_btn" type="submit" value="Submit">
</div>
</form>
</div>
{{form.errors}}
{% for message in messages %}
<p id='messages'>{{message}} </p>
{% endfor %}
</div>
</div>
</div>
</div>
</body>
</html>
Model
class Result(models.Model):
ResponseID = models.AutoField(primary_key=True, unique=True)
userID = models.ForeignKey(Account, on_delete=models.CASCADE)
QuestionsID = models.ForeignKey(Question, on_delete=models.CASCADE)
ChosenOptionID = models.ForeignKey(Option, on_delete=models.CASCADE)
def __str__(self):
return str(self.ResponseID)
VIEWS for statstic
def question_statistics(request, id):
question = Question.objects.get(QuestionsID = id)
options = Option.objects.filter(QuestionsID = question)
r = Result.objects.filter(QuestionsID = id)
counter = 0
for x in r:
counter+=1
a = Result.objects.filter(ChosenOptionID = options)
counter2 = 0
for y in options:
a = Result.objects.filter(ChosenOptionID = y)
for x in a:
counter2+=1
current_user = request.user
if current_user.is_admin:
pass
else:
messages.error("User is not admin")
return redirect('dashboardPage')
context = {'question' : question,
'options' : options,
'results' : r,
'counter' : counter,
'counter2' : counter}
return render(request, 'questionStatistic.html', context)
Your 'counter2': counter should be 'counter2': counter2. It's returning the answer to the first question because the variable you're calling is the counter for the first question.

ApexCharts.js Line Chart - Display percent from total in tooltip

I want to display percent instead of an integer.
Now it simply shows the value as integer when hovering on the data point of the chart (the tooltip).
I think I need to set in the formatter formula like this:
let test = value / SUM(all_values)
return test.toFixed(0) + '%'
But, I could not find it in their documentation, the best I found is this one which always gives me 100%, per every data point:
tooltip: {
y: {
formatter: function(value, opts) {
let percent = opts.w.globals.seriesPercent[opts.seriesIndex][opts.dataPointIndex];
return percent.toFixed(0) + '%'
}
}
}
You're right that you'll need to use the tooltip formatter, but the array you're using to get the percentage values isn't correct.
If you log the values opts.w.globals.seriesPercent you'll find that it's all 100's:
[[100, 100, 100, ..., 100]]
(I suspect this might be because it's intended to be used with other chart types.)
You can still get the percentage though, it'll just need to be worked out.
Using your same method to format the tooltip, but getting the values from opts.series instead:
tooltip: {
y: {
formatter: function(value, opts) {
const sum = opts.series[0].reduce((a, b) => a + b, 0);
const percent = (value / sum) * 100;
return percent.toFixed(0) + '%'
},
},
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<!-- Bootstrap CSS -->
<link href="https://cdn.jsdelivr.net/npm/bootstrap#5.1.3/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-1BmE4kWBq78iYhFldvKuhfTAU6auU8tT94WrHftjDbrCEXSU1oBoqyl2QvZ6jIW3" crossorigin="anonymous">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
<script src="app/app.js"></script>
<script>
let apiLabels = [];
let apiData = [];
let currentTimestamp = (Math.floor(Date.now() / 1000)).toString();
//use this URL for daily fetch
//let url = "https://aave-api-v2.aave.com/data/rates-history?reserveId=0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb480xb53c1a33016b2dc2ff3653530bff1848a515c8c5&resolutionInHours=24&from="+currentTimestamp;
let url = "https://aave-api-v2.aave.com/data/rates-history?reserveId=0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb480xb53c1a33016b2dc2ff3653530bff1848a515c8c5&resolutionInHours=24&from=1639813032";
// months list
const monthNames = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
// fetch from api
var xhr = new XMLHttpRequest();
xhr.addEventListener("readystatechange", function () {
if (this.readyState === 4) {
let myData = JSON.parse(this.responseText);
console.log(myData);
for (let i = 0; i < myData.length; i++) {
//let formedDate = (myData[i]['x']['year']+'-'+myData[i]['x']['month']+'-'+myData[i]['x']['date']).toString();
let formedDate = monthNames[myData[i]['x']['month']] + ' ' + myData[i]['x']['date'];
console.log(formedDate);
let formedLiquidData = (myData[i]['liquidityRate_avg'] * 100).toFixed(2);
console.log(formedLiquidData+"%");
apiLabels.push(formedDate);
apiData.push(formedLiquidData);
}
const data = {
labels: apiLabels,
datasets: [
{
label: "USDC",
borderColor: "rgb(180,11,107)",
data: apiData
}
]
};
const config = {
type: "line",
data: data,
options:{
scales: {
y: {
ticks: {
callback: function (value, index, values) {
return value + '%';
}
}
},
},
},
};
const myChart = new Chart(document.getElementById("myChart"), config);
}
});
xhr.open(
"GET",
url
);
xhr.send();
</script>
</div>
<div id="app"></div>
<title>brew</title>
<style>
body {
background-color: white;
}
.center-form {
width: 80%;
margin: auto;
}
#title {
color: teal;
text-align: center;
}
.waitlist {
position: relative;
z-index: 3;
width: 146px;
height: 30px;
margin-top: 60px;
border-style: solid;
border-width: 1px;
border-color: #081852;
background-color: transparent;
box-shadow: -10px 10px 0 0 #081852;
color: #081852;
font-size: 12px;
line-height: 30px;
font-weight: 400;
text-align: center;
letter-spacing: 0.21px;
position: fixed;
right: 3%;
top: -5%;
}
.waitlist-button {
display: inline-block;
padding: 9px 15px;
background-color: #3898EC;
color: white;
border: 0;
line-height: inherit;
text-decoration: none;
cursor: pointer;
border-radius: 0;
}
.box-prices {
padding: 20px;
width: 200px;
height: auto;
color: #000;
/* background-color: #fff; */
border: 4px solid #1c1c53;
border-radius: 2px;
}
.dropbtn {
background-color: navy;
color: white;
padding: 16px;
font-size: 16px;
border: none;
}
.dropdown-content {
display: none;
position: absolute;
background-color: #f1f1f1;
min-width: 160px;
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
z-index: 1;
}
.dropdown-content a {
color: black;
padding: 12px 16px;
text-decoration: none;
display: block;
}
.dropdown-content a:hover {
background-color: #ddd;
}
.dropdown:hover .dropdown-content {
display: block;
}
.dropdown:hover .dropbtn {
background-color: #0b95f1;
}
footer {
display: flex;
justify-content: space-between;
}
.myChart {
width: 50%;
height: 40px;
}
/* Decorative */
a {
text-decoration: none;
color: #555;
}
footer {
background-color: #82cdf8;
padding: 20px 40px;
}
.right {
float: left;
text-align: center;
}
.article {
color: white;
}
</style>
</head>
<body>
<img src="https://uploads-ssl.webflow.com/61768faf04d20bf3487b5a2a/6176a6363e64b8d84e32d0b7_brew-icon-256px-v2.png"
loading="eager" width="55" height="55" alt="Brew" class="img-logo" position="absolute" top="-3%" left="7;">
<!-------------- title------------------------>
<div class="container">
<div class="row">
<div class="col-md-12 mt-2">
<h1 style="color: #1c98">
Stablecoin Deposits on Aave
</h1>
<a data-w-id="14f1dfb2-926f-581f-7faa-3f62171b1db3" href="" class="waitlist waitlist-button">Join the
waitlist</a>
</div>
</div>
</div>
<!------------------------AAVE PRICE ------------>
<div class="container">
<div class="row mt-5">
<div class="col-md-6">
<div class="aave-prices">
<p>AAVE PRICE</p>
<p>$172.8</p>
<p>16%</p>
</div>
</div>
<!------------------------AAVE DESCRIPTION ------------>
<div class="col-md-6">
<h1>What is AAVE ?</h1>
<p>Aave is an open source and non-custodial liquidity protocol for earning
interest on deposits and borrowing assets.</p>
</div>
</div>
<!------------------------AAVe CHAIN ------------>
<div class="row">
<div class="col-md-12 mt-3">
<button class="dropbtn">CHAIN</button>
<div class="dropdown-content">
Polygon
Ethereum
Avalance
</div>
</div>
</div>
</div>
<!------graph----->
<div>
<canvas id="myChart" height="50" weidth="50"></canvas>
</div>
<!-- calculator -->
<div class="container mt-5" style="border: 1px solid black; border-radius: 5px;">
<div class="row">
<div class="col-md-12">
<h1 id="title">How Much You Can Earn ?</h1>
</div>
</div>
<br>
<div class="row">
<div class="col-md-6">
<form>
<div class="form-group mt-2">
<label class="form-group">Amount</label>
<input class="form-control mt-2" type="text" id="principal" placeholder="$">
</div>
<div class="form-group mt-2">
<label class="form-group">Interest rate: 4%</label>
</div>
<div class="form-group mt-2">
<label class="form-group">Compound Freequency: Daily</label>
</div>
<div class="form-group mt-2">
<label class="form-group">Payout Freequency: Monthly</label>
</div>
<button class="btn btn-block btn-info mt-3" type="button" onclick="calculate()">Calculate</button>
</form>
</div>
<div class="col-md-6">
<form style="padding-top: 70px;">
<div class="form-group mt-2">
<label class="form-group">Interest rate: 5%</label>
</div>
<div class="form-group mt-2">
<label class="form-group">Compound Freequency: Daily</label>
</div>
<div class="form-group mt-2">
<label class="form-group">Payout Freequency: Monthly</label>
</div>
</form>
</div>
</div>
<br>
<div class="row">
<div class="col-md-6">
<h5 class="total">Result USD</h5>
<h1 id="USD"></h1>
</div>
<div class="col-md-6">
<h5 class="total">Result USDT</h5>
<h1 id="USDC"></h1>
</div>
</div>
</div>
<!-- calculator end -->
<div>
<canvas id="myChart_subbagain" height="50" weidth="50" top="80%"></canvas>
</div>
<!----footer-->
<div class="col-md-12 mt-5">
<footer>
<div class="col-md-6">
<img src="/dawnload.png" alt="" width="150">
</div>
<div class="col-md-6">
<section class="right">
Join Brew Today
<article>USDC is not a legal tender recognised by US or any other government. Unlike a bank account, your
deposit is not insured. While Brew will make every effort to ensure that your deposit earn the best interest
rates in the most secure way possible, please note that any investment entails risk. Interest Rates are
subject to change anytime as per the market conditions.</article>
</section>
</div>
</footer>
</div>
</body>
<script type="text/javascript">
function calculate() {
var principle = 0;
var interest = 5 / 100;
var numberOfPeriod = 12;
var time = 10;
var CI = 0;
principle = document.getElementById("principal").value;
// CI = ((p * (1 + i) ^ n) - p);
CI = principle * (1 + interest / numberOfPeriod) ^ (numberOfPeriod * time);
document.getElementById("USD").innerHTML = CI;
document.getElementById("USDC").innerHTML = CI;
}
</script>
<!------cal graph-->
</html>

xhtml2pdf - Problem with displaying table rows using django forloop tag

What I am trying to do is to create an invoice pdf file with several table rows. Table rows would be created using for loop in Django. The problem is data inside for loop tag is not visible on the pdf file.
You can check the screenshots below. Django properly renders invoice.html template so the code is valid, but the pdf file contains empty frame without any table rows. To render pdf from html I am using xhtml2pdf.
how django render the invoice.html template
how pdf file looks like
invoice.html
<html>
<head>
{% load static %}
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta charset="UTF-8">
<style>
#font-face {
font-family: Roboto;
src: url('{% static 'fonts/Roboto-Regular.ttf' %}');
}
#font-face {
font-family: Roboto-bold;
src: url('{% static 'fonts/Roboto-Bold.ttf' %}');
font-weight: bold;
}
#page {
size: a4 portrait;
#frame header_frame { /* Static Frame */
-pdf-frame-content: frame_header_left;
left: 50pt; width: 245pt; top: 30pt; height: 150pt;
}
#frame header_frame { /* Static Frame */
-pdf-frame-content: frame_header_right;
left: 300pt; width: 245pt; top: 50pt; height: 150pt;
}
#frame content_frame { /* Content Frame */
-pdf-frame-content: frame_invoice_number;
left: 50pt; width: 512pt; top: 170pt; height: 30pt;
}
#frame col1 {
-pdf-frame-content: frame_col1;
left: 50pt; width: 245pt; top: 220pt; height: 130pt;
}
#frame col2 {
-pdf-frame-content: frame_col2;
left: 300pt; width: 245pt; top: 220pt; height: 130pt;
}
#frame frame_services {
-pdf-frame-content: frame_services;
left: 50pt; width: 512pt; top: 380pt; height: 250pt;
-pdf-frame-border: 1;
}
#frame content_frame {
-pdf-frame-content: frame_summary;
left: 465pt; width: 100pt; top: 590pt; height: 50pt;
}
#frame content_frame {
-pdf-frame-content: frame_vat;
left: 50pt; width: 512pt; top: 590pt; height: 150pt;
}
#frame content_frame {
-pdf-frame-content: frame_signatures_left;
left: 50pt; width: 140pt; top: 725pt; height: 70pt;
}
#frame content_frame {
-pdf-frame-content: frame_signatures_right;
left: 400pt; width: 140pt; top: 725pt; height: 70pt;
left: 400pt; width: 140pt; top: 725pt; height: 70pt;
}
#frame content_frame {
-pdf-frame-content: footer_content;
left: 50pt; width: 512pt; top: 775pt; height: 50pt;
}
}
body {
background-color: white;
font-family: "Roboto", sans-serif;
}
.right{
font-size: 10px;
text-align: right;
}
.left{
text-align: left;
}
.invoice-number {
font-size: 18px;
font-family: "Roboto-bold", sans-serif;
}
.col-titles {
font-size: 16px;
text-decoration: underline;
font-family: "Roboto-bold", sans-serif;
}
.footer {
font-size: 8px;
text-align: center;
}
p{
font-size: 10px;
line-height: 0;
}
table {
border-bottom: 1px solid #ddd;
text-align: center;
}
td, td {
border-bottom: 1px solid #ddd;
vertical-align: middle;
}
.summary{
border-bottom: 1px solid #ddd;
}
.signatures {
border-top: 1px solid black;
font-size: 8px;
text-align: center;
}
th {
height: 36px;
}
td {
height: 25px;
}
</style>
</head>
<body>
<div>
<div>
<div id="frame_header_left" class="left">
<img src="{% static 'invoices/logo.png' %}" alt="logo" width="150" height="112">
</div>
<div id="frame_header_right" class="right">
<p>Miejsce wystawienia: Żabno</p>
<p>Data badania: {{ invoice.data_badania }}</p>
<p>Data wystawienia: {{ invoice.data_wystawienia_faktury }}</p>
</div>
</div>
<div id="frame_invoice_number">
<div class="invoice-number">
<h2>Faktura nr: {{ invoice.numer }}</h2>
</div>
</div>
<div id="frame_col1">
<p class="col-titles">Sprzedawca</p>
<div>
<p>MEDIKAP</p>
<p>ul. Plac Grunwaldzki 15B, 33-240 Żabno</p>
<p>NIP: 999999999</p>
<p>REGON: 9999999</p>
<p>Bank: ING Bank Śląski</p>
<p>Nr konta: 12 1234 1234 1243 1243 214 1244</p>
</div>
</div>
<div id="frame_col2">
<p class="col-titles">Nabywca</p>
<div>
<p> {{ invoice.firma}} </p>
<p> ul. {{ invoice.firma.ulica }} </p>
<p> {{ invoice.firma.kod_pocztowy }} {{ invoice.firma.miasto}}</p>
<p> NIP: {{ invoice.firma.nip }}</p>
<p> REGON: {{ invoice.firma.regon }}</p>
<p> forma płatności: {{ invoice.get_forma_platnosci_display}}</p>
</div>
</div>
<div id="frame_services">
<table>
<tr>
<th style="width: 50px;"> # </th>
<th style="width: 600px;"> Nazwa usługi</th>
<th style="width: 100px;"> Ilość</th>
<th style="width: 100px;"> Rabat[%]</th>
<th style="width: 100px;"> Cena usługi</th>
<th style="width: 100px;"> Wartość</th>
<th style="width: 100px;"> Wartość z rabatem:</th>
</tr>
{% for service in services_items %}
<tr>
<td> test </td>
<td> test </td>
</tr>
{% endfor %}
</table>
</div>
<div id="frame_summary" class="summary">
<p> : {{ service.get_total_value }} PLN</p>
<p> Wartość z uwzględnieniem {{ invoice.rabat}}% rabatu: {{ discounted_value|floatformat:"-2" }} PLN</p>
</div>
<div id="frame_vat">
<p> Podstawa zwolnienia z VAT: </p>
<p> Zwolnienie ze względu na zakres wykonywanych czynności (art. 43 ust.1) pkt 19 Ustawy o VAT</p>
</div>
<div id="frame_signatures_left">
<p class="signatures"> podpis osoby upoważnionej do odbioru faktury</p>
</div>
<div id="frame_signatures_right">
<p class="signatures"> podpis osoby upoważnionej do wystawienia faktury</p>
</div>
<div id="footer_content" >
<p class="footer">MEDIKAP Maria K.</p>
<p class="footer">Plac Grunwaldzki 15B, 33-240 Żabno</p>
<p class="footer">e-mail: gabinet.medikap#gmail.com tel: 539 993 332</p>
<p class="footer">NIP: 9930212793 REGON: 852441210</p>
</div>
</div>
</body>
</html>
views.py/DetailsInvoice
class DetailsInvoice(generic.View):
template_name = 'invoices/invoice_detail.html'
form_class = DetailInvoiceForm
success_url = reverse_lazy("invoices:list")
def get(self, request, invoice_id):
current_invoice = get_object_or_404(Invoice, id=invoice_id)
form = self.form_class(instance=current_invoice)
request.session['invoice_id'] = current_invoice.id
services = Service.objects.all()
all_service_items = ServiceItem.objects.all().filter(faktura = current_invoice).order_by('usluga')
context = {
'invoice': current_invoice,
'form' : form,
'services' : services,
'services_items' : all_service_items,
}
return render(request, self.template_name, context)
def post(self, request, invoice_id):
current_invoice = get_object_or_404(Invoice, id=invoice_id)
form = self.form_class(request.POST, instance=current_invoice)
all_service_items = ServiceItem.objects.all().filter(faktura=current_invoice)
context = {
'invoice' : current_invoice,
}
pdf = render_to_pdf('invoices/invoice.html', context)
services_assigned_to_invoice = current_invoice.uslugi.all()
if 'update-data' in request.POST and form.is_valid():
for service in all_service_items:
service_item = get_object_or_404(ServiceItem, id=service.id)
quantity_input = request.POST.get('quantity-' + str(service.id))
discount_input = request.POST.get('discount-' + str(service.id))
service_item.ilosc = int(quantity_input)
service_item.rabat = int(discount_input)
service_item.save()
form.save()
for service_item in all_service_items:
if service_item.usluga not in services_assigned_to_invoice:
service_item.delete()
for single_service in services_assigned_to_invoice:
new_service_item, created = ServiceItem.objects.get_or_create(usluga=single_service, faktura=current_invoice)
messages.success(request, 'Pomyślnie zaktualizowane dane')
return HttpResponseRedirect(self.request.META.get('HTTP_REFERER'))
if 'view-pdf' in request.POST:
return HttpResponse(pdf, content_type='application/pdf')
if 'download-pdf' in request.POST:
response = HttpResponse(pdf, content_type='application/pdf')
filename = f"Faktura {current_invoice.numer}.pdf"
content = "attachment; filename={}".format(filename)
response['Content-Disposition'] = content
return response
else:
return redirect('invoices:list')
rendering function
def render_to_pdf(template_src, context_dict={}):
template = get_template(template_src)
html = template.render(context_dict)
result = BytesIO()
pdf = pisa.pisaDocument(BytesIO(html.encode("utf-8")), result, link_callback=link_callback)
if not pdf.err:
return HttpResponse(result.getvalue(), content_type='application/pdf')
return None
You are not passing the same context variables to your PDF as the ones you are passing to your HTML template. To your HTML template you are passing:
context = {
'invoice': current_invoice,
'form' : form,
'services' : services,
'services_items' : all_service_items,
}
While to your PDF you are only passing:
context = {
'invoice' : current_invoice,
}
services_items is the one your PDF template seems to be missing. So because for service in services_items is an empty/non-existent list in your PDF template, it doesn't render any rows. In the future you can check this by adding an {% empty %} section to your for loop:
{% for service in services_items %}
<tr>
<td> test </td>
<td> test </td>
</tr>
{% empty %}
No items!
{% endfor %}

How do I get the value of CharField() during instansiation?

I want to pass model fields values as arguments to another field.
trademark_class.get_default() method is close to what I want, but I do not want solely the default value.
I am also unable to use __dict__.['trademark_class'] in this case as I need a 'this' reference to the class and do not know the proper way to achieve that for this case.
class Form_33_Model(models.Model):
trademark_number = models.CharField(default='default number', max_length=30)
trademark_class = models.CharField(default='default class', max_length=30)
trademark_date = models.DateField(default=datetime.date.today)
html = models.TextField(default=Form33StringClass(trademark_class=trademark_class.get_default(),
trademark_number=trademark_number.get_default()).form_33_string)
Instead of the default value only, I would like to get either the actual value of the Charfield or its default value.
So something like : trademark_class.get_current_value_or_default() would be ideal for what I want.
Below is my Form33StringClass.py
import datetime
class Form33StringClass():
def __init__(self, trademark_number, trademark_class):
self.trademark_number = trademark_number
self.trademark_class = trademark_class
self.day = datetime.datetime.now().strftime("%A") + " " + datetime.datetime.now().strftime("%d")
self.month = datetime.datetime.now().strftime("%B")
self.year = datetime.datetime.now().strftime("%Y")
self.form_33_string = """
<!DOCTYPE html>
<html lang="en" dir="ltr">
<head>
<meta charset="utf-8">
<title>Trade Marks Act</title>
<style>
.letter_margin {
margin: auto 25%;
line-height: 25px;
}
header{
text-align: center;
}
.intro {
text-align: center;
font-style: italic;
}
.request{
}
.office_address{
width: 50%;
margin-left: 10%;
}
.address_row{
display: flex;
align-items: baseline;
}
.re-address_row{
/* float: right; */
display: flex;
justify-content: flex-end;
}
.re_address{
width: 60%;
}
.to_registrar{
display: flex;
align-items: baseline;
}
.registrar_paragraph {
margin-left: 10%;
}
.underlined{
text-decoration: underline;
}
.trademark_class{
}
.trademark_number{
}
</style>
</head>
<body>
<table>
<tr>
<header>
<h1>TRADE MARKS ACT</h1>
<h2>FORM 33</h2>
</header>
</tr>
<tr >
<div class="intro letter_margin">
<p>
Form of Request to the Registrar by a Registered Proprietor or a Registered User of a Trade Mark, or a person about to be so registered, to enter, alter, or substitute an Address for service as part of his Registration (Regulations 15, 82. 86 and 102)
</p>
</div>
</tr>
<tr>
<div class="request letter_margin">
<p >
Request is made by: <br><br>
A company incorporated in <br>
Carrying on business as manufacturers and merchants at:<br><br><br>
who is about to be registered as/who is the Registered Proprietor of Trade Mark(s)<br> No <span class="underlined trademark_number"> """+ trademark_number +"""</span> registered in Class(es) <span class="underlined trademark_class"> """+trademark_class+"""</span> <br>
for the inclusion/addition/alteration/substitution of an address for service in Nigeria in or to <br> the entry thereof so that the address for service in Nigeria may read:
</p>
</div>
</tr>
....
</body>
</html>
"""
when a new Form_33_Model.objects.create(trademark_class, trademark_number) is created :
{
trademark_class : "class foo",
trademark_number : "number bar",
html : "<html>...
<tag> class foo </tag>
<tag> number bar </tag>
...
</html>"
}
I still can't really understand what you are trying to do. But perhaps SerializerMethodField is what you want?
class Form33ModelSerializer(serializers.ModelSerializer):
html = SerializerMethodField()
class Meta:
model = Form_33_Model
def get_html(self, obj):
return obj.html or obj._meta.get_field('trademark_class').get_default()
I solved my problem in the end by overriding the save() method in the model.
class Form_33_Model(models.Model):
trademark_number = models.CharField(default='default number', max_length=30)
trademark_class = models.CharField(default='default class', max_length=30)
trademark_date = models.DateField(default=datetime.date.today)
html = models.TextField(default=Form33StringClass(
trademark_class=trademark_class.get_default(),
trademark_number=trademark_number.get_default()
).form_33_string)
def save(self, *args, **kwargs):
self.html = Form33StringClass(
trademark_class=self.trademark_class,
trademark_number=self.trademark_number
).form_33_string
super(Form_33_Model, self).save(*args, **kwargs)

python code error for movie project "TypeError: this constructor takes no arguments"

I am working on a code project for Udacity and I get these 2 errors I dont get I have attached the pictures. One error is with parenthesis and when I remove the parenthesis I get an error on the code saying invalid syntax.
import webbrowser
class Movie():
valid_ratings = ["G", "R", "PG-13", "R"]
def _init_(self, movie_title, movie_storyline, poster_image,
trailer_youtube):
self.title = movie_title
self.storyline = movie_storyline
self.poster_image_url = poster_image
self.trailer_youtube_url = trailer_youtube
def show_trailer(self):
webbrowser.open(self.trailer_youtube_url)
import fresh_tomatoes
import media
toy_story = media.Movie("Toy Story",
"A story of a boy and his toys come to life",
"http://upload.wikimedia.org/wikipedia/en/1/13/Toy_Story.jpg",
"http://www.youtube.com/watch?v=vwyZH85NQC4")
The_devils_double = media.Movie("The_devils_double",
"The story of the son of sadam hussain's body double",
"https://upload.wikimedia.org/wikipedia/en/4/4c/The_Devil%27s_Double.jpg",
"https://www.youtube.com/watch?v=2-MsGEWFiYg",)
Movie_300 = media.Movie("300",
"300 spartans vs an amry of persians",
"https://upload.wikimedia.org/wikipedia/en/5/5c/300poster.jpg",
"https://www.youtube.com/watch?v=wDiUG52ZyHQ")
Ratatouille = media.Movie("Ratatouille",
"A rat is a chef in paris",
"https://upload.wikimedia.org/wikipedia/en/5/50/RatatouillePoster.jpg",
"https://www.youtube.com/watch?v=c3sBBRxDAqk")
Star_Wars_Episode_III = media.Movie("Star_Wars_Episode_III",
"Akin Skywalker goes dark",
"https://upload.wikimedia.org/wikipedia/en/9/93/Star_Wars_Episode_III_Revenge_of_the_Sith_poster.jpg",
"https://www.youtube.com/watch?v=5UnjrG_N8hU")
Office_Space = media.Movie("Office_Space ",
"A movie about how work sucks",
"https://upload.wikimedia.org/wikipedia/en/8/8e/Office_space_poster.jpg",
"https://www.youtube.com/watch?v=kwQziVIzDeg")
#movies = [toy_story, The_devils_double, 300, Ratatouille, Star_Wars_Episode_III , Office_Space ]
fresh_tomatoes.open_movies_page(movies)
print(media.Movie.valid_ratings)
import webbrowser
import os
import re
# Styles and scripting for the page
main_page_head = '''
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Fresh Tomatoes!</title>
<!-- Bootstrap 3 -->
<link rel="stylesheet" href="https://netdna.bootstrapcdn.com/bootstrap/3.1.0/css/bootstrap.min.css">
<link rel="stylesheet" href="https://netdna.bootstrapcdn.com/bootstrap/3.1.0/css/bootstrap-theme.min.css">
<script src="http://code.jquery.com/jquery-1.10.1.min.js"></script>
<script src="https://netdna.bootstrapcdn.com/bootstrap/3.1.0/js/bootstrap.min.js"></script>
<style type="text/css" media="screen">
body {
padding-top: 80px;
}
#trailer .modal-dialog {
margin-top: 200px;
width: 640px;
height: 480px;
}
.hanging-close {
position: absolute;
top: -12px;
right: -12px;
z-index: 9001;
}
#trailer-video {
width: 100%;
height: 100%;
}
.movie-tile {
margin-bottom: 20px;
padding-top: 20px;
}
.movie-tile:hover {
background-color: #EEE;
cursor: pointer;
}
.scale-media {
padding-bottom: 56.25%;
position: relative;
}
.scale-media iframe {
border: none;
height: 100%;
position: absolute;
width: 100%;
left: 0;
top: 0;
background-color: white;
}
</style>
<script type="text/javascript" charset="utf-8">
// Pause the video when the modal is closed
$(document).on('click', '.hanging-close, .modal-backdrop, .modal', function (event) {
// Remove the src so the player itself gets removed, as this is the only
// reliable way to ensure the video stops playing in IE
$("#trailer-video-container").empty();
});
// Start playing the video whenever the trailer modal is opened
$(document).on('click', '.movie-tile', function (event) {
var trailerYouTubeId = $(this).attr('data-trailer-youtube-id')
var sourceUrl = 'http://www.youtube.com/embed/' + trailerYouTubeId + '?autoplay=1&html5=1';
$("#trailer-video-container").empty().append($("<iframe></iframe>", {
'id': 'trailer-video',
'type': 'text-html',
'src': sourceUrl,
'frameborder': 0
}));
});
// Animate in the movies when the page loads
$(document).ready(function () {
$('.movie-tile').hide().first().show("fast", function showNext() {
$(this).next("div").show("fast", showNext);
});
});
</script>
</head>
'''
# The main page layout and title bar
main_page_content = '''
<body>
<!-- Trailer Video Modal -->
<div class="modal" id="trailer">
<div class="modal-dialog">
<div class="modal-content">
<a href="#" class="hanging-close" data-dismiss="modal" aria-hidden="true">
<img src="https://lh5.ggpht.com/v4-628SilF0HtHuHdu5EzxD7WRqOrrTIDi_MhEG6_qkNtUK5Wg7KPkofp_VJoF7RS2LhxwEFCO1ICHZlc-o_=s0#w=24&h=24"/>
</a>
<div class="scale-media" id="trailer-video-container">
</div>
</div>
</div>
</div>
<!-- Main Page Content -->
<div class="container">
<div class="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="#">Fresh Tomatoes Movie Trailers</a>
</div>
</div>
</div>
</div>
<div class="container">
{movie_tiles}
</div>
</body>
</html>
'''
# A single movie entry html template
movie_tile_content = '''
<div class="col-md-6 col-lg-4 movie-tile text-center" data-trailer-youtube-id="{trailer_youtube_id}" data-toggle="modal" data-target="#trailer">
<img src="{poster_image_url}" width="220" height="342">
<h2>{movie_title}</h2>
</div>
'''
def create_movie_tiles_content(movies):
# The HTML content for this section of the page
content = ''
for movie in movies:
# Extract the youtube ID from the url
youtube_id_match = re.search(
r'(?<=v=)[^&#]+', movie.trailer_youtube_url)
youtube_id_match = youtube_id_match or re.search(
r'(?<=be/)[^&#]+', movie.trailer_youtube_url)
trailer_youtube_id = (youtube_id_match.group(0) if youtube_id_match
else None)
# Append the tile for the movie with its content filled in
content += movie_tile_content.format(
movie_title=movie.title,
poster_image_url=movie.poster_image_url,
trailer_youtube_id=trailer_youtube_id
)
return content
def open_movies_page(movies):
# Create or overwrite the output file
output_file = open('fresh_tomatoes.html', 'w')
# Replace the movie tiles placeholder generated content
rendered_content = main_page_content.format(
movie_tiles=create_movie_tiles_content(movies))
# Output the file
output_file.write(main_page_head + rendered_content)
output_file.close()
# open the output file in the browser (in a new tab, if possible)
url = os.path.abspath(output_file.name)
webbrowser.open('file://' + url, new=2)
Your init method has single underscores. It needs to have double underscores before and after "init".
def __init__(self, movie_title, movie_storyline, poster_image, trailer_youtube):
Also, you're missing a comma when you create the Movie_300 object. It should look like this (with a comma after the wikimedia link):
Movie_300 = Movie("300",
"300 spartans vs an amry of persians",
"https://upload.wikimedia.org/wikipedia/en/5/5c/300poster.jpg",
"https://www.youtube.com/watch?v=wDiUG52ZyHQ")
As an aside, the convention to name variables in Python is to use all lowercase.