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