Comparing dates in liquid - templates

I'm using Liquid with Jekyll to post dates on my band's website (http://longislandsound.com.au)
What I want is to automatically hide old dates, so I don't have to go in and delete them again. I think the best way to do it would be to compare the post date to the current date and only display the post if the date is in the future, but I can't figure out how to do this.
Here is the current code:
<ul id="dates">
{% for post in site.posts reversed %}
<a href="{{post.link}}">
<li>
<div class="date">
<span class="day">{{post.date | date: "%d"}}</span><br />
<span class="month">{{post.date | date: "%b"}}</span>
<span class="week">{{post.date | date: "%a"}}</span>
</div>
<div class="details">
<span class="venue">{{post.venue}}</span><br />
<span class="town">{{post.town}}</span>
</div>
</li>
</a>
{% endfor %}
</ul>
I've tried some if statements, but I can't figure out how to compare the post date to the current date.
Can anyone help?

Based on date-math-manipulation-in-liquid-template-filter and get-todays-date-in-jekyll-with-liquid-markup, you should be able to use a combination of {{'now'}} or {{site.time}} and the hard to find unix timestamp date filter | date: '%s'
{% capture nowunix %}{{'now' | date: '%s'}}{% endcapture %}
{% capture posttime %}{{post.date | date: '%s'}}{% endcapture %}
{% if posttime < nowunix %}
...show posts...
Captured numbers may act as strings, not numbers, and can be type cast back to numbers using the following hack:
{% assign nowunix = nowunix | plus: 0 %}

Although this code works:
{% capture nowunix %}{{'now' | date: '%s'}}{% endcapture %}
{% capture posttime %}{{post.date | date: '%s'}}{% endcapture %}
{% if posttime < nowunix %} ...show posts...
It only is executed during build. If you want your website to truly update automatically, you should let javascript do the hiding.
Start with this Liquid:
{% for item in site.events %}
<div future-date="{{ item.date | date: '%Y%m%d' }}">...</div>
{% endfor %}
And add this javascript:
function getCompareDate() {
var d = new Date(),
month = '' + (d.getMonth() + 1),
day = '' + d.getDate(),
year = d.getFullYear();
if (month.length < 2) month = '0' + month;
if (day.length < 2) day = '0' + day;
return [year, month, day].join('');
}
$('[future-date]').each(function() {
if($(this).attr('future-date') < getCompareDate()) $(this).hide();
});
The solution was found here: http://jekyllcodex.org/without-plugin/future-dates/
UPDATE (2018-02-19):
CloudCannon now has scheduled builds where you can simply specify to build your project once a day. If you use CloudCannon, I recommend the answer of the user [here].

Related

Is it possible to add days to the date in the template?

I have today's date in my template, which is in a loop. I want to add a counter to the cycle so that in the template I don't have today's date, but date + 1 day, date + 2 days, and so on.
{% for day_week_list in set_day_week %}
<div class="card mb-2">
<div class="card-header">
{{ day_week_list }} {{ day_data|date:'d.m' }}
</div>
<div class="card-body">
No, that's not possible. To add days to a date in HTML, you need to use a programming language such as JavaScript.

Translate Django template to ReactJS

I've created some templates in Django, and I want to translate them to work in ReactJS. The thing that I'm struggling with the most is the regroup function. There's a number of ways I've thought of approaching it, but I think the easiest is probably to do it within the component. All I've managed to do is map the items which generates the entire list, but I need to have the items dynamically grouped before iterating each group.
In React I'd like to be able to apply a command like items.groupBy('start_time').groupBy('event_name').map(item =>
The output should be 'start_time', 'event_name', and then the rest of the data within each event group. Each 'start_time' will contain multiple events. I'd like to keep the code as concise as possible.
This is the Django template:
{% if event_list %}
<div id="accordian" class="panel-group" role="tablist" aria-multiselectable="true">
{% regroup event_list by start_time as start_time_list %}
{% for start_time in start_time_list %}
<div class="row start-time">
<div class="col">
<h6 class="text-muted">{{ start_time.grouper|date:' d-m-Y H:i' }}</h6>
</div>
</div>
{% regroup start_time.list by event as events_list_by_start_time %}
{% for event_name in events_list_by_start_time %}
<div class="panel panel-default">
<div class="card-header" id="{{ event_name.grouper|slugify }}">
<div class="panel-heading">
<h5 class="panel-title">
<a data-toggle="collapse" data-parent="#accordian" href="#collapse-{{ event_name.grouper|slugify }}">
{{ event_name.grouper|title }}
</a>
</h5>
</div>
</div>
<div id="collapse-{{ event_name.grouper|slugify }}" class="panel-collapse collapse in">
<div class="panel-body">
{% for item in event_name.list %}
# continue iterating the items in the list
This is the render method from the React component:
render() {
const { error, isLoaded, items, groups } = this.state;
if (error) {
return <div>Error: {error.message}</div>;
} else if (!isLoaded) {
return <div>Loading...</div>;
} else {
return (
<div>
{items.map(item => (
<h4 key={item.id}>{item.event}</h4>
)
)}
</div>
);
}
}
}
If you are setting up a React front end, it would be more sensible not to mix django templates with the react front end components. Instead set up a Django/DRF backend api that will feed your react components with JSON data.
To translate this template from django to react, you just have to reimplement the regroup template tag as a javascript function. For pretty much any django template tag, you can easily find some javascript library that does the same. This is not included in React.js, but instead you can import utilities from libraries such as underscore or moment.js etc.
This is the sample django template code from the example in the documentation for the {% regroup %} template tag.
{% regroup cities by country as country_list %}
<ul>
{% for country in country_list %}
<li>{{ country.grouper }}
<ul>
{% for city in country.list %}
<li>{{ city.name }}: {{ city.population }}</li>
{% endfor %}
</ul>
</li>
{% endfor %}
</ul>
Here's how you could do it with react.js
// React ( or javascript ) doesn't come with a groupby function built in.
// But we can write our own. You'll also find this kind of stuff in lots
// of javascript toolsets, such as lowdash, Ramda.js etc.
const groupBy = (key, data) => {
const groups = {}
data.forEach(entry => {
const {[key]: groupkey, ...props} = entry
const group = groups[groupkey] = groups[groupkey] || []
group.push(props)
})
return groups
}
// I'll define a dumb component function for each nested level of the list.
// You can also write a big render function with everything included,
// but I find this much more readable – and reusable.
const City = ({ name, population }) => (
<li> {name}: {population} </li>
)
const Country = ({ country, cities }) => (
<li>
{country}
<ul>{cities.map(props => <City key={props.name} {...props} />)}</ul>
</li>
)
const CityList = ({ cities }) => {
const groups = Object.entries(groupBy("country", cities))
return (
<ul>
{groups.map(([country, cities]) => (
<Country key={country} country={country} cities={cities} />
))}
</ul>
)
}
// We'll use the exact same data from the django docs example.
const data = [
{ name: "Mumbai", population: "19,000,000", country: "India" },
{ name: "Calcutta", population: "15,000,000", country: "India" },
{ name: "New York", population: "20,000,000", country: "USA" },
{ name: "Chicago", population: "7,000,000", country: "USA" },
{ name: "Tokyo", population: "33,000,000", country: "Japan" }
]
ReactDOM.render(<CityList cities={data} />, document.getElementById("app"))
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<main id=app></main>
If you run the snippet above, you should get the exact same output as what you find in the original django example.
<ul>
<li>India
<ul>
<li>Mumbai: 19,000,000</li>
<li>Calcutta: 15,000,000</li>
</ul>
</li>
<li>USA
<ul>
<li>New York: 20,000,000</li>
<li>Chicago: 7,000,000</li>
</ul>
</li>
<li>Japan
<ul>
<li>Tokyo: 33,000,000</li>
</ul>
</li>
</ul>

Passing Order Value to conversion code in Success page

I'm implementing Google Dynamic Remarketing code for 'success' page and I would like to pass order data to tracking script -
Is it doable? I don't see any Order variables in documentation. Would shopify order variables work?
Script I need to populate and pass -
<script type="text/javascript">
var google_tag_params = {
ecomm_prodid: [PRODUCT IDs],
ecomm_pagetype: 'purchase',
ecomm_totalvalue: ORDER TOTAL
};
</script>
Script I use in Shopify Order Success -
<script type="text/javascript">
var google_tag_params = {
{% if order.line_items.size > 1 %}
ecomm_prodid: [{% for line_item in order.line_items %}'{{line_item.product.product.id }}'{% if forloop.last != true %},{% endif %}{% endfor %}],
{% elsif order.line_items.size == 1 %}
ecomm_prodid: [{% for line_item in order.line_items %}'{{line_item.product.product.id }}'{% endfor %}],
{% else %}
ecomm_prodid: '',
{% endif %}
ecomm_pagetype: 'purchase',
ecomm_totalvalue: {{ cart.total }}
};
</script>
Please advise.
Thanks
Sorry, but at the moment there's no way to pass any kind of order details to the Success page - unfortunately this type of code isn't going to work.
I had a similar challenge, given the constraints in Big Cartel, I dropped in a tracking script for conversions on the cart if the cart isn't empty. It was better than nothing.
{%if page.permalink == 'cart' %}
{% if cart.items != blank %}
<script></script>
{%endif%}
{%endif%}
if you are looking for Product id and total value in confirmation page of shopify
you can use below code.
<!-- google dynamic remarketing tag for thank-you page -->
<script type="text/javascript">
var google_tag_params = {
ecomm_prodid: [{% for line_item in order.line_items %} 'shopify_US_{{line_item.product.id}}_{{line_item.variant.id}}', {% endfor %}],
ecomm_pagetype: 'purchase',
ecomm_totalvalue: {{ subtotal_price | money_without_currency | remove: ","}}
};
</script>
To get complete instruction follow this link http://www.notesonclick.com/blog/dynamic-remarketing-code-for-shopify/

Django: Can I map the output of a template variable to another string?

I am working with Django's generic views, specifically django.views.generic.date_based.archive_month.
This views sets the template context to include date_list which contains a list of the relevant python datetime.datetime objects. (See https://docs.djangoproject.com/en/1.4/ref/generic-views/#django-views-generic-date-based-archive-month)
This my simple template to use this view:
<html>
<head>
<title>Entries index</title>
</head>
<body>
<h1>Entries index by month: {{ month|date:"F" }}</h1>
<h2>Day:</h2>
{% for datetime_object in date_list %}
<ul>{{ datetime_object.day }}</ul>
{% endfor %}
</body>
</html>
My problem is that although I can get the numerical month, say 3 for march, my urls are set up to use the three letter representation, i.e. mar for march, so that <a href= block isn't pointing to the right url.
How can I set up a mapping to map each of the month's numerical value to their three letter representation?
You can use the built-in date template tag with the b format character.
Applied example below:
<ul>
<!-- whitespace added below solely for readability
I do not recommend adding such whitespace in your code -->
<a href="/weblog/{{ year }}/{{ month|date:"b" }}/{{ datetime_object.day }}/">
{{ datetime_object.day }}
</a>
</ul>

Customize count list

I have this code I'm using to generate a list of records categorized into year, make, series, body style, and color for vehicles. I'd like to customize this further this way:
for the year, I want to have only up to 2004 being individual...the rest will fall under other i.e. 2009, 2008, 2007, 2006, 2005, 2004, Other.
for the make, I want to display the six makes with the highest popularity...there's a field in the model I'm using to assign the popularity of a make with a value of primary (highest), secondary or tertiary. The rest will fall under Other.
For the body style and color, I want to have the items having less than 3 records falling under Other.
My code is as below:
year_count = vehicle_query.order_by(
'-common_vehicle__year__year').values('common_vehicle__year__year').
annotate(count=Count('id'))
make_count = vehicle_query.order_by(
'common_vehicle__series__model__manufacturer__manufacturer').
values('common_vehicle__series__model__manufacturer__manufacturer').
annotate(count=Count('id'))
style_count = vehicle_query.order_by(
'common_vehicle__body_style__style').values
('common_vehicle__body_style__style').annotate(count=Count('id'))
colour_count = vehicle_query.order_by(
'exterior_colour__exterior_colour').values(
'exterior_colour__exterior_colour').annotate(count=Count('id'))
The bulk of what you're asking would probably better be handled outside of Django and instead by client-side javascript. To be clear, you could have portions handled by Django, but it would be cleaner not doing so. There are benefits to doing it this way:
Your Django template code stays cleaner
It will degrade nicely
You can later update the interface (change the javascript) and not have to worry about breaking the Django template
To handle this you could simply make a script that when given a <ul> tag (and maybe some arguments) will render that list in the format you're asking about.
Here's a simple example using jQuery. For this example, I'm going to wrap the functionality in a using a jQuery plugin pattern.
Say your django template outputs the following...
<ul>
<li>Chevy</li>
<li>Mazda</li>
<li>Honda</li>
<li>Ford</li>
<li>BMW</li>
</ul>
jquery.showmorelist.js
(function($) {
$.fn.ShowMoreList = function(visibleItemCount) {
// Wrap parent element
var parent = $(this).wrap('<div class="show-more-list"></div>').parent();
var ul = $(this);
// Enumerate children and hide extras
var counter = 0;
$(this).children().filter('li').each(function(){
counter += 1;
if (counter > visibleItemCount) {
$(this).hide();
$(this).addClass('hidden');
}
});
// Add link and bind click
var link = $('> Show More').click(function(){
$(ul).children().filter('.hidden').show();
});
$(parent).append(link);
}
})(jQuery);
page.html
<script type="text/javascript" src="jquery.showmorelist.js"></script>
<script type="text/javascript">
// On page load...
$(function() {
$('ul').ShowMoreList(4); // Shows only the first 4 items
});
</script>
This is a rather simple example, and it won't switch the "Show More" to "Hide More" but you should be able to figure that out from the context.
I managed to get a solution, so I thought it'd be good to update the answer here:
In the head section I have this:
<script type="text/javascript" src="{{ MEDIA_URL }}share/jquery/jquery.min.js"></SCRIPT>
<script type="text/javascript">
(function($) {
$(document).ready(function() {
//hide the additional content under "Display More"
$("div.additional_content").hide();
$("a.more").click(function () {
//show or hide the additional content
$(this).siblings("div.additional_content").toggle();
//change the attributes and text value of the link toggle
if($(this).text() == "Display Less"){
$(this).removeClass("less");
$(this).addClass("more");
$(this).html("Display More");
}else{
$(this).removeClass("more");
$(this).addClass("less");
$(this).html("Display Less");
}
return false;
});
});
})(jQuery);
Then wherever I want to reduce the number of available options I have this:
<div class="module_wrap">
<div class="module"> {% if year_count %} <strong>{% trans "Year" %}</strong> <br />
{% for item in year_count|slice:":6" %}
<ul>
<li> {{ item.common_vehicle__year__year }} ({{ item.count }}) {% if request.session.chosen_year %} <img src="{{ MEDIA_URL }}img/undo.gif" border="0" alt="Remove Year Filter" title="Remove Year Filter" /> {% endif %} </li>
</ul>
{% endfor %}
<div class="additional_content"> {% for item in year_count|slice:"6:" %}
<ul>
<li> {{ item.common_vehicle__year__year }} ({{ item.count }})</li>
</ul>
{% endfor %} </div>
{% if year_count|slice:"6:" %}Display More<br />
{% endif %} <br />
</div>
</div>
{% endif %}