diff --git a/brasilio/settings.py b/brasilio/settings.py
index 8d90c16c..1ba74564 100644
--- a/brasilio/settings.py
+++ b/brasilio/settings.py
@@ -16,6 +16,7 @@
BASE_DIR = root()
DEBUG = env("DEBUG")
PRODUCTION = env("PRODUCTION", bool)
+APOIASE_PROJECT_ID = env("APOIASE_PROJECT_ID")
SECRET_KEY = env("SECRET_KEY")
FERNET_KEY = env("FERNET_KEY")
@@ -168,7 +169,10 @@
"rest_framework.throttling.AnonRateThrottle",
"rest_framework.throttling.UserRateThrottle",
],
- "DEFAULT_THROTTLE_RATES": {"anon": THROTTLING_RATE, "user": THROTTLING_RATE,},
+ "DEFAULT_THROTTLE_RATES": {
+ "anon": THROTTLING_RATE,
+ "user": THROTTLING_RATE,
+ },
}
)
@@ -238,7 +242,12 @@ def get_neo4j_config_dict(neo4j_uri):
}
# django-rq config
-RQ_QUEUES = {"default": {"URL": REDIS_URL, "DEFAULT_TIMEOUT": 500,}}
+RQ_QUEUES = {
+ "default": {
+ "URL": REDIS_URL,
+ "DEFAULT_TIMEOUT": 500,
+ }
+}
RQ = {
"DEFAULT_RESULT_TTL": 60 * 60 * 24, # 24-hours
}
@@ -263,7 +272,9 @@ def get_neo4j_config_dict(neo4j_uri):
# Sentry config
SENTRY_DSN = env("SENTRY_DSN")
sentry_sdk.init(
- SENTRY_DSN, integrations=[DjangoIntegration(), RqIntegration()], send_default_pii=True,
+ SENTRY_DSN,
+ integrations=[DjangoIntegration(), RqIntegration()],
+ send_default_pii=True,
)
diff --git a/core/templates/base.html b/core/templates/base.html
index f00b4c1c..472d3697 100644
--- a/core/templates/base.html
+++ b/core/templates/base.html
@@ -116,8 +116,9 @@
Links
diff --git a/core/templates/donors.html b/core/templates/donors.html
new file mode 100644
index 00000000..b1fd7216
--- /dev/null
+++ b/core/templates/donors.html
@@ -0,0 +1,78 @@
+{% extends 'base.html' %}
+{% load static %}
+{% block title %}Apoiadores - Brasil.IO{% endblock %}
+{% load thumbnail %}
+{% load endswith %}
+{% block head %}
+{{ block.super }}
+
+
+
+{% endblock %}
+
+{% block content %}
+
+
Doadores
+
+
+
+
+ A lista abaixo é de pessoas que apoiam nossa campanha pela plataforma Apoia.se de
+ financiamento coletivo.
+ Colabore você também clicando aqui.
+
+
+
+ {% for donor in donors %}
+
+
+
+ {% if donor.image|endswith:"?d=mp" %}
+
+ {% else %}
+ {% thumbnail donor.image "170x170" as im %}
+
+ {% endthumbnail %}
+ {% endif %}
+
+
{{ donor.name }}
+
+ {% endfor %}
+
+
+
+ {% if donors.has_next %}
+
+ {% endif %}
+
+
+
+
+{% endblock %}
\ No newline at end of file
diff --git a/core/templatetags/endswith.py b/core/templatetags/endswith.py
new file mode 100644
index 00000000..e4cdf636
--- /dev/null
+++ b/core/templatetags/endswith.py
@@ -0,0 +1,10 @@
+from django import template
+from django.template.defaultfilters import stringfilter
+
+register = template.Library()
+
+
+@register.filter(name="endswith")
+@stringfilter
+def endswith(value, suffix):
+ return value.endswith(suffix)
diff --git a/core/urls.py b/core/urls.py
index dd76064c..bd05cf7e 100644
--- a/core/urls.py
+++ b/core/urls.py
@@ -33,6 +33,7 @@ def limited_dataset_detail(request, slug, tablename):
path("manifesto/", views.manifesto, name="manifesto"),
path("colabore/", views.collaborate, name="collaborate"),
path("doe/", views.donate, name="donate"),
+ path("apoiadores/", views.donors, name="donors"),
path("contribuidores/", views.contributors, name="contributors"),
# Dataset-specific pages (specials)
path("especiais/", views_special.index, name="specials"),
diff --git a/core/util.py b/core/util.py
index 62c70ac3..d54b9c78 100644
--- a/core/util.py
+++ b/core/util.py
@@ -6,6 +6,7 @@
import django.db.models.fields
from cachetools import TTLCache, cached
+from django.conf import settings
USER_AGENT = "brasil.io-backend"
@@ -160,6 +161,11 @@ def get_apoiase_donors(campain_id):
return donors
+@cached(cache=TTLCache(maxsize=100, ttl=24 * 3600))
+def get_cached_apoiase_donors():
+ return get_apoiase_donors(settings.APOIASE_PROJECT_ID)
+
+
def ratelimit_key(group, request):
ip = request.META.get("HTTP_CF_CONNECTING_IP", "").strip()
if not ip:
diff --git a/core/views.py b/core/views.py
index ea668412..92020622 100644
--- a/core/views.py
+++ b/core/views.py
@@ -13,7 +13,7 @@
from core.forms import ContactForm, DatasetSearchForm
from core.models import Dataset, Table
from core.templatetags.utils import obfuscate
-from core.util import cached_http_get_json
+from core.util import cached_http_get_json, get_cached_apoiase_donors
from utils.file_info import human_readable_size
@@ -105,7 +105,12 @@ def dataset_detail(request, slug, tablename=""):
if not tablename:
tablename = dataset.get_default_table().name
- return redirect(reverse("core:dataset-table-detail", kwargs={"slug": slug, "tablename": tablename},))
+ return redirect(
+ reverse(
+ "core:dataset-table-detail",
+ kwargs={"slug": slug, "tablename": tablename},
+ )
+ )
try:
allow_hidden = request.user.is_superuser
@@ -144,10 +149,7 @@ def dataset_detail(request, slug, tablename=""):
if not any([query, search_query]) or not user_agent or block_agent:
# User trying to download a CSV without custom filters or invalid
# user-agent specified.
- context = {
- "html_code_snippet": "400-csv-without-filters.html",
- "download_url": table.version.download_url,
- }
+ context = {"html_content": "400-csv-without-filters.html", "download_url": table.version.download_url}
return render(request, "404.html", context, status=400)
if all_data.count() > settings.CSV_EXPORT_MAX_ROWS:
@@ -159,7 +161,8 @@ def dataset_detail(request, slug, tablename=""):
writer = csv.writer(pseudo_buffer, dialect=csv.excel)
csv_rows = queryset_to_csv(all_data, fields)
response = StreamingHttpResponse(
- (writer.writerow(row) for row in csv_rows), content_type="text/csv;charset=UTF-8",
+ (writer.writerow(row) for row in csv_rows),
+ content_type="text/csv;charset=UTF-8",
)
response["Content-Disposition"] = 'attachment; filename="{}"'.format(filename)
response.encoding = "UTF-8"
@@ -231,3 +234,10 @@ def dataset_tables_files_detail(request, slug):
"file_list": dataset.tables_files + [sha512sums_file],
}
return render(request, "tables_files_list.html", context)
+
+
+def donors(request):
+ page = request.GET.get("page", 1)
+ paginator = Paginator(get_cached_apoiase_donors(), 25)
+ data = paginator.page(page)
+ return render(request, "donors.html", {"donors": data})
\ No newline at end of file
diff --git a/env.example b/env.example
index 285708fc..f27ec71f 100644
--- a/env.example
+++ b/env.example
@@ -16,6 +16,7 @@ DATA_URL="https://docs.google.com/spreadsheets/d/1-hw07Q7PBGlz2QjOifkwM3T8406Oqs
DEBUG=True
DEBUG_SQL=True
PRODUCTION=False
+APOIASE_PROJECT_ID=5ab97be3c3f083c623a26742
SECRET_KEY=012345678901234567890123456789
EMAIL_BACKEND=django.core.mail.backends.smtp.EmailBackend
EMAIL_HOST=localhost
diff --git a/static/img/defaultimgavatar.jpg b/static/img/defaultimgavatar.jpg
new file mode 100644
index 00000000..0356f911
Binary files /dev/null and b/static/img/defaultimgavatar.jpg differ
diff --git a/static/js/infinite.js b/static/js/infinite.js
new file mode 100644
index 00000000..eec5dacb
--- /dev/null
+++ b/static/js/infinite.js
@@ -0,0 +1,84 @@
+/*!
+Waypoints Infinite Scroll Shortcut - 4.0.0
+Copyright © 2011-2015 Caleb Troughton
+Licensed under the MIT license.
+https://github.com/imakewebthings/waypoints/blob/master/licenses.txt
+*/
+(function() {
+ 'use strict'
+
+ var $ = window.jQuery
+ var Waypoint = window.Waypoint
+
+ /* http://imakewebthings.com/waypoints/shortcuts/infinite-scroll */
+ function Infinite(options) {
+ this.options = $.extend({}, Infinite.defaults, options)
+ this.container = this.options.element
+ if (this.options.container !== 'auto') {
+ this.container = this.options.container
+ }
+ this.$container = $(this.container)
+ this.$more = $(this.options.more)
+
+ if (this.$more.length) {
+ this.setupHandler()
+ this.waypoint = new Waypoint(this.options)
+ }
+ }
+
+ /* Private */
+ Infinite.prototype.setupHandler = function() {
+ this.options.handler = $.proxy(function() {
+ this.options.onBeforePageLoad()
+ this.destroy()
+ this.$container.addClass(this.options.loadingClass)
+
+ $.get($(this.options.more).attr('href'), $.proxy(function(data) {
+ var $data = $($.parseHTML(data))
+ var $newMore = $data.find(this.options.more)
+
+ var $items = $data.find(this.options.items)
+ if (!$items.length) {
+ $items = $data.filter(this.options.items)
+ }
+
+ this.$container.append($items)
+ this.$container.removeClass(this.options.loadingClass)
+
+ if (!$newMore.length) {
+ $newMore = $data.filter(this.options.more)
+ }
+ if ($newMore.length) {
+ this.$more.replaceWith($newMore)
+ this.$more = $newMore
+ this.waypoint = new Waypoint(this.options)
+ }
+ else {
+ this.$more.remove()
+ }
+
+ this.options.onAfterPageLoad($items)
+ }, this))
+ }, this)
+ }
+
+ /* Public */
+ Infinite.prototype.destroy = function() {
+ if (this.waypoint) {
+ this.waypoint.destroy()
+ }
+ }
+
+ Infinite.defaults = {
+ container: 'auto',
+ items: '.infinite-item',
+ more: '.infinite-more-link',
+ offset: 'bottom-in-view',
+ loadingClass: 'infinite-loading',
+ onBeforePageLoad: $.noop,
+ onAfterPageLoad: $.noop
+ }
+
+ Waypoint.Infinite = Infinite
+}())
+;
\ No newline at end of file
diff --git a/static/js/noframework.waypoints.min.js b/static/js/noframework.waypoints.min.js
new file mode 100644
index 00000000..2ce1f916
--- /dev/null
+++ b/static/js/noframework.waypoints.min.js
@@ -0,0 +1,7 @@
+/*!
+Waypoints - 4.0.0
+Copyright © 2011-2015 Caleb Troughton
+Licensed under the MIT license.
+https://github.com/imakewebthings/waypoints/blob/master/licenses.txt
+*/
+!function(){"use strict";function t(n){if(!n)throw new Error("No options passed to Waypoint constructor");if(!n.element)throw new Error("No element option passed to Waypoint constructor");if(!n.handler)throw new Error("No handler option passed to Waypoint constructor");this.key="waypoint-"+e,this.options=t.Adapter.extend({},t.defaults,n),this.element=this.options.element,this.adapter=new t.Adapter(this.element),this.callback=n.handler,this.axis=this.options.horizontal?"horizontal":"vertical",this.enabled=this.options.enabled,this.triggerPoint=null,this.group=t.Group.findOrCreate({name:this.options.group,axis:this.axis}),this.context=t.Context.findOrCreateByElement(this.options.context),t.offsetAliases[this.options.offset]&&(this.options.offset=t.offsetAliases[this.options.offset]),this.group.add(this),this.context.add(this),i[this.key]=this,e+=1}var e=0,i={};t.prototype.queueTrigger=function(t){this.group.queueTrigger(this,t)},t.prototype.trigger=function(t){this.enabled&&this.callback&&this.callback.apply(this,t)},t.prototype.destroy=function(){this.context.remove(this),this.group.remove(this),delete i[this.key]},t.prototype.disable=function(){return this.enabled=!1,this},t.prototype.enable=function(){return this.context.refresh(),this.enabled=!0,this},t.prototype.next=function(){return this.group.next(this)},t.prototype.previous=function(){return this.group.previous(this)},t.invokeAll=function(t){var e=[];for(var n in i)e.push(i[n]);for(var o=0,r=e.length;r>o;o++)e[o][t]()},t.destroyAll=function(){t.invokeAll("destroy")},t.disableAll=function(){t.invokeAll("disable")},t.enableAll=function(){t.invokeAll("enable")},t.refreshAll=function(){t.Context.refreshAll()},t.viewportHeight=function(){return window.innerHeight||document.documentElement.clientHeight},t.viewportWidth=function(){return document.documentElement.clientWidth},t.adapters=[],t.defaults={context:window,continuous:!0,enabled:!0,group:"default",horizontal:!1,offset:0},t.offsetAliases={"bottom-in-view":function(){return this.context.innerHeight()-this.adapter.outerHeight()},"right-in-view":function(){return this.context.innerWidth()-this.adapter.outerWidth()}},window.Waypoint=t}(),function(){"use strict";function t(t){window.setTimeout(t,1e3/60)}function e(t){this.element=t,this.Adapter=o.Adapter,this.adapter=new this.Adapter(t),this.key="waypoint-context-"+i,this.didScroll=!1,this.didResize=!1,this.oldScroll={x:this.adapter.scrollLeft(),y:this.adapter.scrollTop()},this.waypoints={vertical:{},horizontal:{}},t.waypointContextKey=this.key,n[t.waypointContextKey]=this,i+=1,this.createThrottledScrollHandler(),this.createThrottledResizeHandler()}var i=0,n={},o=window.Waypoint,r=window.onload;e.prototype.add=function(t){var e=t.options.horizontal?"horizontal":"vertical";this.waypoints[e][t.key]=t,this.refresh()},e.prototype.checkEmpty=function(){var t=this.Adapter.isEmptyObject(this.waypoints.horizontal),e=this.Adapter.isEmptyObject(this.waypoints.vertical);t&&e&&(this.adapter.off(".waypoints"),delete n[this.key])},e.prototype.createThrottledResizeHandler=function(){function t(){e.handleResize(),e.didResize=!1}var e=this;this.adapter.on("resize.waypoints",function(){e.didResize||(e.didResize=!0,o.requestAnimationFrame(t))})},e.prototype.createThrottledScrollHandler=function(){function t(){e.handleScroll(),e.didScroll=!1}var e=this;this.adapter.on("scroll.waypoints",function(){(!e.didScroll||o.isTouch)&&(e.didScroll=!0,o.requestAnimationFrame(t))})},e.prototype.handleResize=function(){o.Context.refreshAll()},e.prototype.handleScroll=function(){var t={},e={horizontal:{newScroll:this.adapter.scrollLeft(),oldScroll:this.oldScroll.x,forward:"right",backward:"left"},vertical:{newScroll:this.adapter.scrollTop(),oldScroll:this.oldScroll.y,forward:"down",backward:"up"}};for(var i in e){var n=e[i],o=n.newScroll>n.oldScroll,r=o?n.forward:n.backward;for(var s in this.waypoints[i]){var l=this.waypoints[i][s],a=n.oldScroll=l.triggerPoint,p=a&&h,u=!a&&!h;(p||u)&&(l.queueTrigger(r),t[l.group.id]=l.group)}}for(var c in t)t[c].flushTriggers();this.oldScroll={x:e.horizontal.newScroll,y:e.vertical.newScroll}},e.prototype.innerHeight=function(){return this.element==this.element.window?o.viewportHeight():this.adapter.innerHeight()},e.prototype.remove=function(t){delete this.waypoints[t.axis][t.key],this.checkEmpty()},e.prototype.innerWidth=function(){return this.element==this.element.window?o.viewportWidth():this.adapter.innerWidth()},e.prototype.destroy=function(){var t=[];for(var e in this.waypoints)for(var i in this.waypoints[e])t.push(this.waypoints[e][i]);for(var n=0,o=t.length;o>n;n++)t[n].destroy()},e.prototype.refresh=function(){var t,e=this.element==this.element.window,i=e?void 0:this.adapter.offset(),n={};this.handleScroll(),t={horizontal:{contextOffset:e?0:i.left,contextScroll:e?0:this.oldScroll.x,contextDimension:this.innerWidth(),oldScroll:this.oldScroll.x,forward:"right",backward:"left",offsetProp:"left"},vertical:{contextOffset:e?0:i.top,contextScroll:e?0:this.oldScroll.y,contextDimension:this.innerHeight(),oldScroll:this.oldScroll.y,forward:"down",backward:"up",offsetProp:"top"}};for(var r in t){var s=t[r];for(var l in this.waypoints[r]){var a,h,p,u,c,f=this.waypoints[r][l],d=f.options.offset,y=f.triggerPoint,g=0,w=null==y;f.element!==f.element.window&&(g=f.adapter.offset()[s.offsetProp]),"function"==typeof d?d=d.apply(f):"string"==typeof d&&(d=parseFloat(d),f.options.offset.indexOf("%")>-1&&(d=Math.ceil(s.contextDimension*d/100))),a=s.contextScroll-s.contextOffset,f.triggerPoint=g+a-d,h=y=s.oldScroll,u=h&&p,c=!h&&!p,!w&&u?(f.queueTrigger(s.backward),n[f.group.id]=f.group):!w&&c?(f.queueTrigger(s.forward),n[f.group.id]=f.group):w&&s.oldScroll>=f.triggerPoint&&(f.queueTrigger(s.forward),n[f.group.id]=f.group)}}return o.requestAnimationFrame(function(){for(var t in n)n[t].flushTriggers()}),this},e.findOrCreateByElement=function(t){return e.findByElement(t)||new e(t)},e.refreshAll=function(){for(var t in n)n[t].refresh()},e.findByElement=function(t){return n[t.waypointContextKey]},window.onload=function(){r&&r(),e.refreshAll()},o.requestAnimationFrame=function(e){var i=window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||t;i.call(window,e)},o.Context=e}(),function(){"use strict";function t(t,e){return t.triggerPoint-e.triggerPoint}function e(t,e){return e.triggerPoint-t.triggerPoint}function i(t){this.name=t.name,this.axis=t.axis,this.id=this.name+"-"+this.axis,this.waypoints=[],this.clearTriggerQueues(),n[this.axis][this.name]=this}var n={vertical:{},horizontal:{}},o=window.Waypoint;i.prototype.add=function(t){this.waypoints.push(t)},i.prototype.clearTriggerQueues=function(){this.triggerQueues={up:[],down:[],left:[],right:[]}},i.prototype.flushTriggers=function(){for(var i in this.triggerQueues){var n=this.triggerQueues[i],o="up"===i||"left"===i;n.sort(o?e:t);for(var r=0,s=n.length;s>r;r+=1){var l=n[r];(l.options.continuous||r===n.length-1)&&l.trigger([i])}}this.clearTriggerQueues()},i.prototype.next=function(e){this.waypoints.sort(t);var i=o.Adapter.inArray(e,this.waypoints),n=i===this.waypoints.length-1;return n?null:this.waypoints[i+1]},i.prototype.previous=function(e){this.waypoints.sort(t);var i=o.Adapter.inArray(e,this.waypoints);return i?this.waypoints[i-1]:null},i.prototype.queueTrigger=function(t,e){this.triggerQueues[e].push(t)},i.prototype.remove=function(t){var e=o.Adapter.inArray(t,this.waypoints);e>-1&&this.waypoints.splice(e,1)},i.prototype.first=function(){return this.waypoints[0]},i.prototype.last=function(){return this.waypoints[this.waypoints.length-1]},i.findOrCreate=function(t){return n[t.axis][t.name]||new i(t)},o.Group=i}(),function(){"use strict";function t(t){return t===t.window}function e(e){return t(e)?e:e.defaultView}function i(t){this.element=t,this.handlers={}}var n=window.Waypoint;i.prototype.innerHeight=function(){var e=t(this.element);return e?this.element.innerHeight:this.element.clientHeight},i.prototype.innerWidth=function(){var e=t(this.element);return e?this.element.innerWidth:this.element.clientWidth},i.prototype.off=function(t,e){function i(t,e,i){for(var n=0,o=e.length-1;o>n;n++){var r=e[n];i&&i!==r||t.removeEventListener(r)}}var n=t.split("."),o=n[0],r=n[1],s=this.element;if(r&&this.handlers[r]&&o)i(s,this.handlers[r][o],e),this.handlers[r][o]=[];else if(o)for(var l in this.handlers)i(s,this.handlers[l][o]||[],e),this.handlers[l][o]=[];else if(r&&this.handlers[r]){for(var a in this.handlers[r])i(s,this.handlers[r][a],e);this.handlers[r]={}}},i.prototype.offset=function(){if(!this.element.ownerDocument)return null;var t=this.element.ownerDocument.documentElement,i=e(this.element.ownerDocument),n={top:0,left:0};return this.element.getBoundingClientRect&&(n=this.element.getBoundingClientRect()),{top:n.top+i.pageYOffset-t.clientTop,left:n.left+i.pageXOffset-t.clientLeft}},i.prototype.on=function(t,e){var i=t.split("."),n=i[0],o=i[1]||"__default",r=this.handlers[o]=this.handlers[o]||{},s=r[n]=r[n]||[];s.push(e),this.element.addEventListener(n,e)},i.prototype.outerHeight=function(e){var i,n=this.innerHeight();return e&&!t(this.element)&&(i=window.getComputedStyle(this.element),n+=parseInt(i.marginTop,10),n+=parseInt(i.marginBottom,10)),n},i.prototype.outerWidth=function(e){var i,n=this.innerWidth();return e&&!t(this.element)&&(i=window.getComputedStyle(this.element),n+=parseInt(i.marginLeft,10),n+=parseInt(i.marginRight,10)),n},i.prototype.scrollLeft=function(){var t=e(this.element);return t?t.pageXOffset:this.element.scrollLeft},i.prototype.scrollTop=function(){var t=e(this.element);return t?t.pageYOffset:this.element.scrollTop},i.extend=function(){function t(t,e){if("object"==typeof t&&"object"==typeof e)for(var i in e)e.hasOwnProperty(i)&&(t[i]=e[i]);return t}for(var e=Array.prototype.slice.call(arguments),i=1,n=e.length;n>i;i++)t(e[0],e[i]);return e[0]},i.inArray=function(t,e,i){return null==e?-1:e.indexOf(t,i)},i.isEmptyObject=function(t){for(var e in t)return!1;return!0},n.adapters.push({name:"noframework",Adapter:i}),n.Adapter=i}();
\ No newline at end of file