├── budget_app ├── __init__.py ├── admin.py ├── management │ ├── __init__.py │ └── commands │ │ ├── __init__.py │ │ ├── clear_cache_local.py │ │ ├── load_stats.py │ │ ├── load_glossary.py │ │ ├── load_budget_data.py │ │ ├── load_entities.py │ │ ├── load_payments.py │ │ ├── load_execution.py │ │ └── load_budget.py ├── static │ ├── assets │ │ ├── big-intro.png │ │ ├── icomoon.svg │ │ ├── cc.png │ │ ├── social.png │ │ ├── waves.png │ │ ├── favicon.png │ │ ├── icomoon.eot │ │ ├── icomoon.ttf │ │ ├── icomoon.woff │ │ ├── select2.png │ │ ├── select2x2.png │ │ ├── sepAcces.gif │ │ ├── flag-waves.png │ │ ├── option-tax.png │ │ ├── pignatelli.jpg │ │ ├── slick │ │ │ ├── sort.png │ │ │ ├── collapse.png │ │ │ ├── expand.png │ │ │ ├── sort-asc.png │ │ │ └── sort-desc.png │ │ ├── wait16trans.gif │ │ ├── cornerAccess.gif │ │ ├── entity--chart.png │ │ ├── logoAragonEs.gif │ │ ├── logoAragonEs.png │ │ ├── option-global.png │ │ ├── option-terms.png │ │ ├── reuse inverso2.png │ │ ├── unionEuropea.gif │ │ ├── busget-stream-1.png │ │ ├── busget-stream-2.png │ │ ├── busget-stream-3.png │ │ ├── jslider │ │ │ ├── jslider.png │ │ │ ├── jslider.blue.png │ │ │ ├── jslider.round.png │ │ │ ├── jslider.plastic.png │ │ │ ├── jslider.presus.png │ │ │ └── jslider.round.plastic.png │ │ ├── logoFooterAragon.gif │ │ ├── option-payments.png │ │ ├── option-policies.png │ │ ├── select2-spinner.gif │ │ ├── select2 │ │ │ ├── select2.png │ │ │ ├── select2x2.png │ │ │ └── select2-spinner.gif │ │ ├── sticky-note-text.png │ │ ├── logoAragonOpenData.png │ │ ├── logo-gobierno-aragon.png │ │ ├── logoAragonOpenBudget.png │ │ ├── busget-stream-question.png │ │ ├── comparison__entity--chart.png │ │ ├── busget-stream-question-hover.png │ │ ├── bootstrap │ │ │ ├── glyphicons-halflings.png │ │ │ └── glyphicons-halflings-white.png │ │ └── jquery-ui │ │ │ ├── ui-icons_222222_256x240.png │ │ │ ├── ui-icons_2e83ff_256x240.png │ │ │ ├── ui-icons_454545_256x240.png │ │ │ ├── ui-icons_888888_256x240.png │ │ │ ├── ui-icons_cd0a0a_256x240.png │ │ │ ├── ui-bg_flat_0_aaaaaa_40x100.png │ │ │ ├── ui-bg_flat_75_ffffff_40x100.png │ │ │ ├── ui-bg_glass_55_fbf9ee_1x400.png │ │ │ ├── ui-bg_glass_65_ffffff_1x400.png │ │ │ ├── ui-bg_glass_75_dadada_1x400.png │ │ │ ├── ui-bg_glass_75_e6e6e6_1x400.png │ │ │ ├── ui-bg_glass_75_ffffff_1x400.png │ │ │ ├── ui-bg_inset-soft_95_fef1ec_1x100.png │ │ │ └── ui-bg_highlight-soft_75_cccccc_1x100.png │ ├── stylesheets │ │ ├── variables.less │ │ ├── jslider │ │ │ ├── jslider.blue.less │ │ │ ├── jslider.plastic.less │ │ │ ├── jslider.round.less │ │ │ ├── jslider.round.plastic.less │ │ │ ├── jslider.presus.less │ │ │ └── jslider.less │ │ ├── bootstrap │ │ │ ├── layouts.less │ │ │ ├── component-animations.less │ │ │ ├── utilities.less │ │ │ ├── breadcrumbs.less │ │ │ ├── grid.less │ │ │ ├── hero-unit.less │ │ │ ├── responsive-768px-979px.less │ │ │ ├── wells.less │ │ │ ├── responsive-1200px-min.less │ │ │ ├── close.less │ │ │ ├── pager.less │ │ │ ├── accordion.less │ │ │ ├── scaffolding.less │ │ │ ├── responsive.less │ │ │ ├── responsive-utilities.less │ │ │ ├── thumbnails.less │ │ │ ├── alerts.less │ │ │ ├── code.less │ │ │ ├── pagination.less │ │ │ ├── bootstrap.less │ │ │ ├── tooltip.less │ │ │ ├── labels-badges.less │ │ │ ├── modals.less │ │ │ └── carousel.less │ │ ├── vis │ │ │ ├── infobox.less │ │ │ ├── policies.index.less │ │ │ ├── budget.index.less │ │ │ └── map.less │ │ ├── fonts.less │ │ ├── main.less │ │ ├── print.less │ │ ├── patches.less │ │ ├── colors.less │ │ ├── reset.less │ │ ├── alerts.less │ │ ├── grid.less │ │ └── home.less │ └── javascripts │ │ ├── leaflet │ │ └── images │ │ │ ├── layers.png │ │ │ ├── layers-2x.png │ │ │ ├── marker-icon.png │ │ │ ├── marker-icon-2x.png │ │ │ └── marker-shadow.png │ │ ├── utils.js │ │ ├── comparator.js │ │ ├── select2_locale_es.js │ │ ├── jquery.cookie.min.js │ │ ├── css_browser_selector.js │ │ ├── jslider │ │ ├── tmpl.js │ │ └── jquery.dependClass-0.1.js │ │ ├── icons-lte-ie7.js │ │ ├── grid_controls.js │ │ ├── bootstrap │ │ ├── bootstrap-transition.js │ │ ├── bootstrap-alert.js │ │ └── bootstrap-button.js │ │ ├── vis │ │ └── budget-summary.js │ │ └── breakpoints.js ├── views │ ├── reuse.py │ ├── propertiesLoader.py │ ├── terms.py │ ├── tax_receipt.py │ ├── payments.py │ ├── __init__.py │ ├── welcome.py │ ├── budgets.py │ ├── towns.py │ └── counties.py ├── loaders │ ├── __init__.py │ ├── entity_loader.py │ ├── glossary_loader.py │ └── stat_loader.py ├── models │ ├── __init__.py │ ├── funding_category.py │ ├── institutional_category.py │ ├── glossary_term.py │ ├── inflation_stat.py │ ├── entity.py │ ├── economic_category.py │ ├── population_stat.py │ ├── functional_category.py │ ├── budget_breakdown.py │ └── payment.py └── tests.py ├── project ├── __init__.py ├── middleware.py └── wsgi.py ├── django_jasmine ├── __init__.py ├── templates │ └── jasmine │ │ ├── index.html │ │ └── base.html ├── static │ ├── jasmine-latest │ │ ├── jasmine_favicon.png │ │ └── MIT.LICENSE │ ├── jasmine-1.1.0.rc1 │ │ ├── jasmine_favicon.png │ │ ├── MIT.LICENSE │ │ └── jasmine.css │ └── jasmine-1.0.1 │ │ ├── MIT.LICENSE │ │ └── jasmine.css ├── urls.py └── views.py ├── theme-aragon ├── __init__.py ├── static │ ├── .do_not_remove │ ├── sitemaps │ │ ├── Gobierno.xml │ │ ├── Home.xml │ │ └── VisionGlobal.xml │ ├── robots.txt │ └── sitemap.xml ├── loaders │ └── __init__.py ├── .gitignore ├── data │ ├── comunidad │ │ ├── 2014 │ │ │ ├── gastos.csv │ │ │ ├── ingresos.csv │ │ │ ├── estructura_economica.csv │ │ │ ├── estructura_funcional.csv │ │ │ ├── estructura_organica.csv │ │ │ └── estructura_financiacion.csv │ │ └── 2021 │ │ │ ├── gastos.csv │ │ │ ├── ingresos.csv │ │ │ ├── estructura_economica.csv │ │ │ ├── estructura_funcional.csv │ │ │ ├── estructura_organica.csv │ │ │ └── estructura_financiacion.csv │ ├── inflacion.csv │ └── inspect.rb ├── templates │ └── shared │ │ └── meta.html ├── settings.py └── scripts │ └── carga_nuevos_datos.sh ├── templates ├── payments │ └── payments_intro.html ├── shared │ ├── fonts.html │ ├── data_controllers_select.html │ ├── analytics.html │ ├── meta.html │ ├── payment_tab.html │ ├── entity_select.html │ ├── breakdown_tab.html │ ├── chromeframe.html │ ├── pagination.html │ ├── social_sharing.html │ ├── footer.html │ ├── data_controllers.html │ ├── data_sources.html │ └── header.html ├── 500.html ├── entities │ └── totals.html ├── robots.txt ├── terms │ └── index.html └── budgets │ ├── budgets_intro.html │ ├── budgets_indicators.html │ └── budgets_update_indicators.html ├── docs ├── Memoria Fase 1.pdf ├── Memoria Fase 2.pdf └── Documentación Técnica.pdf ├── etls └── presupuestos_locales │ ├── project │ └── AOD_PREL.zip │ └── doc │ ├── [AOD]_Manual de ejecución_v1.0.0.docx │ ├── [AOD]_Extracción y carga de presupuestos locales_v1.0.0.docx │ └── carga_datos_presupuestos.md ├── requirements.txt ├── .gitignore ├── manage.py ├── tests ├── files.json └── spec │ ├── tax_calculator_spec.js │ └── grid_formatters_spec.js ├── local_settings.py-example ├── properties.py └── CHANGELOG.md /budget_app/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /budget_app/admin.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /project/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /django_jasmine/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme-aragon/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /budget_app/management/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /budget_app/static/assets/big-intro.png: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /budget_app/management/commands/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /theme-aragon/static/.do_not_remove: -------------------------------------------------------------------------------- 1 | .do_not_remove -------------------------------------------------------------------------------- /budget_app/static/stylesheets/variables.less: -------------------------------------------------------------------------------- 1 | /* Variables */ -------------------------------------------------------------------------------- /budget_app/static/assets/icomoon.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /django_jasmine/templates/jasmine/index.html: -------------------------------------------------------------------------------- 1 | {% extends "jasmine/base.html" %} 2 | -------------------------------------------------------------------------------- /theme-aragon/loaders/__init__.py: -------------------------------------------------------------------------------- 1 | from aragon_budget_loader import AragonBudgetLoader 2 | -------------------------------------------------------------------------------- /theme-aragon/.gitignore: -------------------------------------------------------------------------------- 1 | data/municipio 2 | data/comarca 3 | data/comunidad 4 | data_summary.py -------------------------------------------------------------------------------- /templates/payments/payments_intro.html: -------------------------------------------------------------------------------- 1 |

{{ _('Gastos del año vigente') }}

-------------------------------------------------------------------------------- /docs/Memoria Fase 1.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/docs/Memoria Fase 1.pdf -------------------------------------------------------------------------------- /docs/Memoria Fase 2.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/docs/Memoria Fase 2.pdf -------------------------------------------------------------------------------- /budget_app/static/assets/cc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/cc.png -------------------------------------------------------------------------------- /docs/Documentación Técnica.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/docs/Documentación Técnica.pdf -------------------------------------------------------------------------------- /budget_app/static/assets/social.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/social.png -------------------------------------------------------------------------------- /budget_app/static/assets/waves.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/waves.png -------------------------------------------------------------------------------- /budget_app/static/assets/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/favicon.png -------------------------------------------------------------------------------- /budget_app/static/assets/icomoon.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/icomoon.eot -------------------------------------------------------------------------------- /budget_app/static/assets/icomoon.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/icomoon.ttf -------------------------------------------------------------------------------- /budget_app/static/assets/icomoon.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/icomoon.woff -------------------------------------------------------------------------------- /budget_app/static/assets/select2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/select2.png -------------------------------------------------------------------------------- /budget_app/static/assets/select2x2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/select2x2.png -------------------------------------------------------------------------------- /budget_app/static/assets/sepAcces.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/sepAcces.gif -------------------------------------------------------------------------------- /budget_app/static/assets/flag-waves.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/flag-waves.png -------------------------------------------------------------------------------- /budget_app/static/assets/option-tax.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/option-tax.png -------------------------------------------------------------------------------- /budget_app/static/assets/pignatelli.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/pignatelli.jpg -------------------------------------------------------------------------------- /budget_app/static/assets/slick/sort.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/slick/sort.png -------------------------------------------------------------------------------- /budget_app/static/assets/wait16trans.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/wait16trans.gif -------------------------------------------------------------------------------- /budget_app/static/assets/cornerAccess.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/cornerAccess.gif -------------------------------------------------------------------------------- /budget_app/static/assets/entity--chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/entity--chart.png -------------------------------------------------------------------------------- /budget_app/static/assets/logoAragonEs.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/logoAragonEs.gif -------------------------------------------------------------------------------- /budget_app/static/assets/logoAragonEs.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/logoAragonEs.png -------------------------------------------------------------------------------- /budget_app/static/assets/option-global.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/option-global.png -------------------------------------------------------------------------------- /budget_app/static/assets/option-terms.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/option-terms.png -------------------------------------------------------------------------------- /budget_app/static/assets/reuse inverso2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/reuse inverso2.png -------------------------------------------------------------------------------- /budget_app/static/assets/slick/collapse.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/slick/collapse.png -------------------------------------------------------------------------------- /budget_app/static/assets/slick/expand.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/slick/expand.png -------------------------------------------------------------------------------- /budget_app/static/assets/slick/sort-asc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/slick/sort-asc.png -------------------------------------------------------------------------------- /budget_app/static/assets/unionEuropea.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/unionEuropea.gif -------------------------------------------------------------------------------- /theme-aragon/data/comunidad/2014/gastos.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/theme-aragon/data/comunidad/2014/gastos.csv -------------------------------------------------------------------------------- /theme-aragon/data/comunidad/2021/gastos.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/theme-aragon/data/comunidad/2021/gastos.csv -------------------------------------------------------------------------------- /budget_app/static/assets/busget-stream-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/busget-stream-1.png -------------------------------------------------------------------------------- /budget_app/static/assets/busget-stream-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/busget-stream-2.png -------------------------------------------------------------------------------- /budget_app/static/assets/busget-stream-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/busget-stream-3.png -------------------------------------------------------------------------------- /budget_app/static/assets/jslider/jslider.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/jslider/jslider.png -------------------------------------------------------------------------------- /budget_app/static/assets/logoFooterAragon.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/logoFooterAragon.gif -------------------------------------------------------------------------------- /budget_app/static/assets/option-payments.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/option-payments.png -------------------------------------------------------------------------------- /budget_app/static/assets/option-policies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/option-policies.png -------------------------------------------------------------------------------- /budget_app/static/assets/select2-spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/select2-spinner.gif -------------------------------------------------------------------------------- /budget_app/static/assets/select2/select2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/select2/select2.png -------------------------------------------------------------------------------- /budget_app/static/assets/slick/sort-desc.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/slick/sort-desc.png -------------------------------------------------------------------------------- /budget_app/static/assets/sticky-note-text.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/sticky-note-text.png -------------------------------------------------------------------------------- /theme-aragon/data/comunidad/2014/ingresos.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/theme-aragon/data/comunidad/2014/ingresos.csv -------------------------------------------------------------------------------- /theme-aragon/data/comunidad/2021/ingresos.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/theme-aragon/data/comunidad/2021/ingresos.csv -------------------------------------------------------------------------------- /budget_app/static/assets/logoAragonOpenData.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/logoAragonOpenData.png -------------------------------------------------------------------------------- /budget_app/static/assets/select2/select2x2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/select2/select2x2.png -------------------------------------------------------------------------------- /etls/presupuestos_locales/project/AOD_PREL.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/etls/presupuestos_locales/project/AOD_PREL.zip -------------------------------------------------------------------------------- /budget_app/static/assets/jslider/jslider.blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/jslider/jslider.blue.png -------------------------------------------------------------------------------- /budget_app/static/assets/jslider/jslider.round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/jslider/jslider.round.png -------------------------------------------------------------------------------- /budget_app/static/assets/logo-gobierno-aragon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/logo-gobierno-aragon.png -------------------------------------------------------------------------------- /budget_app/static/assets/logoAragonOpenBudget.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/logoAragonOpenBudget.png -------------------------------------------------------------------------------- /budget_app/static/assets/busget-stream-question.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/busget-stream-question.png -------------------------------------------------------------------------------- /budget_app/static/assets/jslider/jslider.plastic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/jslider/jslider.plastic.png -------------------------------------------------------------------------------- /budget_app/static/assets/jslider/jslider.presus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/jslider/jslider.presus.png -------------------------------------------------------------------------------- /budget_app/static/assets/select2/select2-spinner.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/select2/select2-spinner.gif -------------------------------------------------------------------------------- /budget_app/static/assets/comparison__entity--chart.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/comparison__entity--chart.png -------------------------------------------------------------------------------- /budget_app/static/javascripts/leaflet/images/layers.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/javascripts/leaflet/images/layers.png -------------------------------------------------------------------------------- /budget_app/static/assets/busget-stream-question-hover.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/busget-stream-question-hover.png -------------------------------------------------------------------------------- /budget_app/static/assets/jslider/jslider.round.plastic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/jslider/jslider.round.plastic.png -------------------------------------------------------------------------------- /budget_app/static/javascripts/leaflet/images/layers-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/javascripts/leaflet/images/layers-2x.png -------------------------------------------------------------------------------- /budget_app/static/stylesheets/jslider/jslider.blue.less: -------------------------------------------------------------------------------- 1 | .jslider_blue .jslider-bg i, .jslider_blue .jslider-pointer {background-image: url(../assets/jslider/jslider.blue.png);} -------------------------------------------------------------------------------- /django_jasmine/static/jasmine-latest/jasmine_favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/django_jasmine/static/jasmine-latest/jasmine_favicon.png -------------------------------------------------------------------------------- /theme-aragon/data/comunidad/2014/estructura_economica.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/theme-aragon/data/comunidad/2014/estructura_economica.csv -------------------------------------------------------------------------------- /theme-aragon/data/comunidad/2014/estructura_funcional.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/theme-aragon/data/comunidad/2014/estructura_funcional.csv -------------------------------------------------------------------------------- /theme-aragon/data/comunidad/2014/estructura_organica.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/theme-aragon/data/comunidad/2014/estructura_organica.csv -------------------------------------------------------------------------------- /theme-aragon/data/comunidad/2021/estructura_economica.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/theme-aragon/data/comunidad/2021/estructura_economica.csv -------------------------------------------------------------------------------- /theme-aragon/data/comunidad/2021/estructura_funcional.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/theme-aragon/data/comunidad/2021/estructura_funcional.csv -------------------------------------------------------------------------------- /theme-aragon/data/comunidad/2021/estructura_organica.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/theme-aragon/data/comunidad/2021/estructura_organica.csv -------------------------------------------------------------------------------- /budget_app/static/assets/bootstrap/glyphicons-halflings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/bootstrap/glyphicons-halflings.png -------------------------------------------------------------------------------- /budget_app/static/javascripts/leaflet/images/marker-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/javascripts/leaflet/images/marker-icon.png -------------------------------------------------------------------------------- /django_jasmine/static/jasmine-1.1.0.rc1/jasmine_favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/django_jasmine/static/jasmine-1.1.0.rc1/jasmine_favicon.png -------------------------------------------------------------------------------- /theme-aragon/data/comunidad/2014/estructura_financiacion.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/theme-aragon/data/comunidad/2014/estructura_financiacion.csv -------------------------------------------------------------------------------- /theme-aragon/data/comunidad/2021/estructura_financiacion.csv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/theme-aragon/data/comunidad/2021/estructura_financiacion.csv -------------------------------------------------------------------------------- /budget_app/static/assets/jquery-ui/ui-icons_222222_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/jquery-ui/ui-icons_222222_256x240.png -------------------------------------------------------------------------------- /budget_app/static/assets/jquery-ui/ui-icons_2e83ff_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/jquery-ui/ui-icons_2e83ff_256x240.png -------------------------------------------------------------------------------- /budget_app/static/assets/jquery-ui/ui-icons_454545_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/jquery-ui/ui-icons_454545_256x240.png -------------------------------------------------------------------------------- /budget_app/static/assets/jquery-ui/ui-icons_888888_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/jquery-ui/ui-icons_888888_256x240.png -------------------------------------------------------------------------------- /budget_app/static/assets/jquery-ui/ui-icons_cd0a0a_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/jquery-ui/ui-icons_cd0a0a_256x240.png -------------------------------------------------------------------------------- /budget_app/static/javascripts/leaflet/images/marker-icon-2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/javascripts/leaflet/images/marker-icon-2x.png -------------------------------------------------------------------------------- /budget_app/static/javascripts/leaflet/images/marker-shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/javascripts/leaflet/images/marker-shadow.png -------------------------------------------------------------------------------- /budget_app/static/assets/bootstrap/glyphicons-halflings-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/bootstrap/glyphicons-halflings-white.png -------------------------------------------------------------------------------- /budget_app/static/assets/jquery-ui/ui-bg_flat_0_aaaaaa_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/jquery-ui/ui-bg_flat_0_aaaaaa_40x100.png -------------------------------------------------------------------------------- /budget_app/static/stylesheets/jslider/jslider.plastic.less: -------------------------------------------------------------------------------- 1 | .jslider_plastic .jslider-bg i, .jslider_plastic .jslider-pointer {background-image: url(../assets/jslider/jslider.plastic.png);} 2 | -------------------------------------------------------------------------------- /budget_app/static/assets/jquery-ui/ui-bg_flat_75_ffffff_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/jquery-ui/ui-bg_flat_75_ffffff_40x100.png -------------------------------------------------------------------------------- /budget_app/static/assets/jquery-ui/ui-bg_glass_55_fbf9ee_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/jquery-ui/ui-bg_glass_55_fbf9ee_1x400.png -------------------------------------------------------------------------------- /budget_app/static/assets/jquery-ui/ui-bg_glass_65_ffffff_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/jquery-ui/ui-bg_glass_65_ffffff_1x400.png -------------------------------------------------------------------------------- /budget_app/static/assets/jquery-ui/ui-bg_glass_75_dadada_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/jquery-ui/ui-bg_glass_75_dadada_1x400.png -------------------------------------------------------------------------------- /budget_app/static/assets/jquery-ui/ui-bg_glass_75_e6e6e6_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/jquery-ui/ui-bg_glass_75_e6e6e6_1x400.png -------------------------------------------------------------------------------- /budget_app/static/assets/jquery-ui/ui-bg_glass_75_ffffff_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/jquery-ui/ui-bg_glass_75_ffffff_1x400.png -------------------------------------------------------------------------------- /etls/presupuestos_locales/doc/[AOD]_Manual de ejecución_v1.0.0.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/etls/presupuestos_locales/doc/[AOD]_Manual de ejecución_v1.0.0.docx -------------------------------------------------------------------------------- /templates/shared/fonts.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /budget_app/static/assets/jquery-ui/ui-bg_inset-soft_95_fef1ec_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/jquery-ui/ui-bg_inset-soft_95_fef1ec_1x100.png -------------------------------------------------------------------------------- /budget_app/static/assets/jquery-ui/ui-bg_highlight-soft_75_cccccc_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/budget_app/static/assets/jquery-ui/ui-bg_highlight-soft_75_cccccc_1x100.png -------------------------------------------------------------------------------- /etls/presupuestos_locales/doc/[AOD]_Extracción y carga de presupuestos locales_v1.0.0.docx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aragonopendata/presupuesto/HEAD/etls/presupuestos_locales/doc/[AOD]_Extracción y carga de presupuestos locales_v1.0.0.docx -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | Coffin==0.3.7 2 | Django==1.4.3 3 | django-appconf==1.0.1 4 | django-compressor==1.4 5 | django-jasmine==0.4.1 6 | Jinja2==2.6 7 | MarkupSafe==1.0 8 | psycopg2-binary==2.8.4 9 | Pygments==2.4.2 10 | six==1.12.0 11 | xlwt==0.7.5 12 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | db 2 | *pyc 3 | .DS_Store 4 | /static 5 | local_settings*py 6 | /project/settings.py 7 | fabfile.* 8 | presupuesto-* 9 | *config.codekit 10 | 11 | budget_app/static/stylesheets/config.codekit 12 | /.gitignore 13 | /.project 14 | /.pydevproject -------------------------------------------------------------------------------- /templates/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Presupuestos - En mantenimiento 6 | 7 | 8 |

En mantenimiento

9 |

Volvemos en unos minutos.

10 | 11 | -------------------------------------------------------------------------------- /budget_app/static/stylesheets/jslider/jslider.round.less: -------------------------------------------------------------------------------- 1 | .jslider_round .jslider-bg i, .jslider_round .jslider-pointer {background-image: url(../assets/jslider/jslider.round.png);} 2 | .jslider_round .jslider-pointer {width: 17px; height: 17px; top: -6px; margin-left: -8px;} -------------------------------------------------------------------------------- /budget_app/management/commands/clear_cache_local.py: -------------------------------------------------------------------------------- 1 | from django.core.management.base import BaseCommand 2 | from django.core.cache import cache 3 | 4 | class Command(BaseCommand): 5 | def handle(self, *args, **kwargs): 6 | cache.clear() 7 | self.stdout.write('Cleared cache\n') 8 | -------------------------------------------------------------------------------- /manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | import os 3 | import sys 4 | 5 | if __name__ == "__main__": 6 | os.environ.setdefault("DJANGO_SETTINGS_MODULE", "project.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /budget_app/static/stylesheets/jslider/jslider.round.plastic.less: -------------------------------------------------------------------------------- 1 | .jslider_round_plastic .jslider-bg i, .jslider_round_plastic .jslider-pointer {background-image: url(../assets/jslider/jslider.round.plastic.png);} 2 | .jslider_round_plastic .jslider-pointer {width: 18px; height: 18px; top: -7px; margin-left: -8px;} -------------------------------------------------------------------------------- /budget_app/views/reuse.py: -------------------------------------------------------------------------------- 1 | from coffin.shortcuts import render_to_response 2 | from django.utils.translation import ugettext as _ 3 | from helpers import * 4 | 5 | 6 | def reuse(request): 7 | c = get_context(request, css_class='body-reuse', title=_('Inicio')) 8 | return render_to_response('reuse/index.html', c) 9 | -------------------------------------------------------------------------------- /budget_app/loaders/__init__.py: -------------------------------------------------------------------------------- 1 | from aragon_bulk_budget_loader import AragonBulkBudgetLoader 2 | from budget_loader import BudgetLoader 3 | from simple_budget_loader import SimpleBudgetLoader 4 | from glossary_loader import GlossaryLoader 5 | from stat_loader import StatLoader 6 | from entity_loader import EntityLoader 7 | from payments_loader import PaymentsLoader -------------------------------------------------------------------------------- /budget_app/models/__init__.py: -------------------------------------------------------------------------------- 1 | from entity import * 2 | from budget import * 3 | from economic_category import * 4 | from functional_category import * 5 | from funding_category import * 6 | from budget_item import * 7 | from budget_breakdown import * 8 | from glossary_term import * 9 | from institutional_category import * 10 | from inflation_stat import * 11 | from population_stat import * 12 | from payment import * -------------------------------------------------------------------------------- /budget_app/static/stylesheets/bootstrap/layouts.less: -------------------------------------------------------------------------------- 1 | // 2 | // Layouts 3 | // -------------------------------------------------- 4 | 5 | 6 | // Container (centered, fixed-width layouts) 7 | .container { 8 | .container-fixed(); 9 | } 10 | 11 | // Fluid layouts (left aligned, with sidebar, min- & max-width content) 12 | .container-fluid { 13 | padding-right: @gridGutterWidth; 14 | padding-left: @gridGutterWidth; 15 | .clearfix(); 16 | } -------------------------------------------------------------------------------- /templates/shared/data_controllers_select.html: -------------------------------------------------------------------------------- 1 | 9 | -------------------------------------------------------------------------------- /budget_app/tests.py: -------------------------------------------------------------------------------- 1 | """ 2 | This file demonstrates writing tests using the unittest module. These will pass 3 | when you run "manage.py test". 4 | 5 | Replace this with more appropriate tests for your application. 6 | """ 7 | 8 | from django.test import TestCase 9 | 10 | 11 | class SimpleTest(TestCase): 12 | def test_basic_addition(self): 13 | """ 14 | Tests that 1 + 1 always equals 2. 15 | """ 16 | self.assertEqual(1 + 1, 2) 17 | -------------------------------------------------------------------------------- /tests/files.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Main Test Suite", 3 | "js_files": [ 4 | "../static/javascripts/jquery-1.8.3.min.js", 5 | "../static/javascripts/grid_controls.js", 6 | "../static/javascripts/grid_formatters.js", 7 | "../static/javascripts/grid_helpers.js", 8 | "../static/javascripts/numeral.min.js", 9 | "../static/javascripts/numeral-languages.min.js", 10 | "../static/javascripts/tax_calculator.js" 11 | ], 12 | "media_files": [ 13 | ] 14 | } -------------------------------------------------------------------------------- /budget_app/static/stylesheets/vis/infobox.less: -------------------------------------------------------------------------------- 1 | // INFOBOX 2 | #pop-up { 3 | display: none; 4 | position:absolute; 5 | color: white; 6 | line-height: 1.1; 7 | background: rgba(0,0,0,0.7); 8 | padding: 5px 10px 5px 10px; 9 | -moz-border-radius: 8px 8px; 10 | border-radius: 8px 8px; 11 | pointer-events: none; 12 | } 13 | 14 | #pop-up-title { 15 | font-size: 14px; 16 | margin-bottom: 4px; 17 | font-weight: bolder; 18 | } 19 | 20 | #pop-up-content { 21 | font-size: 12px; 22 | } -------------------------------------------------------------------------------- /budget_app/static/javascripts/utils.js: -------------------------------------------------------------------------------- 1 | function countReused() { 2 | var cnt_aragon = $('#adm_aragon li').length; 3 | var cnt_spain = $('#adm_spain li').length; 4 | var cnt_orgs = $('#emp_org li').length; 5 | var cnt_int = $('#adm_int li').length; 6 | $('#adm_count_text').text('Ya sois ' + cnt_aragon + ' administraciones en Aragón, ' + cnt_spain + ' en España, ' 7 | + cnt_orgs + ' empresas y organizaciones y ' + cnt_int + ' entidad internacional. Y al resto de administraciones, ¡os esperamos!'); 8 | } -------------------------------------------------------------------------------- /budget_app/static/stylesheets/bootstrap/component-animations.less: -------------------------------------------------------------------------------- 1 | // 2 | // Component animations 3 | // -------------------------------------------------- 4 | 5 | 6 | .fade { 7 | opacity: 0; 8 | .transition(opacity .15s linear); 9 | &.in { 10 | opacity: 1; 11 | } 12 | } 13 | 14 | .collapse { 15 | position: relative; 16 | height: 0; 17 | overflow: hidden; 18 | overflow: visible \9; 19 | .transition(height .35s ease); 20 | &.in { 21 | height: auto; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /budget_app/static/stylesheets/bootstrap/utilities.less: -------------------------------------------------------------------------------- 1 | // 2 | // Utility classes 3 | // -------------------------------------------------- 4 | 5 | 6 | // Quick floats 7 | .pull-right { 8 | float: right; 9 | } 10 | .pull-left { 11 | float: left; 12 | } 13 | 14 | // Toggling content 15 | .hide { 16 | display: none; 17 | } 18 | .show { 19 | display: block; 20 | } 21 | 22 | // Visibility 23 | .invisible { 24 | visibility: hidden; 25 | } 26 | 27 | // For Affix plugin 28 | .affix { 29 | position: fixed; 30 | } 31 | -------------------------------------------------------------------------------- /budget_app/static/stylesheets/fonts.less: -------------------------------------------------------------------------------- 1 | // Titillium Web 2 | // URL: http://www.google.com/fonts/specimen/Titillium+Web 3 | // Titillium+Web:400,200,200italic,300,300italic,400italic,600,600italic,700,700italic:latin 4 | // Styles (+ their italics): 5 | // Extra Light 200 6 | // Light 300 7 | // Normal 400 8 | // Semi-Bold 600 9 | // Bold 700 10 | body, label, input, select, optgroup, option, textarea, button {font-family: @baseFontFamily;} 11 | em, i, q, blockquote, cite, caption {font-family: @serifFontFamily;} -------------------------------------------------------------------------------- /templates/shared/analytics.html: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /budget_app/static/stylesheets/jslider/jslider.presus.less: -------------------------------------------------------------------------------- 1 | .jslider_presus .jslider-bg i, .jslider_presus .jslider-pointer {background-image: url(../assets/jslider/jslider.presus.png);} 2 | .jslider_presus .jslider-pointer {background-position: 0 -36px; height: 40px; margin-left: -27px; top: -16px; width: 56px; z-index: 2;} 3 | .jslider_presus .jslider-pointer-hover {background-position: -56px -36px;} 4 | .jslider_presus .jslider-value {top: -2.65em;} 5 | .jslider_presus .jslider-scale ins {top: 12px;} 6 | .jslider_presus .jslider-scale span {height: 12px;} -------------------------------------------------------------------------------- /templates/entities/totals.html: -------------------------------------------------------------------------------- 1 |
2 |

3 | 4 |
5 | {{ _('Presupuestados') }} 6 |

7 |

8 | 9 |
10 | {{ _('Presupuestados') }} 11 |

12 |
13 | -------------------------------------------------------------------------------- /budget_app/static/stylesheets/bootstrap/breadcrumbs.less: -------------------------------------------------------------------------------- 1 | // 2 | // Breadcrumbs 3 | // -------------------------------------------------- 4 | 5 | 6 | .breadcrumb { 7 | padding: 8px 15px; 8 | margin: 0 0 @baseLineHeight; 9 | list-style: none; 10 | background-color: #f5f5f5; 11 | .border-radius(4px); 12 | li { 13 | display: inline-block; 14 | .ie7-inline-block(); 15 | text-shadow: 0 1px 0 @white; 16 | } 17 | .divider { 18 | padding: 0 5px; 19 | color: #ccc; 20 | } 21 | .active { 22 | color: @grayLight; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /budget_app/static/stylesheets/bootstrap/grid.less: -------------------------------------------------------------------------------- 1 | // 2 | // Grid system 3 | // -------------------------------------------------- 4 | 5 | 6 | // Fixed (940px) 7 | #grid > .core(@gridColumnWidth, @gridGutterWidth); 8 | 9 | // Fluid (940px) 10 | #grid > .fluid(@fluidGridColumnWidth, @fluidGridGutterWidth); 11 | 12 | // Reset utility classes due to specificity 13 | [class*="span"].hide, 14 | .row-fluid [class*="span"].hide { 15 | display: none; 16 | } 17 | 18 | [class*="span"].pull-right, 19 | .row-fluid [class*="span"].pull-right { 20 | float: right; 21 | } 22 | -------------------------------------------------------------------------------- /budget_app/views/propertiesLoader.py: -------------------------------------------------------------------------------- 1 | from properties import * 2 | 3 | def loadProperties(c,page): 4 | 5 | #Global Properties 6 | 7 | 8 | #Page Properties 9 | if page == 'reuse': 10 | c['urlOpenData'] = urlOpenData 11 | c['urlCatalogoPresupuesto'] = urlCatalogoPresupuesto 12 | c['urlCodeRepo'] = urlCodeRepo 13 | c['urlLicenseRepo'] = urlLicenseRepo 14 | c['urlInstallRepo'] = urlInstallRepo 15 | c['urlCivioFundation'] = urlCivioFundation 16 | elif page == 'welcome': 17 | c['urlOpenData'] = urlOpenData -------------------------------------------------------------------------------- /local_settings.py-example: -------------------------------------------------------------------------------- 1 | # ENVIRONMENT-SPECIFIC SETTINGS 2 | # 3 | 4 | ENV = { 5 | 6 | 'THEME': 'theme-aragon', 7 | 8 | # 'DEBUG': True, 9 | # 'TEMPLATE_DEBUG': True, 10 | 11 | # Database 12 | 'DATABASE_NAME': 'presupuestos', 13 | 'DATABASE_USER': '', 14 | 'DATABASE_PASSWORD': '', 15 | 'DATABASE_HOST': '', 16 | # 'SEARCH_CONFIG': 'unaccent_spa', 17 | 18 | # Caching 19 | # 'CACHES': { 20 | # 'default': { 21 | # 'BACKEND': 'django.core.cache.backends.filebased.FileBasedCache', 22 | # 'LOCATION': '/tmp' 23 | # } 24 | # } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /properties.py: -------------------------------------------------------------------------------- 1 | # Properties 2 | 3 | # GLOBAL 4 | urlOpenData = 'http://opendata.aragon.es' 5 | appCkan = '/datos' 6 | urlCodeRepo = 'https://github.com/aragonopendata/presupuesto' 7 | urlLicenseRepo = 'https://github.com/aragonopendata/presupuesto/blob/master/LICENSE.md' 8 | urlInstallRepo = 'https://github.com/aragonopendata/presupuesto/blob/master/INSTALL.md' 9 | urlCivioFundation = 'http://dondevanmisimpuestos.es/' 10 | draftBudgetYear = '2024' 11 | draftBudgetYear_2 = '2023' 12 | 13 | # /reuse 14 | urlCatalogoPresupuesto = urlOpenData + appCkan + '/busqueda-libre/Presupuesto' 15 | -------------------------------------------------------------------------------- /theme-aragon/static/sitemaps/Gobierno.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | http://presupuesto.aragon.es/static/sitemaps/Politicas.xml 9 | 10 | 11 | http://presupuesto.aragon.es/static/sitemaps/Programas.xml 12 | 13 | 14 | -------------------------------------------------------------------------------- /budget_app/static/stylesheets/bootstrap/hero-unit.less: -------------------------------------------------------------------------------- 1 | // 2 | // Hero unit 3 | // -------------------------------------------------- 4 | 5 | 6 | .hero-unit { 7 | padding: 60px; 8 | margin-bottom: 30px; 9 | background-color: @heroUnitBackground; 10 | .border-radius(6px); 11 | h1 { 12 | margin-bottom: 0; 13 | font-size: 60px; 14 | line-height: 1; 15 | color: @heroUnitHeadingColor; 16 | letter-spacing: -1px; 17 | } 18 | p { 19 | font-size: 18px; 20 | font-weight: 200; 21 | line-height: @baseLineHeight * 1.5; 22 | color: @heroUnitLeadColor; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /budget_app/static/stylesheets/bootstrap/responsive-768px-979px.less: -------------------------------------------------------------------------------- 1 | // 2 | // Responsive: Tablet to desktop 3 | // -------------------------------------------------- 4 | 5 | 6 | @media (min-width: 768px) and (max-width: 979px) { 7 | 8 | // Fixed grid 9 | #grid > .core(@gridColumnWidth768, @gridGutterWidth768); 10 | 11 | // Fluid grid 12 | #grid > .fluid(@fluidGridColumnWidth768, @fluidGridGutterWidth768); 13 | 14 | // Input grid 15 | #grid > .input(@gridColumnWidth768, @gridGutterWidth768); 16 | 17 | // No need to reset .thumbnails here since it's the same @gridGutterWidth 18 | 19 | } 20 | -------------------------------------------------------------------------------- /budget_app/static/javascripts/comparator.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | // 3 | // 1. Sugerir la comparación entre entidades (municipio o comarca) 4 | // 5 | // Ocultar form + Meter Select2 6 | $(".comparator__form").hide().find("select").select2(); 7 | 8 | // Abrir / Cerrar form 9 | $('.comparator__cta').toggle( 10 | function(){ 11 | $(this).closest(".comparator").addClass("active"); 12 | $(this).next(".comparator__form").show(); 13 | }, 14 | function(){ 15 | $(this).closest(".comparator").removeClass("active"); 16 | $(this).next(".comparator__form").hide(); 17 | } 18 | ); 19 | }); -------------------------------------------------------------------------------- /budget_app/static/stylesheets/vis/policies.index.less: -------------------------------------------------------------------------------- 1 | // TREEMAP 2 | .chart-container { 3 | rect { 4 | cursor: pointer; 5 | 6 | &.out{ 7 | fill: rgb(134,134,134) !important; 8 | } 9 | 10 | &.parent { 11 | fill-opacity: 0; 12 | } 13 | 14 | g.bg & { 15 | cursor: default; 16 | } 17 | } 18 | 19 | .treemap-text { 20 | font-size: 15px; 21 | padding: 4px; 22 | line-height: 1.1; 23 | display: block; 24 | fill: #fff; 25 | pointer-events: none; 26 | text-align: center; 27 | text-anchor: middle; 28 | } 29 | 30 | text.nv-legend-text:hover{ 31 | font-weight: 600; 32 | } 33 | } -------------------------------------------------------------------------------- /project/middleware.py: -------------------------------------------------------------------------------- 1 | from django.middleware.cache import UpdateCacheMiddleware 2 | 3 | import re 4 | 5 | # We need to remove Google Analytics cookies or they'll break the cache 6 | # as soon as there's a "Vary: Cookie" header, which is our case. 7 | # See https://github.com/aragonopendata/presupuesto/issues/12 for more info. 8 | class SmartUpdateCacheMiddleware(UpdateCacheMiddleware): 9 | STRIP_RE = re.compile(r'\b(_[^=]+=.+?(?:; |$))') 10 | 11 | def process_request(self, request): 12 | cookie = self.STRIP_RE.sub('', request.META.get('HTTP_COOKIE', '')) 13 | request.META['HTTP_COOKIE'] = cookie 14 | -------------------------------------------------------------------------------- /templates/shared/meta.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /templates/shared/payment_tab.html: -------------------------------------------------------------------------------- 1 | {% macro render_tab(title, category, additional_css) %} 2 |
3 |

{{ title }}

4 |
5 |
6 | 18 |
19 | {% endmacro %} 20 | -------------------------------------------------------------------------------- /budget_app/static/stylesheets/bootstrap/wells.less: -------------------------------------------------------------------------------- 1 | // 2 | // Wells 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base class 7 | .well { 8 | min-height: 20px; 9 | padding: 19px; 10 | margin-bottom: 20px; 11 | background-color: @wellBackground; 12 | border: 1px solid darken(@wellBackground, 7%); 13 | .border-radius(4px); 14 | .box-shadow(inset 0 1px 1px rgba(0,0,0,.05)); 15 | blockquote { 16 | border-color: #ddd; 17 | border-color: rgba(0,0,0,.15); 18 | } 19 | } 20 | 21 | // Sizes 22 | .well-large { 23 | padding: 24px; 24 | .border-radius(6px); 25 | } 26 | .well-small { 27 | padding: 9px; 28 | .border-radius(3px); 29 | } 30 | -------------------------------------------------------------------------------- /budget_app/loaders/entity_loader.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | from budget_app.models import Entity 3 | import csv 4 | import re 5 | 6 | 7 | class EntityLoader: 8 | def load(self, filename): 9 | self._delete_all() 10 | 11 | print "Cargando lista de organismos de %s..." % filename 12 | reader = csv.reader(open(filename, 'rb')) 13 | for index, line in enumerate(reader): 14 | if re.match("^#", line[0]): # Ignore comments 15 | continue 16 | 17 | entity = Entity(code=line[0], level=line[1], name=line[2]) 18 | entity.save() 19 | 20 | def _delete_all(self): 21 | Entity.objects.all().delete() 22 | -------------------------------------------------------------------------------- /budget_app/views/terms.py: -------------------------------------------------------------------------------- 1 | from coffin.shortcuts import render_to_response 2 | from django.core.paginator import Paginator 3 | from django.utils.translation import ugettext as _ 4 | from budget_app.models import GlossaryTerm 5 | from helpers import * 6 | 7 | 8 | def terms(request): 9 | c = get_context(request, css_class='body-glossary', title=_('Inicio')) 10 | 11 | c['query'] = request.GET.get('q', '') 12 | c['query_string'] = "q=%s&" % (c['query']) 13 | page = request.GET.get('page', 1) 14 | 15 | results = Paginator(list(GlossaryTerm.objects.search(c['query'])), 10) 16 | c['terms'] = results.page(page) 17 | 18 | return render_to_response('terms/index.html', c) 19 | -------------------------------------------------------------------------------- /budget_app/management/commands/load_stats.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | from django.core.management.base import BaseCommand 3 | from django.conf import settings 4 | from budget_app.loaders import StatLoader 5 | import os.path 6 | 7 | 8 | class Command(BaseCommand): 9 | help = u"Carga las estadísticas oficiales desde fichero, _reemplazando las actuales_" 10 | 11 | def handle(self, *args, **options): 12 | # Allow overriding the data path from command line 13 | if len(args) < 1: 14 | path = os.path.join(settings.ROOT_PATH, settings.THEME, 'data') # Default: theme data folder 15 | else: 16 | path = args[0] 17 | 18 | StatLoader().load(path) 19 | -------------------------------------------------------------------------------- /budget_app/management/commands/load_glossary.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | from django.core.management.base import BaseCommand 3 | from django.conf import settings 4 | from budget_app.loaders import GlossaryLoader 5 | import os.path 6 | 7 | 8 | class Command(BaseCommand): 9 | help = u"Carga los términos del glosario desde un fichero, _reemplazando el actual_" 10 | 11 | def handle(self, *args, **options): 12 | # Allow overriding the data path from command line 13 | if len(args) < 1: 14 | path = os.path.join(settings.ROOT_PATH, settings.THEME, 'data') # Default: theme data folder 15 | else: 16 | path = args[0] 17 | 18 | GlossaryLoader().load(os.path.join(path, 'glosario.csv')) 19 | -------------------------------------------------------------------------------- /budget_app/static/stylesheets/main.less: -------------------------------------------------------------------------------- 1 | @import "bootstrap/bootstrap.less"; 2 | @import "variables.less"; 3 | @import "colors.less"; 4 | @import "less-elements.less"; 5 | @import "reset.less"; 6 | @import "fonts.less"; 7 | @import "elements.less"; 8 | @import "icons.less"; 9 | @import "layout.less"; 10 | @import "grid.less"; 11 | @import "forms.less"; 12 | @import "tables.less"; 13 | @import "buttons.less"; 14 | @import "alerts.less"; 15 | @import "classes.less"; 16 | @import "patterns.less"; 17 | @import "modules.less"; 18 | @import "helpers.less"; 19 | @import "print.less"; 20 | 21 | @import "vis/nv.d3.less"; 22 | @import "vis/budget.index.less"; 23 | @import "vis/policies.index.less"; 24 | @import "vis/infobox.less"; 25 | @import "vis/map.less"; -------------------------------------------------------------------------------- /budget_app/loaders/glossary_loader.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | from budget_app.models import GlossaryTerm 3 | import csv 4 | import re 5 | 6 | 7 | class GlossaryLoader: 8 | def load(self, filename): 9 | self._delete_all() 10 | 11 | print "Cargando glosario de %s..." % filename 12 | reader = csv.reader(open(filename, 'rb')) 13 | for index, line in enumerate(reader): 14 | if re.match("^#", line[0]): # Ignore comments 15 | continue 16 | 17 | print " Cargando término %s..." % line[0] 18 | term = GlossaryTerm(title=line[0], description=line[1]) 19 | term.save() 20 | 21 | def _delete_all(self): 22 | GlossaryTerm.objects.all().delete() 23 | -------------------------------------------------------------------------------- /budget_app/static/stylesheets/bootstrap/responsive-1200px-min.less: -------------------------------------------------------------------------------- 1 | // 2 | // Responsive: Large desktop and up 3 | // -------------------------------------------------- 4 | 5 | 6 | @media (min-width: 1200px) { 7 | 8 | // Fixed grid 9 | #grid > .core(@gridColumnWidth1200, @gridGutterWidth1200); 10 | 11 | // Fluid grid 12 | #grid > .fluid(@fluidGridColumnWidth1200, @fluidGridGutterWidth1200); 13 | 14 | // Input grid 15 | #grid > .input(@gridColumnWidth1200, @gridGutterWidth1200); 16 | 17 | // Thumbnails 18 | .thumbnails { 19 | margin-left: -@gridGutterWidth1200; 20 | } 21 | .thumbnails > li { 22 | margin-left: @gridGutterWidth1200; 23 | } 24 | .row-fluid .thumbnails { 25 | margin-left: 0; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /theme-aragon/templates/shared/meta.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /templates/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: /comarcas/dc-zaragoza 3 | Disallow: /municipios/valle-de-bardaji 4 | Disallow: /municipios/monterde-de-albarracin 5 | Disallow: /municipios/valdelinares 6 | Disallow: /municipios/berdejo 7 | Disallow: /municipios/cabolafuente 8 | Disallow: /municipios/malanquilla 9 | Disallow: /municipios/malon 10 | Disallow: /municipios/pomer 11 | Disallow: /municipios/clares-de-ribota 12 | Allow: /municipios/camporrells 13 | Disallow: /municipios/campo 14 | Disallow: /municipios/veracruz 15 | Disallow: /municipios/nigüella 16 | Disallow: /municipios/angües 17 | Disallow: /municipios/bagües 18 | Disallow: /municipios/savinan 19 | Disallow: /municipios/*/gastos/main-menu 20 | Disallow: comarcas/d.c.-zaragoza 21 | Sitemap: http://presupuesto.aragon.es/static/sitemap.xml 22 | -------------------------------------------------------------------------------- /budget_app/management/commands/load_budget_data.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | from django.core.management.base import BaseCommand 3 | from django.conf import settings 4 | from budget_app.loaders import AragonBulkBudgetLoader 5 | import os.path 6 | import sys 7 | 8 | 9 | class Command(BaseCommand): 10 | help = u"Carga los presupuestos del nivel dado, para el periodo indicado, reemplazando los existentes" 11 | 12 | def handle(self, *args, **options): 13 | if len(args) < 2: 14 | print "Por favor indique el tipo de entidad y el periodo a cargar." 15 | return 16 | 17 | level = args[0] 18 | period = args[1] 19 | path = os.path.join(settings.ROOT_PATH, settings.THEME, 'data', level, period) 20 | 21 | AragonBulkBudgetLoader().load(level, path) 22 | -------------------------------------------------------------------------------- /budget_app/static/stylesheets/bootstrap/close.less: -------------------------------------------------------------------------------- 1 | // 2 | // Close icons 3 | // -------------------------------------------------- 4 | 5 | 6 | .close { 7 | float: right; 8 | font-size: 20px; 9 | font-weight: bold; 10 | line-height: @baseLineHeight; 11 | color: @black; 12 | text-shadow: 0 1px 0 rgba(255,255,255,1); 13 | .opacity(20); 14 | &:hover { 15 | color: @black; 16 | text-decoration: none; 17 | cursor: pointer; 18 | .opacity(40); 19 | } 20 | } 21 | 22 | // Additional properties for button version 23 | // iOS requires the button element instead of an anchor tag. 24 | // If you want the anchor version, it requires `href="#"`. 25 | button.close { 26 | padding: 0; 27 | cursor: pointer; 28 | background: transparent; 29 | border: 0; 30 | -webkit-appearance: none; 31 | } -------------------------------------------------------------------------------- /budget_app/static/stylesheets/bootstrap/pager.less: -------------------------------------------------------------------------------- 1 | // 2 | // Pager pagination 3 | // -------------------------------------------------- 4 | 5 | 6 | .pager { 7 | margin: @baseLineHeight 0; 8 | list-style: none; 9 | text-align: center; 10 | .clearfix(); 11 | } 12 | .pager li { 13 | display: inline; 14 | } 15 | .pager a { 16 | display: inline-block; 17 | padding: 5px 14px; 18 | background-color: #fff; 19 | border: 1px solid #ddd; 20 | .border-radius(15px); 21 | } 22 | .pager a:hover { 23 | text-decoration: none; 24 | background-color: #f5f5f5; 25 | } 26 | .pager .next a { 27 | float: right; 28 | } 29 | .pager .previous a { 30 | float: left; 31 | } 32 | .pager .disabled a, 33 | .pager .disabled a:hover { 34 | color: @grayLight; 35 | background-color: #fff; 36 | cursor: default; 37 | } -------------------------------------------------------------------------------- /theme-aragon/static/robots.txt: -------------------------------------------------------------------------------- 1 | User-agent: * 2 | Disallow: /comarcas/dc-zaragoza 3 | Disallow: /municipios/valle-de-bardaji 4 | Disallow: /municipios/monterde-de-albarracin 5 | Disallow: /municipios/valdelinares 6 | Disallow: /municipios/berdejo 7 | Disallow: /municipios/cabolafuente 8 | Disallow: /municipios/malanquilla 9 | Disallow: /municipios/malon 10 | Disallow: /municipios/pomer 11 | Disallow: /municipios/clares-de-ribota 12 | Allow: /municipios/camporrells 13 | Disallow: /municipios/campo 14 | Disallow: /municipios/veracruz 15 | Disallow: /municipios/nigüella 16 | Disallow: /municipios/angües 17 | Disallow: /municipios/bagües 18 | Disallow: /municipios/savinan 19 | Disallow: /municipios/*/gastos/main-menu 20 | Disallow: comarcas/d.c.-zaragoza 21 | Sitemap: http://presupuesto.aragon.es/static/sitemap.xml 22 | -------------------------------------------------------------------------------- /budget_app/static/stylesheets/bootstrap/accordion.less: -------------------------------------------------------------------------------- 1 | // 2 | // Accordion 3 | // -------------------------------------------------- 4 | 5 | 6 | // Parent container 7 | .accordion { 8 | margin-bottom: @baseLineHeight; 9 | } 10 | 11 | // Group == heading + body 12 | .accordion-group { 13 | margin-bottom: 2px; 14 | border: 1px solid #e5e5e5; 15 | .border-radius(4px); 16 | } 17 | .accordion-heading { 18 | border-bottom: 0; 19 | } 20 | .accordion-heading .accordion-toggle { 21 | display: block; 22 | padding: 8px 15px; 23 | } 24 | 25 | // General toggle styles 26 | .accordion-toggle { 27 | cursor: pointer; 28 | } 29 | 30 | // Inner needs the styles because you can't animate properly with any styles on the element 31 | .accordion-inner { 32 | padding: 9px 15px; 33 | border-top: 1px solid #e5e5e5; 34 | } 35 | -------------------------------------------------------------------------------- /budget_app/management/commands/load_entities.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | from django.core.management.base import BaseCommand 3 | from django.conf import settings 4 | from budget_app.loaders import EntityLoader 5 | import os.path 6 | 7 | 8 | class Command(BaseCommand): 9 | help = u"Carga la lista de entidades públicas, _reemplazando el actual_" 10 | 11 | def handle(self, *args, **options): 12 | # Allow overriding the data path from command line 13 | # XXX: overriding was useful when data was shared, not inside the themes. Could be removed. 14 | if len(args) < 1: 15 | path = os.path.join(settings.ROOT_PATH, settings.THEME, 'data') # Default: theme data folder 16 | else: 17 | path = args[0] 18 | 19 | EntityLoader().load(os.path.join(path, 'entidades.csv')) 20 | -------------------------------------------------------------------------------- /theme-aragon/static/sitemap.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | http://presupuesto.aragon.es/static/sitemaps/Home.xml 9 | 10 | 11 | http://presupuesto.aragon.es/static/sitemaps/VisionGlobal.xml 12 | 13 | 14 | http://presupuesto.aragon.es/static/sitemaps/Gobierno.xml 15 | 16 | 17 | http://presupuesto.aragon.es/static/sitemaps/Comarcas.xml 18 | 19 | 20 | http://presupuesto.aragon.es/static/sitemaps/Municipios.xml 21 | 22 | 23 | -------------------------------------------------------------------------------- /templates/shared/entity_select.html: -------------------------------------------------------------------------------- 1 | {% macro render_entity_select(id, entities, current_entity) %} 2 |
3 |

¿Quieres comparar {{ 'esta comarca con otra' if current_entity.level == 'comarca' else 'este municipio con otro' }}?

4 |
5 | 13 | 14 |
15 |
16 | {% endmacro %} -------------------------------------------------------------------------------- /budget_app/views/tax_receipt.py: -------------------------------------------------------------------------------- 1 | from coffin.shortcuts import render_to_response 2 | from budget_app.models import Budget, BudgetBreakdown, BudgetItem, Entity 3 | from helpers import * 4 | from properties import * 5 | 6 | 7 | def tax_receipt(request): 8 | c = get_context(request, css_class='body-tax-receipt', title='') 9 | 10 | # Get latest budget data 11 | populate_latest_budget(c) 12 | populate_descriptions(c) 13 | c['default_income'] = 30000 14 | 15 | # Get the budget breakdown 16 | c['breakdown'] = BudgetBreakdown(['policy', 'programme']) 17 | for item in BudgetItem.objects.each_denormalized("b.id = %s", [c['latest_budget'].id]): 18 | c['breakdown'].add_item(c['latest_budget'].name(), item) 19 | 20 | c['draftBudgetYear'] = draftBudgetYear 21 | c['draftBudgetYear_2'] = draftBudgetYear_2 22 | 23 | return render_to_response('tax_receipt/index.html', c) 24 | -------------------------------------------------------------------------------- /templates/terms/index.html: -------------------------------------------------------------------------------- 1 | {% extends 'base.html' %} 2 | {% from 'shared/pagination.html' import render_pagination_controls as render_pagination_controls %} 3 | {% block content %} 4 |

{{ _('¿Qué significa?') }}

5 | 6 |
7 | 8 | 9 | 10 |
11 | 12 |
13 | {% for term in terms %} 14 |
15 |
{{ term.title }}
16 |
{{ term.description|safe }}
17 |
18 | {% endfor %} 19 |
20 | 21 | {{ render_pagination_controls(terms, query) }} 22 | 23 | {% endblock %} -------------------------------------------------------------------------------- /budget_app/static/javascripts/select2_locale_es.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Select2 Spanish translation 3 | */ 4 | (function ($) { 5 | "use strict"; 6 | 7 | $.extend($.fn.select2.defaults, { 8 | formatNoMatches: function () { return "No se encontraron resultados"; }, 9 | formatInputTooShort: function (input, min) { var n = min - input.length; return "Por favor, introduzca " + n + " car" + (n == 1? "á" : "a") + "cter" + (n == 1? "" : "es"); }, 10 | formatInputTooLong: function (input, max) { var n = input.length - max; return "Por favor, elimine " + n + " car" + (n == 1? "á" : "a") + "cter" + (n == 1? "" : "es"); }, 11 | formatSelectionTooBig: function (limit) { return "Sólo puede seleccionar " + limit + " elemento" + (limit == 1 ? "" : "s"); }, 12 | formatLoadMore: function (pageNumber) { return "Cargando más resultados..."; }, 13 | formatSearching: function () { return "Buscando..."; } 14 | }); 15 | })(jQuery); 16 | -------------------------------------------------------------------------------- /templates/shared/breakdown_tab.html: -------------------------------------------------------------------------------- 1 | {% macro render_tab(title, category, csv_type, id, additional_css, csv_link, xls_link) %} 2 |
3 |

{{ title }}

4 |
5 |
6 |
7 |
8 |

9 | {{ _('Descarga la tabla de') }} {{title}} en {{name}} 10 | 11 | CSV 12 | 13 | o 14 | 15 | Excel 16 | 17 |

18 |
19 | {% endmacro %} -------------------------------------------------------------------------------- /templates/shared/chromeframe.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

{{ _('¡Atención!') }}

4 |

{{ _('Este sitio web no va a funcionar correctamente en tu navegador. La visualización de datos de esta página requieren un navegador con funciones adicionales, como') }} IE9, Firefox, Chrome o Safari. {{ _('Si quieres continuar usando Internet Explorer') }} {{ _('puedes instalar el plugin de Google Chrome Frame') }} {{ _('en unos minutos') }}.

5 |
6 |
7 | -------------------------------------------------------------------------------- /templates/shared/pagination.html: -------------------------------------------------------------------------------- 1 | {% macro render_pagination_controls(items, query_string) %} 2 | {% if items.has_other_pages() %} 3 | 24 | {% endif %} 25 | {% endmacro %} -------------------------------------------------------------------------------- /django_jasmine/urls.py: -------------------------------------------------------------------------------- 1 | import os 2 | import django 3 | 4 | if django.VERSION >= (1, 5): 5 | from django.conf.urls import patterns, url 6 | else: 7 | from django.conf.urls.defaults import * 8 | from django.conf import settings 9 | 10 | from .views import run_tests 11 | 12 | static_root = os.path.join(os.path.dirname(__file__), 'static') 13 | 14 | 15 | urlpatterns = patterns('django.views', 16 | url(r'^tests/(?P.*)$', 'static.serve', { 17 | 'document_root': os.path.join(settings.JASMINE_TEST_DIRECTORY, "spec"), 18 | }, name='jasmine_test'), 19 | url(r'^src/(?P.*)$', 'static.serve', { 20 | 'document_root': os.path.join(settings.JASMINE_TEST_DIRECTORY, "src"), 21 | }, name='jasmine_src'), 22 | url(r'^fixtures/(?P.*)$', 'static.serve', { 23 | 'document_root': os.path.join( 24 | settings.JASMINE_TEST_DIRECTORY, "fixtures", 25 | ), 26 | }, name='jasmine_fixtures'), 27 | url('^(?P.*)$', run_tests, name='jasmine_test_overview'), 28 | ) 29 | -------------------------------------------------------------------------------- /project/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for project. 3 | 4 | This module contains the WSGI application used by Django's development server 5 | and any production WSGI deployments. It should expose a module-level variable 6 | named ``application``. Django's ``runserver`` and ``runfcgi`` commands discover 7 | this application via the ``WSGI_APPLICATION`` setting. 8 | 9 | Usually you will have the standard Django WSGI application here, but it also 10 | might make sense to replace the whole Django WSGI application with a custom one 11 | that later delegates to the Django one. For example, you could introduce WSGI 12 | middleware here, or combine a Django application with an application of another 13 | framework. 14 | 15 | """ 16 | import os 17 | import sys 18 | 19 | BASE_DIR = os.path.dirname(os.path.abspath(__file__)) 20 | sys.path.append(os.path.join(BASE_DIR, '..')) 21 | 22 | os.environ['DJANGO_SETTINGS_MODULE'] = 'project.settings' 23 | 24 | from django.core.wsgi import get_wsgi_application 25 | application = get_wsgi_application() 26 | -------------------------------------------------------------------------------- /theme-aragon/static/sitemaps/Home.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | http://presupuesto.aragon.es/ 9 | yearly 10 | 11 | 12 | http://presupuesto.aragon.es/resumen 13 | yearly 14 | 15 | 16 | http://presupuesto.aragon.es/politicas 17 | yearly 18 | 19 | 20 | http://presupuesto.aragon.es/recibo 21 | yearly 22 | 23 | 24 | http://presupuesto.aragon.es/glosario 25 | yearly 26 | 27 | 28 | http://presupuesto.aragon.es/glosario?page=2 29 | yearly 30 | 31 | -------------------------------------------------------------------------------- /budget_app/static/stylesheets/vis/budget.index.less: -------------------------------------------------------------------------------- 1 | /* 2 | * SANKEY 3 | */ 4 | .sankey .link.with-data { 5 | fill: none; 6 | stroke: #1F77B4; 7 | stroke-opacity: .4; 8 | } 9 | .sankey .legend-budget circle{ 10 | fill: #1F77B4; 11 | opacity: .4; 12 | } 13 | 14 | .sankey .link-execution.with-data { 15 | fill: none; 16 | stroke: #E377C2; 17 | stroke-opacity: .5; 18 | } 19 | .sankey .legend-execution circle{ 20 | fill: #E377C2; 21 | opacity: .5; 22 | } 23 | 24 | .sankey .link.no-data, 25 | .sankey .link-execution.no-data { 26 | fill: none; 27 | stroke: #999; 28 | stroke-opacity: .4; 29 | } 30 | 31 | .sankey .link:hover, 32 | .sankey .node:hover, 33 | .sankey .link-execution:hover { 34 | stroke-opacity: .9; 35 | cursor:pointer; 36 | } 37 | 38 | .sankey .legend-item text, 39 | .sankey .node text { 40 | pointer-events: none; 41 | font-weight: bold; 42 | font-size: 12px; 43 | } 44 | 45 | .sankey .legend-note { 46 | circle{ 47 | fill: none; 48 | } 49 | text{ 50 | font-weight: normal !important; 51 | } 52 | } 53 | 54 | -------------------------------------------------------------------------------- /templates/shared/social_sharing.html: -------------------------------------------------------------------------------- 1 | 10 | -------------------------------------------------------------------------------- /theme-aragon/data/inflacion.csv: -------------------------------------------------------------------------------- 1 | # Datos de IPC del IAEST. Medias anuales de IPC para Aragón, Índice general: 2 | # http://www.aragon.es/DepartamentosOrganismosPublicos/Organismos/InstitutoAragonesEstadistica/pcaxis/ci.Aplicacion_axis_IPC.detalleDepartamento 3 | # Las tasas de inflación se refieren a la media de las tasas interanuales durante los 12 4 | # meses del año. Esta media es calculada por el IAEST y publicada en la dirección indicada. 5 | # La aplicación calcula internamente un índice de inflación a partir de estas tasas, tal que: 6 | # Indice Año Actual = 100 7 | # Indice Año N-1 = Indice Año N / (1+Inflación Año N) 8 | # 9 | # Nota: los presupuestos de un año X se ajustan con el índice de la inflación del año anterior. 10 | # Por tanto el índice del año actual no se usa. De hecho sólo se conoce cuando termina el año. 11 | # 12 | 2005,3.5 13 | 2006,3.7 14 | 2007,2.9 15 | 2008,4.4 16 | 2009,-0.4 17 | 2010,1.8 18 | 2011,3.2 19 | 2012,2.4 20 | 2013,1.3 21 | 2014,-0.3 22 | 2015,-0.7 23 | 2016,-0.2 24 | 2017,1.5 25 | 2018,1.2 26 | 2019,0.8 27 | 2020,-0.8 28 | 2021,6.7 29 | 2022,5.8 30 | 2023,3.6 31 | 2024,0 32 | -------------------------------------------------------------------------------- /budget_app/static/stylesheets/bootstrap/scaffolding.less: -------------------------------------------------------------------------------- 1 | // 2 | // Scaffolding 3 | // -------------------------------------------------- 4 | 5 | 6 | // Body reset 7 | // ------------------------- 8 | 9 | body { 10 | margin: 0; 11 | font-family: @baseFontFamily; 12 | font-size: @baseFontSize; 13 | line-height: @baseLineHeight; 14 | color: @textColor; 15 | background-color: @bodyBackground; 16 | } 17 | 18 | 19 | // Links 20 | // ------------------------- 21 | 22 | a { 23 | color: @linkColor; 24 | text-decoration: none; 25 | } 26 | a:hover { 27 | color: @linkColorHover; 28 | text-decoration: underline; 29 | } 30 | 31 | 32 | // Images 33 | // ------------------------- 34 | 35 | .img-rounded { 36 | .border-radius(6px); 37 | } 38 | 39 | .img-polaroid { 40 | padding: 4px; 41 | background-color: #fff; 42 | border: 1px solid #ccc; 43 | border: 1px solid rgba(0,0,0,.2); 44 | -webkit-box-shadow: 0 1px 3px rgba(0,0,0,.1); 45 | -moz-box-shadow: 0 1px 3px rgba(0,0,0,.1); 46 | box-shadow: 0 1px 3px rgba(0,0,0,.1); 47 | } 48 | 49 | .img-circle { 50 | .border-radius(500px); 51 | } 52 | -------------------------------------------------------------------------------- /budget_app/models/funding_category.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | 4 | class FundingCategoriesManager(models.Manager): 5 | pass 6 | 7 | 8 | class FundingCategory(models.Model): 9 | budget = models.ForeignKey('Budget') 10 | expense = models.BooleanField() 11 | source = models.CharField(max_length=1) 12 | fund_class = models.CharField(max_length=2, null=True) 13 | fund = models.CharField(max_length=5, null=True) 14 | description = models.CharField(max_length=200) 15 | updated_at = models.DateTimeField(auto_now=True) 16 | created_at = models.DateTimeField(auto_now_add=True) 17 | 18 | objects = FundingCategoriesManager() 19 | 20 | class Meta: 21 | app_label = "budget_app" 22 | db_table = "funding_categories" 23 | 24 | # Return the 'budget domain' id, used to uniquely identify a category 25 | # in a budget 26 | def uid(self): 27 | if self.fund_class == None: 28 | return self.source 29 | elif self.fund == None: 30 | return self.fund_class 31 | return self.fund 32 | 33 | def __unicode__(self): 34 | return self.description 35 | -------------------------------------------------------------------------------- /budget_app/models/institutional_category.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | 4 | class InstitutionalCategoriesManager(models.Manager): 5 | pass 6 | 7 | 8 | class InstitutionalCategory(models.Model): 9 | budget = models.ForeignKey('Budget') 10 | institution = models.CharField(max_length=5) 11 | section = models.CharField(max_length=8, null=True) 12 | department = models.CharField(max_length=11, null=True) 13 | description = models.CharField(max_length=200) 14 | updated_at = models.DateTimeField(auto_now=True) 15 | created_at = models.DateTimeField(auto_now_add=True) 16 | 17 | objects = InstitutionalCategoriesManager() 18 | 19 | class Meta: 20 | app_label = "budget_app" 21 | db_table = "institutional_categories" 22 | 23 | # Return the 'budget domain' id, used to uniquely identify a category 24 | # in a budget 25 | def uid(self): 26 | if self.section == None: 27 | return self.institution 28 | elif self.department == None: 29 | return self.section 30 | return self.department 31 | 32 | def __unicode__(self): 33 | return self.description 34 | -------------------------------------------------------------------------------- /budget_app/management/commands/load_payments.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | from django.core.management.base import BaseCommand 3 | from django.conf import settings 4 | from budget_app.loaders import * 5 | import os.path 6 | import sys 7 | 8 | 9 | class Command(BaseCommand): 10 | help = u"Carga los pagos correspondientes al presupuesto del año" 11 | 12 | def handle(self, *args, **options): 13 | if len(args) < 1: 14 | print "Por favor indique el año del presupuesto a cargar." 15 | return 16 | 17 | year = args[0] 18 | region_type = settings.MAIN_ENTITY_LEVEL if len(args)<2 else args[1] 19 | region_name = settings.MAIN_ENTITY_NAME if len(args)<3 else args[2] 20 | path = os.path.join(settings.ROOT_PATH, settings.THEME, 'data', region_type, year) 21 | 22 | # Import the loader dynamically. See http://stackoverflow.com/questions/301134/dynamic-module-import-in-python 23 | module = __import__(settings.THEME+'.loaders', globals(), locals(), [settings.PAYMENTS_LOADER]) 24 | loader = module.__dict__[settings.PAYMENTS_LOADER]() 25 | loader.load(region_type, region_name, year, path) 26 | -------------------------------------------------------------------------------- /django_jasmine/static/jasmine-1.0.1/MIT.LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2008-2010 Pivotal Labs 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /django_jasmine/static/jasmine-1.1.0.rc1/MIT.LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2008-2011 Pivotal Labs 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /django_jasmine/static/jasmine-latest/MIT.LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2008-2011 Pivotal Labs 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /templates/shared/footer.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | Fondo Europeo de Desarrollo Regional (FEDER) 5 |
6 |
7 | 19 |
20 | -------------------------------------------------------------------------------- /budget_app/static/stylesheets/print.less: -------------------------------------------------------------------------------- 1 | @media print { 2 | /* print styles: inlined to avoid required HTTP connection www.phpied.com/delay-loading-your-print-css/ */ 3 | * {background: transparent !important; color: lighten(@black, 20%) !important; text-shadow: none !important;} 4 | a, a:visited {color: lighten(@black, 20%) /*!important; text-decoration: underline;*/} 5 | //a:after {content: " (" attr(href) ")";} 6 | //abbr:after {content: " (" attr(title) ")";} 7 | .ir a:after {content: "";} /* Don't show links for images */ 8 | pre, blockquote {border: 1px solid lighten(@black, 60%); page-break-inside: avoid;} 9 | thead {display: table-header-group;} /* css-discuss.incutio.com/wiki/Printing_Tables */ 10 | tr, img {page-break-inside: avoid;} 11 | @page {margin: 0.5cm;} 12 | p, h2, h3 {orphans: 3; widows: 3;} 13 | h2, h3{page-break-after: avoid;} 14 | 15 | #main-menu, 16 | #menu-lang, 17 | .form-search, 18 | .social-sharing, 19 | .footer-corp .footer-menu, 20 | .slick-cell .toggle.expand, 21 | .slick-sort-indicator{ 22 | display: none; 23 | } 24 | 25 | header[role="banner"] > .container-fluid .logo{ 26 | font-size: 2em; 27 | text-indent: 0 !important; 28 | } 29 | } -------------------------------------------------------------------------------- /theme-aragon/settings.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | MAIN_ENTITY_LEVEL = 'comunidad' 4 | MAIN_ENTITY_NAME = 'Aragón' 5 | 6 | BUDGET_LOADER = 'AragonBudgetLoader' 7 | 8 | FEATURED_PROGRAMMES = ['24121', '24221', '35131'] 9 | 10 | # Sobre la Renta, IVA, Impuestos Especiales, Financiación Autonómica, FEAGA, Capital 11 | OVERVIEW_INCOME_NODES = ['10', '21', '22', '40', '49', '11'] 12 | # Sanidad, Educación, Agricultura, Protección Social, Deuda Pública, Infraestructuras 13 | OVERVIEW_EXPENSE_NODES = ['241', '242', '371', '231', '001', '351'] 14 | 15 | # Show an extra tab with institutional breakdown. Default: True. 16 | # SHOW_INSTITUTIONAL_TAB = True 17 | 18 | # Show an extra tab with funding breakdown (only applicable to some budgets). Default: False. 19 | SHOW_FUNDING_TAB = True 20 | 21 | # Show an extra column with actual revenues/expenses. Default: True. 22 | # Warning: the execution data still gets shown in the summary chart and in downloads. 23 | # SHOW_ACTUAL = True 24 | 25 | # Include financial income/expenditures in overview and global policy breakdowns. Default: False. 26 | # INCLUDE_FINANCIAL_CHAPTERS_IN_BREAKDOWNS = False 27 | 28 | # Search in entity names. Default: True. 29 | # SEARCH_ENTITIES = True 30 | -------------------------------------------------------------------------------- /templates/budgets/budgets_intro.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |

1. {{ _('La recaudación') }}

6 |

{{ _('Se realiza a través de impuestos directos - como el IRPF y el impuesto de sociedades - y de impuestos indirectos - el IVA e impuestos sobre el tabaco, el alcohol o la gasolina -.')|safe }}

7 |
8 |
9 |
10 |
11 |

2. {{ _('¿Cómo se reparte?') }}

12 |

{{ _('Una parte del dinero recaudado se queda en la Administración General del Estado, mientras que otra parte se transfiere al Gobierno de Aragón')|safe }}.

13 |
14 |
15 |
16 |
17 |

3. {{ _('¿En qué se emplea?') }}

18 |

{{ _('El Gobierno de Aragón utiliza el dinero recaudado, junto con otras fuentes de financiación como puede ser la deuda, para mantener los servicios públicos')|safe }}.

19 |
20 |
21 |
22 |
-------------------------------------------------------------------------------- /budget_app/views/payments.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | from coffin.shortcuts import render_to_response 3 | from budget_app.models import BudgetBreakdown, Payment 4 | from helpers import * 5 | 6 | def payments(request, render_callback=None): 7 | # Get request context 8 | c = get_context(request, css_class='body-payments', title='Inversiones y pagos') 9 | 10 | # Payments breakdown 11 | c['payee_breakdown'] = BudgetBreakdown(['payee', 'area', 'description']) 12 | c['area_breakdown'] = BudgetBreakdown(['area', 'payee', 'description']) 13 | 14 | for item in Payment.objects.each_denormalized(): 15 | # We add the date to the description, if it exists: 16 | # TODO: I wanted the date to be in a separate column, but it's complicated right 17 | # now the way BudgetBreakdown works. Need to think about it 18 | if item.date: 19 | item.description = item.description + ' (' + str(item.date) + ')' 20 | 21 | c['payee_breakdown'].add_item(item.year, item) 22 | c['area_breakdown'].add_item(item.year, item) 23 | 24 | # Additional data needed by the view 25 | populate_stats(c) 26 | populate_years(c, 'area_breakdown') 27 | 28 | return render_to_response('payments/index.html', c) 29 | -------------------------------------------------------------------------------- /budget_app/static/javascripts/jquery.cookie.min.js: -------------------------------------------------------------------------------- 1 | !function(e){"function"==typeof define&&define.amd?define(["jquery"],e):e("object"==typeof exports?require("jquery"):jQuery)}(function($){function e(e){return c.raw?e:encodeURIComponent(e)}function n(e){return c.raw?e:decodeURIComponent(e)}function o(n){return e(c.json?JSON.stringify(n):String(n))}function i(e){0===e.indexOf('"')&&(e=e.slice(1,-1).replace(/\\"/g,'"').replace(/\\\\/g,"\\"));try{return e=decodeURIComponent(e.replace(t," ")),c.json?JSON.parse(e):e}catch(n){}}function r(e,n){var o=c.raw?e:i(e);return $.isFunction(n)?n(o):o}var t=/\+/g,c=$.cookie=function(i,t,u){if(void 0!==t&&!$.isFunction(t)){if(u=$.extend({},c.defaults,u),"number"==typeof u.expires){var f=u.expires,a=u.expires=new Date;a.setTime(+a+864e5*f)}return document.cookie=[e(i),"=",o(t),u.expires?"; expires="+u.expires.toUTCString():"",u.path?"; path="+u.path:"",u.domain?"; domain="+u.domain:"",u.secure?"; secure":""].join("")}for(var d=i?void 0:{},p=document.cookie?document.cookie.split("; "):[],s=0,m=p.length;m>s;s++){var x=p[s].split("="),v=n(x.shift()),k=x.join("=");if(i&&i===v){d=r(k,t);break}i||void 0===(k=r(k))||(d[v]=k)}return d};c.defaults={},$.removeCookie=function(e,n){return void 0===$.cookie(e)?!1:($.cookie(e,"",$.extend({},n,{expires:-1})),!$.cookie(e))}}); -------------------------------------------------------------------------------- /budget_app/static/stylesheets/patches.less: -------------------------------------------------------------------------------- 1 | /* @group jQuery UI */ 2 | .ui-widget {font-family: @baseFontFamily; margin: 0 0 1.5em;} 3 | .ui-widget-content a {color: @linkColor;} 4 | /* @end */ 5 | 6 | 7 | 8 | /* @group Add this */ 9 | .addthis_toolbox {margin: 0; text-align: center;} 10 | .addthis_toolbox li {display: inline-block; .ie7-inline-block; list-style: none; list-style-type: none; margin: 0 .4em .25em 0.4em; vertical-align: middle;} 11 | [class^="addthis_button_"] {background: @black20 url(../assets/social.png) no-repeat center; height: 40px; width: 40px; .rounded(50%); .ir; @shadow: 0 0 0 3px fadeout(@color-base, 80%), 0 0 .25em fadeout(@black, 50%) inset; .box-shadow(@shadow);} 12 | [class^="addthis_button_"] span {background: none!important;} 13 | [class^="addthis_button_"]:hover {background-color: @color-base;} 14 | .addthis_default_style .at300b, .addthis_default_style .at300bo, .addthis_default_style .at300m {padding: 0!important;} 15 | .addthis_button_twitter {background-position: 0 0;} 16 | .addthis_button_facebook {background-position: -51px 0;} 17 | .addthis_button_google_plusone_share {background-position: -103px 0;} 18 | .addthis_button_email {background-position: -154px 0;} 19 | .addthis_counter {} 20 | .addthis_button_expanded {.box-shadow(none);} 21 | /* @end */ -------------------------------------------------------------------------------- /budget_app/models/glossary_term.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.conf import settings 3 | 4 | 5 | class GlossaryTermManager(models.Manager): 6 | def search(self, query): 7 | sql = "select id, title, description from glossary_terms " 8 | 9 | # If no query is passed (i.e. we're showing the glossary page), 10 | # return all the terms. 11 | if query and query != '': 12 | sql += "where " \ 13 | "to_tsvector('"+settings.SEARCH_CONFIG+"',title) @@ plainto_tsquery('"+settings.SEARCH_CONFIG+"',%s) or " \ 14 | "to_tsvector('"+settings.SEARCH_CONFIG+"',description) @@ plainto_tsquery('"+settings.SEARCH_CONFIG+"',%s) " 15 | 16 | sql += "order by title asc" 17 | return self.raw(sql, [query, query]) 18 | 19 | 20 | class GlossaryTerm(models.Model): 21 | title = models.CharField(max_length=100) 22 | description = models.CharField(max_length=2000) 23 | updated_at = models.DateTimeField(auto_now=True) 24 | created_at = models.DateTimeField(auto_now_add=True) 25 | 26 | objects = GlossaryTermManager() 27 | 28 | class Meta: 29 | app_label = "budget_app" 30 | db_table = "glossary_terms" 31 | 32 | def __unicode__(self): 33 | return self.title 34 | -------------------------------------------------------------------------------- /budget_app/views/__init__.py: -------------------------------------------------------------------------------- 1 | from budgets import budgets 2 | from policies import policies, policies_show, programmes_show, income_articles_show, expense_articles_show 3 | from counties import counties, counties_show, counties_compare, counties_show_income, counties_show_expense, counties_show_fexpense 4 | from towns import towns, towns_show, towns_compare, towns_show_income, towns_show_expense, towns_show_fexpense 5 | from entities import entities_index, entities_show, entities_show_article, entities_show_policy 6 | from tax_receipt import tax_receipt 7 | from terms import terms 8 | from reuse import reuse 9 | from welcome import welcome 10 | from search import search 11 | from payments import payments 12 | from csv_xls import entity_expenses, entity_fexpenses, entity_income, entity_article_expenses, entity_article_fexpenses, entity_article_income 13 | from csv_xls import functional_policy_breakdown, economic_policy_breakdown, funding_policy_breakdown, institutional_policy_breakdown 14 | from csv_xls import economic_programme_breakdown, funding_programme_breakdown, institutional_programme_breakdown 15 | from csv_xls import economic_article_breakdown, funding_article_breakdown, institutional_article_breakdown, functional_article_breakdown 16 | from csv_xls import entities_expenses, entities_income 17 | 18 | -------------------------------------------------------------------------------- /budget_app/static/stylesheets/bootstrap/responsive.less: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap Responsive v2.1.0 3 | * 4 | * Copyright 2012 Twitter, Inc 5 | * Licensed under the Apache License v2.0 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Designed and built with all the love in the world @twitter by @mdo and @fat. 9 | */ 10 | 11 | 12 | // Responsive.less 13 | // For phone and tablet devices 14 | // ------------------------------------------------------------- 15 | 16 | 17 | // REPEAT VARIABLES & MIXINS 18 | // ------------------------- 19 | // Required since we compile the responsive stuff separately 20 | 21 | @import "variables.less"; // Modify this for custom colors, font-sizes, etc 22 | @import "mixins.less"; 23 | 24 | 25 | // RESPONSIVE CLASSES 26 | // ------------------ 27 | 28 | @import "responsive-utilities.less"; 29 | 30 | 31 | // MEDIA QUERIES 32 | // ------------------ 33 | 34 | // Large desktops 35 | @import "responsive-1200px-min.less"; 36 | 37 | // Tablets to regular desktops 38 | @import "responsive-768px-979px.less"; 39 | 40 | // Phones to portrait tablets and narrow desktops 41 | @import "responsive-767px-max.less"; 42 | 43 | 44 | // RESPONSIVE NAVBAR 45 | // ------------------ 46 | 47 | // From 979px and below, show a button to toggle navbar contents 48 | @import "responsive-navbar.less"; 49 | -------------------------------------------------------------------------------- /budget_app/static/stylesheets/colors.less: -------------------------------------------------------------------------------- 1 | @black: #000; 2 | @black10: lighten(@black, 10%);/* #1a1a1a */ 3 | @black20: lighten(@black, 20%);/* #333 */ 4 | @black30: lighten(@black, 30%);/* #4d4d4d */ 5 | @black40: lighten(@black, 40%);/* #666 */ 6 | @black50: lighten(@black, 50%);/* #808080 */ 7 | @black60: lighten(@black, 60%);/* #999 */ 8 | @black70: lighten(@black, 70%);/* #b3b3b3 */ 9 | @black80: lighten(@black, 80%);/* #ccc */ 10 | @black85: lighten(@black, 85%);/* #d9d9d9 */ 11 | @black90: lighten(@black, 90%);/* #e6e6e6 */ 12 | @black95: lighten(@black, 95%);/* #f2f2f2 */ 13 | @white: #fff; 14 | @black75a: fadeout(@black, 75%); 15 | @white75a: fadeout(@white, 75%); 16 | @black90a: fadeout(@black, 90%); 17 | @white90a: fadeout(@white, 90%); 18 | 19 | // Color: Harmony 20 | @brightness: 10%;// Cantidad de brillo añadir/restar a los colores de la paleta de color para crear semitonos 21 | @degrees: 35%; 22 | 23 | @color-base: #b72f00; 24 | @complementary: spin(@color-base, 180); 25 | @accented-1: spin(@color-base, -(@degrees)); 26 | @accented-2: spin(@color-base, @degrees); 27 | @linkColor: @color-base; 28 | 29 | 30 | // Color: helpers 31 | @formControlFocus: @color-base; 32 | @yellow-mark: #ff9; 33 | @yellow-note: #ff9; 34 | @red: #c00; 35 | 36 | // Entities compared 37 | @entity1: @accented-1; 38 | @entity2: @accented-2; -------------------------------------------------------------------------------- /budget_app/static/javascripts/css_browser_selector.js: -------------------------------------------------------------------------------- 1 | /* 2 | CSS Browser Selector v0.4.0 (Nov 02, 2010) 3 | Rafael Lima (http://rafael.adm.br) 4 | http://rafael.adm.br/css_browser_selector 5 | License: http://creativecommons.org/licenses/by/2.5/ 6 | Contributors: http://rafael.adm.br/css_browser_selector#contributors 7 | */ 8 | function css_browser_selector(u){var ua=u.toLowerCase(),is=function(t){return ua.indexOf(t)>-1},g='gecko',w='webkit',s='safari',o='opera',m='mobile',h=document.documentElement,b=[(!(/opera|webtv/i.test(ua))&&/msie\s(\d)/.test(ua))?('ie ie'+RegExp.$1):is('firefox/2')?g+' ff2':is('firefox/3.5')?g+' ff3 ff3_5':is('firefox/3.6')?g+' ff3 ff3_6':is('firefox/3')?g+' ff3':is('gecko/')?g:is('opera')?o+(/version\/(\d+)/.test(ua)?' '+o+RegExp.$1:(/opera(\s|\/)(\d+)/.test(ua)?' '+o+RegExp.$2:'')):is('konqueror')?'konqueror':is('blackberry')?m+' blackberry':is('android')?m+' android':is('chrome')?w+' chrome':is('iron')?w+' iron':is('applewebkit/')?w+' '+s+(/version\/(\d+)/.test(ua)?' '+s+RegExp.$1:''):is('mozilla/')?g:'',is('j2me')?m+' j2me':is('iphone')?m+' iphone':is('ipod')?m+' ipod':is('ipad')?m+' ipad':is('mac')?'mac':is('darwin')?'mac':is('webtv')?'webtv':is('win')?'win'+(is('windows nt 6.0')?' vista':''):is('freebsd')?'freebsd':(is('x11')||is('linux'))?'linux':'','js']; c = b.join(' '); h.className += ' '+c; return c;}; css_browser_selector(navigator.userAgent); 9 | -------------------------------------------------------------------------------- /budget_app/static/javascripts/jslider/tmpl.js: -------------------------------------------------------------------------------- 1 | // Simple JavaScript Templating 2 | // John Resig - http://ejohn.org/ - MIT Licensed 3 | (function(){ 4 | var cache = {}; 5 | 6 | this.tmpl = function tmpl(str, data){ 7 | // Figure out if we're getting a template, or if we need to 8 | // load the template - and be sure to cache the result. 9 | var fn = !/\W/.test(str) ? 10 | cache[str] = cache[str] || 11 | tmpl(document.getElementById(str).innerHTML) : 12 | 13 | // Generate a reusable function that will serve as a template 14 | // generator (and which will be cached). 15 | new Function("obj", 16 | "var p=[],print=function(){p.push.apply(p,arguments);};" + 17 | 18 | // Introduce the data as local variables using with(){} 19 | "with(obj){p.push('" + 20 | 21 | // Convert the template into pure JavaScript 22 | str 23 | .replace(/[\r\t\n]/g, " ") 24 | .split("<%").join("\t") 25 | .replace(/((^|%>)[^\t]*)'/g, "$1\r") 26 | .replace(/\t=(.*?)%>/g, "',$1,'") 27 | .split("\t").join("');") 28 | .split("%>").join("p.push('") 29 | .split("\r").join("\\'") 30 | + "');}return p.join('');"); 31 | 32 | // Provide some basic currying to the user 33 | return data ? fn( data ) : fn; 34 | }; 35 | })(); -------------------------------------------------------------------------------- /budget_app/models/inflation_stat.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | 4 | class InflationStatManager(models.Manager): 5 | def get_table(self): 6 | table = {} 7 | for stat in self.all(): 8 | table[stat.year] = { 9 | 'inflation': stat.inflation 10 | } 11 | 12 | # We get raw inflation data from the stats file, we need an index, 13 | # where the latest year equals 100. 14 | inflation_index = None 15 | for year in sorted(table.keys(), reverse=True): 16 | if inflation_index is None: 17 | inflation_index = 100 # Most recent year 18 | else: 19 | inflation_index = inflation_index / (1 + (table[year+1]['inflation']/100)) 20 | table[year]['inflation_index'] = inflation_index 21 | 22 | return table 23 | 24 | def get_last_year(self): 25 | return self.order_by('-year').all()[0].year 26 | 27 | 28 | class InflationStat(models.Model): 29 | year = models.IntegerField() 30 | inflation = models.FloatField() 31 | updated_at = models.DateTimeField(auto_now=True) 32 | created_at = models.DateTimeField(auto_now_add=True) 33 | 34 | objects = InflationStatManager() 35 | 36 | class Meta: 37 | app_label = "budget_app" 38 | db_table = "inflation_stats" 39 | 40 | def __unicode__(self): 41 | return self.year 42 | -------------------------------------------------------------------------------- /templates/budgets/budgets_indicators.html: -------------------------------------------------------------------------------- 1 |

{{ _('Indicadores presupuestarios en') }}

2 | 3 |
4 |

5 | {{ _('Ahorro bruto') }}

6 |

7 | {{ _('Ahorro neto') }}

8 |

9 | {{ _('Capacidad/Necesidad de financiación') }}

10 |
11 | 12 |

{{ _('Cantidades actualizadas con la inflación a fecha 1 de enero de') }} {{ last_inflation_year }}. {{ _('El cálculo de los indicadores utiliza las cifras del presupuesto aprobado') }}.

-------------------------------------------------------------------------------- /budget_app/static/stylesheets/bootstrap/responsive-utilities.less: -------------------------------------------------------------------------------- 1 | // 2 | // Responsive: Utility classes 3 | // -------------------------------------------------- 4 | 5 | 6 | // Hide from screenreaders and browsers 7 | // Credit: HTML5 Boilerplate 8 | .hidden { 9 | display: none; 10 | visibility: hidden; 11 | } 12 | 13 | // Visibility utilities 14 | 15 | // For desktops 16 | .visible-phone { display: none !important; } 17 | .visible-tablet { display: none !important; } 18 | .hidden-phone { } 19 | .hidden-tablet { } 20 | .hidden-desktop { display: none !important; } 21 | .visible-desktop { display: inherit !important; } 22 | 23 | // Tablets & small desktops only 24 | @media (min-width: 768px) and (max-width: 979px) { 25 | // Hide everything else 26 | .hidden-desktop { display: inherit !important; } 27 | .visible-desktop { display: none !important ; } 28 | // Show 29 | .visible-tablet { display: inherit !important; } 30 | // Hide 31 | .hidden-tablet { display: none !important; } 32 | } 33 | 34 | // Phones only 35 | @media (max-width: 767px) { 36 | // Hide everything else 37 | .hidden-desktop { display: inherit !important; } 38 | .visible-desktop { display: none !important; } 39 | // Show 40 | .visible-phone { display: inherit !important; } // Use inherit to restore previous behavior 41 | // Hide 42 | .hidden-phone { display: none !important; } 43 | } 44 | -------------------------------------------------------------------------------- /budget_app/static/stylesheets/bootstrap/thumbnails.less: -------------------------------------------------------------------------------- 1 | // 2 | // Thumbnails 3 | // -------------------------------------------------- 4 | 5 | 6 | // Note: `.thumbnails` and `.thumbnails > li` are overriden in responsive files 7 | 8 | // Make wrapper ul behave like the grid 9 | .thumbnails { 10 | margin-left: -@gridGutterWidth; 11 | list-style: none; 12 | .clearfix(); 13 | } 14 | // Fluid rows have no left margin 15 | .row-fluid .thumbnails { 16 | margin-left: 0; 17 | } 18 | 19 | // Float li to make thumbnails appear in a row 20 | .thumbnails > li { 21 | float: left; // Explicity set the float since we don't require .span* classes 22 | margin-bottom: @baseLineHeight; 23 | margin-left: @gridGutterWidth; 24 | } 25 | 26 | // The actual thumbnail (can be `a` or `div`) 27 | .thumbnail { 28 | display: block; 29 | padding: 4px; 30 | line-height: @baseLineHeight; 31 | border: 1px solid #ddd; 32 | .border-radius(4px); 33 | .box-shadow(0 1px 3px rgba(0,0,0,.055)); 34 | .transition(all .2s ease-in-out); 35 | } 36 | // Add a hover state for linked versions only 37 | a.thumbnail:hover { 38 | border-color: @linkColor; 39 | .box-shadow(0 1px 4px rgba(0,105,214,.25)); 40 | } 41 | 42 | // Images and captions 43 | .thumbnail > img { 44 | display: block; 45 | max-width: 100%; 46 | margin-left: auto; 47 | margin-right: auto; 48 | } 49 | .thumbnail .caption { 50 | padding: 9px; 51 | color: @gray; 52 | } 53 | -------------------------------------------------------------------------------- /budget_app/static/stylesheets/bootstrap/alerts.less: -------------------------------------------------------------------------------- 1 | // 2 | // Alerts 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base styles 7 | // ------------------------- 8 | 9 | .alert { 10 | padding: 8px 35px 8px 14px; 11 | margin-bottom: @baseLineHeight; 12 | text-shadow: 0 1px 0 rgba(255,255,255,.5); 13 | background-color: @warningBackground; 14 | border: 1px solid @warningBorder; 15 | .border-radius(4px); 16 | color: @warningText; 17 | } 18 | .alert h4 { 19 | margin: 0; 20 | } 21 | 22 | // Adjust close link position 23 | .alert .close { 24 | position: relative; 25 | top: -2px; 26 | right: -21px; 27 | line-height: @baseLineHeight; 28 | } 29 | 30 | 31 | // Alternate styles 32 | // ------------------------- 33 | 34 | .alert-success { 35 | background-color: @successBackground; 36 | border-color: @successBorder; 37 | color: @successText; 38 | } 39 | .alert-danger, 40 | .alert-error { 41 | background-color: @errorBackground; 42 | border-color: @errorBorder; 43 | color: @errorText; 44 | } 45 | .alert-info { 46 | background-color: @infoBackground; 47 | border-color: @infoBorder; 48 | color: @infoText; 49 | } 50 | 51 | 52 | // Block alerts 53 | // ------------------------- 54 | 55 | .alert-block { 56 | padding-top: 14px; 57 | padding-bottom: 14px; 58 | } 59 | .alert-block > p, 60 | .alert-block > ul { 61 | margin-bottom: 0; 62 | } 63 | .alert-block p + p { 64 | margin-top: 5px; 65 | } 66 | -------------------------------------------------------------------------------- /templates/shared/data_controllers.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |

{{ _('Ingresos y gastos') }}

6 |
7 | 8 | {% if display_functional_view %} 9 | 10 | 11 | {% else %} 12 | 13 | {% endif %} 14 |
15 |
16 |
17 |
18 |
19 |

{{ _('Cantidades') }}

20 | {% include 'shared/data_controllers_select.html' %} 21 |
22 |
23 | {% if show_side %} 24 |
25 | {% else %} 26 |
27 | {% endif %} 28 |

{{ _('Año') }}

29 |
30 | 31 |
32 |
33 |
34 |
-------------------------------------------------------------------------------- /budget_app/management/commands/load_execution.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | from django.core.management.base import BaseCommand 3 | from django.conf import settings 4 | from budget_app.loaders import * 5 | from budget_app.models import Entity 6 | import os.path 7 | 8 | 9 | class Command(BaseCommand): 10 | help = u"Carga la ejecución presupuestaria del año" 11 | 12 | def handle(self, *args, **options): 13 | if len(args) < 1: 14 | print "Por favor indique el año de la ejecución presupuestaria a cargar." 15 | return 16 | 17 | year = args[0] 18 | 19 | level = settings.MAIN_ENTITY_LEVEL if len(args)<2 else args[1] 20 | name = settings.MAIN_ENTITY_NAME if len(args)<3 else args[2] 21 | entity = self._get_entity(level, name) 22 | 23 | path = os.path.join(settings.ROOT_PATH, settings.THEME, 'data', level, year) 24 | 25 | # Import the loader dynamically. See http://stackoverflow.com/questions/301134/dynamic-module-import-in-python 26 | module = __import__(settings.THEME+'.loaders', globals(), locals(), [settings.BUDGET_LOADER]) 27 | loader = module.__dict__[settings.BUDGET_LOADER]() 28 | loader.load_execution(entity, year, path) 29 | 30 | def _get_entity(self, level, name): 31 | entity = Entity.objects.filter(level=level, name=name) 32 | if not entity: 33 | raise Exception("Entity (%s/%s) not found" % (level, name)) 34 | return entity[0] 35 | -------------------------------------------------------------------------------- /budget_app/static/stylesheets/bootstrap/code.less: -------------------------------------------------------------------------------- 1 | // 2 | // Code (inline and blocK) 3 | // -------------------------------------------------- 4 | 5 | 6 | // Inline and block code styles 7 | code, 8 | pre { 9 | padding: 0 3px 2px; 10 | #font > #family > .monospace; 11 | font-size: @baseFontSize - 2; 12 | color: @grayDark; 13 | .border-radius(3px); 14 | } 15 | 16 | // Inline code 17 | code { 18 | padding: 2px 4px; 19 | color: #d14; 20 | background-color: #f7f7f9; 21 | border: 1px solid #e1e1e8; 22 | } 23 | 24 | // Blocks of code 25 | pre { 26 | display: block; 27 | padding: (@baseLineHeight - 1) / 2; 28 | margin: 0 0 @baseLineHeight / 2; 29 | font-size: @baseFontSize - 1; // 14px to 13px 30 | line-height: @baseLineHeight; 31 | word-break: break-all; 32 | word-wrap: break-word; 33 | white-space: pre; 34 | white-space: pre-wrap; 35 | background-color: #f5f5f5; 36 | border: 1px solid #ccc; // fallback for IE7-8 37 | border: 1px solid rgba(0,0,0,.15); 38 | .border-radius(4px); 39 | 40 | // Make prettyprint styles more spaced out for readability 41 | &.prettyprint { 42 | margin-bottom: @baseLineHeight; 43 | } 44 | 45 | // Account for some code outputs that place code tags in pre tags 46 | code { 47 | padding: 0; 48 | color: inherit; 49 | background-color: transparent; 50 | border: 0; 51 | } 52 | } 53 | 54 | // Enable scrollable blocks of code 55 | .pre-scrollable { 56 | max-height: 340px; 57 | overflow-y: scroll; 58 | } -------------------------------------------------------------------------------- /budget_app/static/javascripts/icons-lte-ie7.js: -------------------------------------------------------------------------------- 1 | /* Load this script using conditional IE comments if you need to support IE 7 and IE 6. */ 2 | 3 | window.onload = function() { 4 | function addIcon(el, entity) { 5 | var html = el.innerHTML; 6 | el.innerHTML = '' + entity + '' + html; 7 | } 8 | var icons = { 9 | 'icon-file-pdf' : 'f', 10 | 'icon-file-word' : 'w', 11 | 'icon-file-excel' : 'l', 12 | 'icon-file-powerpoint' : 's', 13 | 'icon-file-zip' : 'z', 14 | 'icon-plus' : '+', 15 | 'icon-cancel' : 'x', 16 | 'icon-minus' : '-', 17 | 'icon-expenses' : '<', 18 | 'icon-incomes' : '>', 19 | 'icon-programme' : 'p', 20 | 'icon-budget' : 'B', 21 | 'icon-search' : 'S', 22 | 'icon-term' : 'T', 23 | 'icon-glossary' : 'G', 24 | 'icon-calendar' : 'C', 25 | 'icon-aportacion' : 'c', 26 | 'icon-policy' : 'P', 27 | 'icon-home' : 'H', 28 | 'icon-checkmark' : 'v', 29 | 'icon-feed' : 'r', 30 | 'icon-loop' : '!', 31 | 'icon-comparison' : '"' 32 | }, 33 | els = document.getElementsByTagName('*'), 34 | i, attr, c, el; 35 | for (i = 0; ; i += 1) { 36 | el = els[i]; 37 | if(!el) { 38 | break; 39 | } 40 | attr = el.getAttribute('data-icon'); 41 | if (attr) { 42 | addIcon(el, attr); 43 | } 44 | c = el.className; 45 | c = c.match(/icon-[^\s'"]+/); 46 | if (c && icons[c[0]]) { 47 | addIcon(el, icons[c[0]]); 48 | } 49 | } 50 | }; -------------------------------------------------------------------------------- /budget_app/views/welcome.py: -------------------------------------------------------------------------------- 1 | from coffin.shortcuts import render_to_response 2 | from django.conf import settings 3 | from django.utils.translation import ugettext as _ 4 | from budget_app.models import Budget, Entity, FunctionalCategory, BudgetBreakdown 5 | from helpers import * 6 | from random import sample 7 | 8 | 9 | def welcome(request): 10 | c = get_context(request, css_class='body-welcome', title=_('Inicio')) 11 | c['formatter'] = add_thousands_separator 12 | 13 | # Retrieve front page examples 14 | populate_latest_budget(c) 15 | c['featured_programmes'] = list(FunctionalCategory.objects 16 | .filter(budget=c['latest_budget']) 17 | .filter(programme__in=settings.FEATURED_PROGRAMMES)) 18 | 19 | # Decide whether we're going to show budget or execution figures 20 | use_actual = False 21 | for programme in c['featured_programmes']: 22 | if (BudgetItem.objects 23 | .filter(functional_category=programme) 24 | .filter(budget__status='') # Don't use partially executed budgets 25 | .filter(actual=True).count()) > 0: 26 | use_actual = True 27 | break 28 | 29 | # Calculate subtotals for the selected programmes 30 | c['breakdown'] = BudgetBreakdown(['programme']) 31 | for programme in c['featured_programmes']: 32 | for item in programme.budgetitem_set.filter(actual=use_actual): 33 | c['breakdown'].add_item(c['latest_budget'].year, item) 34 | 35 | return render_to_response('welcome/index.html', c) 36 | -------------------------------------------------------------------------------- /budget_app/static/stylesheets/reset.less: -------------------------------------------------------------------------------- 1 | html, body, div, span, object, iframe, 2 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 3 | abbr, address, cite, code, del, dfn, em, img, ins, kbd, q, samp, 4 | small, strong, sub, sup, var, b, i, dl, dt, dd, ol, ul, li, 5 | fieldset, form, label, legend, 6 | table, caption, tbody, tfoot, thead, tr, th, td, 7 | article, aside, canvas, details, figcaption, figure, 8 | footer, header, hgroup, menu, nav, section, summary, 9 | time, mark, audio, video 10 | {margin: 0; padding: 0; border: 0; font-size: 100%; font: inherit; vertical-align: baseline;} 11 | 12 | article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section {display: block;} 13 | audio[controls], canvas, video {display: inline-block; *display: inline; *zoom: 1;} 14 | /* Prevents modern browsers from displaying 'audio' without controls */ 15 | audio:not([controls]) {display: none;} 16 | 17 | /* Corrects inline-block display not defined in IE6/7/8/9 & FF3 */ 18 | audio, canvas, video {display: inline-block; *display: inline; *zoom: 1;} 19 | 20 | /* Normalize monospace sizing: en.wikipedia.org/wiki/MediaWiki_talk:Common.css/Archive_11#Teletype_style_fix_for_Chrome */ 21 | /* Redeclare monospace font family: en.wikipedia.org/wiki/User:Davidgothberg/Test59 */ 22 | pre, code, kbd, samp {font-family: monospace, monospace; _font-family: 'courier new', monospace; font-size: 1em;} 23 | 24 | /* Remove margins for navigation lists */ 25 | nav ul, nav li {margin: 0;} 26 | 27 | /* Addresses styling for 'hidden' attribute not present in IE7/8/9, FF3, S4. Known issue: no IE6 support */ 28 | [hidden] {display: none;} -------------------------------------------------------------------------------- /budget_app/static/stylesheets/bootstrap/pagination.less: -------------------------------------------------------------------------------- 1 | // 2 | // Pagination (multiple pages) 3 | // -------------------------------------------------- 4 | 5 | 6 | .pagination { 7 | height: @baseLineHeight * 2; 8 | margin: @baseLineHeight 0; 9 | } 10 | .pagination ul { 11 | display: inline-block; 12 | .ie7-inline-block(); 13 | margin-left: 0; 14 | margin-bottom: 0; 15 | .border-radius(3px); 16 | .box-shadow(0 1px 2px rgba(0,0,0,.05)); 17 | } 18 | .pagination li { 19 | display: inline; 20 | } 21 | .pagination a, 22 | .pagination span { 23 | float: left; 24 | padding: 0 14px; 25 | line-height: (@baseLineHeight * 2) - 2; 26 | text-decoration: none; 27 | background-color: @paginationBackground; 28 | border: 1px solid @paginationBorder; 29 | border-left-width: 0; 30 | } 31 | .pagination a:hover, 32 | .pagination .active a, 33 | .pagination .active span { 34 | background-color: #f5f5f5; 35 | } 36 | .pagination .active a, 37 | .pagination .active span { 38 | color: @grayLight; 39 | cursor: default; 40 | } 41 | .pagination .disabled span, 42 | .pagination .disabled a, 43 | .pagination .disabled a:hover { 44 | color: @grayLight; 45 | background-color: transparent; 46 | cursor: default; 47 | } 48 | .pagination li:first-child a, 49 | .pagination li:first-child span { 50 | border-left-width: 1px; 51 | .border-radius(3px 0 0 3px); 52 | } 53 | .pagination li:last-child a, 54 | .pagination li:last-child span { 55 | .border-radius(0 3px 3px 0); 56 | } 57 | 58 | // Centered 59 | .pagination-centered { 60 | text-align: center; 61 | } 62 | .pagination-right { 63 | text-align: right; 64 | } 65 | -------------------------------------------------------------------------------- /budget_app/models/entity.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.conf import settings 3 | from django.template.defaultfilters import slugify 4 | 5 | class EntityManager(models.Manager): 6 | def entities(self, level): 7 | return self.filter(level=level).order_by('name') 8 | 9 | def get_entities_table(self, level): 10 | table = {} 11 | for county in self.filter(level=level): 12 | table[county.name] = { 13 | 'code': county.code, 14 | 'slug': county.slug 15 | } 16 | return table 17 | 18 | def search(self, query): 19 | sql = "select id, level, name, slug from entities " \ 20 | "where " \ 21 | "to_tsvector('"+settings.SEARCH_CONFIG+"',name) @@ plainto_tsquery('"+settings.SEARCH_CONFIG+"',%s) " 22 | return self.raw(sql, [query,]) 23 | 24 | 25 | class Entity(models.Model): 26 | code = models.CharField(max_length=10, db_index=True) 27 | level = models.CharField(max_length=20, db_index=True) 28 | name = models.CharField(max_length=200, db_index=True) 29 | slug = models.SlugField(unique=True) 30 | updated_at = models.DateTimeField(auto_now=True) 31 | created_at = models.DateTimeField(auto_now_add=True) 32 | 33 | objects = EntityManager() 34 | 35 | class Meta: 36 | app_label = "budget_app" 37 | db_table = "entities" 38 | 39 | def save(self): 40 | if not self.id: # Only set the slug when the object is created. 41 | self.slug = slugify(self.name) 42 | super(Entity, self).save() 43 | 44 | def __unicode__(self): 45 | return self.name 46 | -------------------------------------------------------------------------------- /budget_app/models/economic_category.py: -------------------------------------------------------------------------------- 1 | from django.template.defaultfilters import slugify 2 | from django.db import models 3 | 4 | 5 | class EconomicCategoriesManager(models.Manager): 6 | def expenses(self): 7 | return self.filter(expense=True) 8 | 9 | def income(self): 10 | return self.filter(expense=False) 11 | 12 | 13 | class EconomicCategory(models.Model): 14 | budget = models.ForeignKey('Budget') 15 | expense = models.BooleanField() 16 | chapter = models.CharField(max_length=1) 17 | article = models.CharField(max_length=2, null=True) 18 | heading = models.CharField(max_length=9, null=True) # 9 for PGE: '480/xxxxx' with xxxxx entity_id 19 | subheading = models.CharField(max_length=9, null=True) # 6 for Aragon, not used for PGE 20 | description = models.CharField(max_length=500) # 500 for PGE 21 | updated_at = models.DateTimeField(auto_now=True) 22 | created_at = models.DateTimeField(auto_now_add=True) 23 | 24 | objects = EconomicCategoriesManager() 25 | 26 | class Meta: 27 | app_label = "budget_app" 28 | db_table = "economic_categories" 29 | 30 | # Return the 'budget domain' id, used to uniquely identify a category 31 | # in a budget 32 | def uid(self): 33 | if self.article == None: 34 | return self.chapter 35 | elif self.heading == None: 36 | return self.article 37 | elif self.subheading == None: 38 | return self.heading 39 | return self.subheading 40 | 41 | def slug(self): 42 | return slugify(self.description) 43 | 44 | def __unicode__(self): 45 | return self.description 46 | -------------------------------------------------------------------------------- /budget_app/static/javascripts/jslider/jquery.dependClass-0.1.js: -------------------------------------------------------------------------------- 1 | /** 2 | * jquery.dependClass - Attach class based on first class in list of current element 3 | * 4 | * Written by 5 | * Egor Khmelev (hmelyoff@gmail.com) 6 | * 7 | * Licensed under the MIT (MIT-LICENSE.txt). 8 | * 9 | * @author Egor Khmelev 10 | * @version 0.1.0-BETA ($Id$) 11 | * 12 | **/ 13 | 14 | (function($) { 15 | $.baseClass = function(obj){ 16 | obj = $(obj); 17 | return obj.get(0).className.match(/([^ ]+)/)[1]; 18 | }; 19 | 20 | $.fn.addDependClass = function(className, delimiter){ 21 | var options = { 22 | delimiter: delimiter ? delimiter : '-' 23 | } 24 | return this.each(function(){ 25 | var baseClass = $.baseClass(this); 26 | if(baseClass) 27 | $(this).addClass(baseClass + options.delimiter + className); 28 | }); 29 | }; 30 | 31 | $.fn.removeDependClass = function(className, delimiter){ 32 | var options = { 33 | delimiter: delimiter ? delimiter : '-' 34 | } 35 | return this.each(function(){ 36 | var baseClass = $.baseClass(this); 37 | if(baseClass) 38 | $(this).removeClass(baseClass + options.delimiter + className); 39 | }); 40 | }; 41 | 42 | $.fn.toggleDependClass = function(className, delimiter){ 43 | var options = { 44 | delimiter: delimiter ? delimiter : '-' 45 | } 46 | return this.each(function(){ 47 | var baseClass = $.baseClass(this); 48 | if(baseClass) 49 | if($(this).is("." + baseClass + options.delimiter + className)) 50 | $(this).removeClass(baseClass + options.delimiter + className); 51 | else 52 | $(this).addClass(baseClass + options.delimiter + className); 53 | }); 54 | }; 55 | 56 | })(jQuery); -------------------------------------------------------------------------------- /budget_app/management/commands/load_budget.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | from django.core.management.base import BaseCommand 3 | from django.conf import settings 4 | from budget_app.loaders import * 5 | from budget_app.models import Entity 6 | from optparse import make_option 7 | import os.path 8 | 9 | 10 | class Command(BaseCommand): 11 | option_list = BaseCommand.option_list + ( 12 | make_option('--status', 13 | action='store', 14 | dest='status', 15 | help='Set budget status'), 16 | ) 17 | 18 | help = u"Carga el presupuesto del año" 19 | 20 | def handle(self, *args, **options): 21 | if len(args) < 1: 22 | print "Por favor indique el año del presupuesto a cargar." 23 | return 24 | 25 | year = args[0] 26 | status = options['status'] if options['status'] else '' 27 | 28 | level = settings.MAIN_ENTITY_LEVEL if len(args)<2 else args[1] 29 | name = settings.MAIN_ENTITY_NAME if len(args)<3 else args[2] 30 | entity = self._get_entity(level, name) 31 | 32 | path = os.path.join(settings.ROOT_PATH, settings.THEME, 'data', level, year) 33 | 34 | # Import the loader dynamically. See http://stackoverflow.com/questions/301134/dynamic-module-import-in-python 35 | module = __import__(settings.THEME+'.loaders', globals(), locals(), [settings.BUDGET_LOADER]) 36 | loader = module.__dict__[settings.BUDGET_LOADER]() 37 | loader.load(entity, year, path, status) 38 | 39 | def _get_entity(self, level, name): 40 | entity = Entity.objects.filter(level=level, name=name) 41 | if not entity: 42 | raise Exception("Entity (%s/%s) not found" % (level, name)) 43 | return entity[0] 44 | -------------------------------------------------------------------------------- /budget_app/static/javascripts/grid_controls.js: -------------------------------------------------------------------------------- 1 | // Helper methods for handling controls related to data displays 2 | 3 | function setRedrawOnButtonGroupChange(selector, callback) { 4 | $('.btn-group').click(function(event) { 5 | $(event.target).siblings().removeClass('active'); 6 | $(event.target).addClass('active').blur(); 7 | callback(); 8 | event.preventDefault(); 9 | }); 10 | } 11 | 12 | function getActiveButton(selector) { 13 | var button = $(selector+' > .btn.active')[0]; 14 | return button==undefined ? undefined : button.id; 15 | } 16 | 17 | // Activar slider de años (Documentation: http://egorkhmelev.github.com/jslider/) 18 | function initSlider(selector, years, callback, startValue, labels) { 19 | var mostRecentYear = Number(years[years.length-1]); 20 | if ( years.length > 1 ) { 21 | $(selector).val(startValue ? startValue : mostRecentYear); 22 | jQuery(selector).slider({ 23 | from: Number(years[0]), 24 | // JSlider gets stuck if from==to (i.e. only one year), so workaround that #wtf 25 | to: mostRecentYear + 0.01, 26 | step: 1, 27 | format: { format: '####', locale: 'es' }, 28 | skin: "presus", 29 | callback: callback 30 | }); 31 | } else { 32 | $(selector).val(mostRecentYear).hide(); 33 | $(selector).parent().append('

'+mostRecentYear+'

'); 34 | } 35 | } 36 | 37 | function getUIState() { 38 | return { 39 | field: getActiveButton('#btn-field') == 'income' ? 'income' : 'expense', 40 | view: getActiveButton('#btn-field'), 41 | format: $('#select-format').val(), 42 | year: $("#year-selection").val() 43 | } 44 | } 45 | 46 | function sameUIState(a, b) { 47 | return a.view==b.view && a.field==b.field && a.year==b.year && a.format==b.format; 48 | } -------------------------------------------------------------------------------- /budget_app/models/population_stat.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from budget_app.models import Entity, InflationStat 3 | 4 | 5 | class PopulationStatManager(models.Manager): 6 | def get_entity_table(self, entity): 7 | table = {} 8 | stats = self.filter(entity=entity).order_by('year') 9 | for stat in stats: 10 | table[stat.year] = stat.population 11 | 12 | # Now populate the returned table up to the latest year, filling in the gaps. 13 | # We need to do this because population data is often incomplete or not up to date. 14 | last_year = InflationStat.objects.get_last_year() 15 | last_valid_population = None 16 | for year in range(stats[0].year, last_year+3): # Some extra years to avoid breaking every year 17 | if year in table: 18 | last_valid_population = table[year] 19 | table[year] = last_valid_population 20 | return table 21 | 22 | def get_level_table(self, level): 23 | table = {} 24 | for entity in Entity.objects.entities(level): 25 | table[entity.name] = self.get_entity_table(entity) 26 | return table 27 | 28 | def get_last_year(self): 29 | return self.order_by('-year').all()[0].year 30 | 31 | 32 | class PopulationStat(models.Model): 33 | entity = models.ForeignKey('Entity', db_column='entity_id') 34 | year = models.IntegerField() 35 | population = models.IntegerField() 36 | updated_at = models.DateTimeField(auto_now=True) 37 | created_at = models.DateTimeField(auto_now_add=True) 38 | 39 | objects = PopulationStatManager() 40 | 41 | class Meta: 42 | app_label = "budget_app" 43 | db_table = "population_stats" 44 | 45 | def __unicode__(self): 46 | return self.year 47 | -------------------------------------------------------------------------------- /budget_app/views/budgets.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | 3 | from coffin.shortcuts import render_to_response 4 | from django.conf import settings 5 | from budget_app.models import Budget, BudgetBreakdown, BudgetItem 6 | from helpers import * 7 | from properties import * 8 | 9 | 10 | def budgets(request): 11 | # Get request context 12 | c = get_context(request, css_class='body-summary', title='') 13 | 14 | # Retrieve the entity to display 15 | main_entity = get_main_entity(c) 16 | 17 | # Income/expense breakdown 18 | c['functional_breakdown'] = BudgetBreakdown(['policy', 'programme']) 19 | c['economic_breakdown'] = BudgetBreakdown(['article', 'heading']) 20 | c['chapter_breakdown'] = BudgetBreakdown(['chapter']) # Used for indicators 21 | c['include_financial_chapters'] = hasattr(settings, 'INCLUDE_FINANCIAL_CHAPTERS_IN_BREAKDOWNS') and settings.INCLUDE_FINANCIAL_CHAPTERS_IN_BREAKDOWNS 22 | for item in BudgetItem.objects.each_denormalized("e.id = %s", [main_entity.id]): 23 | column_name = year_column_name(item) 24 | c['chapter_breakdown'].add_item(column_name, item) 25 | if c['include_financial_chapters'] or not item.is_financial(): 26 | c['functional_breakdown'].add_item(column_name, item) 27 | c['economic_breakdown'].add_item(column_name, item) 28 | 29 | # Additional data needed by the view 30 | populate_stats(c) 31 | populate_descriptions(c) 32 | populate_budget_statuses(c, main_entity) 33 | populate_years(c, 'functional_breakdown') 34 | 35 | c['income_nodes'] = json.dumps(settings.OVERVIEW_INCOME_NODES) 36 | c['expense_nodes'] = json.dumps(settings.OVERVIEW_EXPENSE_NODES) 37 | 38 | c['draftBudgetYear'] = draftBudgetYear 39 | c['draftBudgetYear_2'] = draftBudgetYear_2 40 | 41 | return render_to_response('budgets/index.html', c) 42 | -------------------------------------------------------------------------------- /budget_app/static/stylesheets/bootstrap/bootstrap.less: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v2.1.0 3 | * 4 | * Copyright 2012 Twitter, Inc 5 | * Licensed under the Apache License v2.0 6 | * http://www.apache.org/licenses/LICENSE-2.0 7 | * 8 | * Designed and built with all the love in the world @twitter by @mdo and @fat. 9 | */ 10 | 11 | // CSS Reset 12 | // @import "reset.less"; 13 | 14 | // Core variables and mixins 15 | @import "variables.less"; // Modify this for custom colors, font-sizes, etc 16 | @import "mixins.less"; 17 | 18 | // Grid system and page structure 19 | // @import "scaffolding.less"; 20 | @import "grid.less"; 21 | @import "layouts.less"; 22 | @import "responsive.less"; 23 | 24 | // Base CSS 25 | /* 26 | @import "type.less"; 27 | @import "code.less"; 28 | @import "forms.less"; 29 | @import "tables.less"; 30 | */ 31 | 32 | // Components: common 33 | /* 34 | @import "sprites.less"; 35 | @import "dropdowns.less"; 36 | @import "wells.less"; 37 | @import "component-animations.less"; 38 | @import "close.less"; 39 | */ 40 | 41 | // Components: Buttons & Alerts 42 | @import "buttons.less"; 43 | @import "button-groups.less"; 44 | @import "alerts.less"; // Note: alerts share common CSS with buttons and thus have styles in buttons.less 45 | 46 | 47 | // Components: Nav 48 | /* 49 | @import "navs.less"; 50 | @import "navbar.less"; 51 | @import "breadcrumbs.less"; 52 | @import "pagination.less"; 53 | @import "pager.less"; 54 | */ 55 | 56 | // Components: Popovers 57 | @import "modals.less"; 58 | @import "tooltip.less"; 59 | @import "popovers.less"; 60 | 61 | // Components: Misc 62 | /* 63 | @import "thumbnails.less"; 64 | @import "labels-badges.less"; 65 | @import "progress-bars.less"; 66 | @import "accordion.less"; 67 | @import "carousel.less"; 68 | @import "hero-unit.less"; 69 | */ 70 | 71 | // Utility classes 72 | // @import "utilities.less"; // Has to be last to override when necessary 73 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 5 | and this project adheres to [Semantic Versioning](http://semver.org/). 6 | 7 | ## [Unreleased] 8 | ### Changed 9 | - New system of error pages like 404, 500, etc. 10 | 11 | ### Added 12 | - New section with the budgets of the University of Zaragoza. 13 | 14 | ## [1.0.5] - 2017-08-17 15 | ### Added 16 | - New link to the page of Junta de Comunidades de Castilla la Mancha. 17 | 18 | 19 | ## [1.0.4] - 2017-05-22 20 | ### Added 21 | - AOD PREL Talend Process (ETL). 22 | - Talend Process documentation. 23 | - Manual of execution for developers. 24 | 25 | 26 | ## [1.0.3] - 2017-05-16 27 | ### Changed 28 | - Inflation information updated at footer section. 29 | 30 | 31 | ## [1.0.2] - 2017-04-11 32 | ### Added 33 | - New items added to the reuse section. 34 | - New counter of reused code for every section. 35 | 36 | 37 | ## [1.0.1] - 2017-04-05 38 | ### Added 39 | - New link to the page of Ayuntamiento de Madrid. 40 | 41 | 42 | ## [1.0.0] - 2017-04-04 43 | ### Added 44 | - New CHANGELOG file necessary to follow the changes of the application in the new versioning system. 45 | 46 | ### Changed 47 | - Initial version of the new versioning system of the application. 48 | 49 | 50 | [Unreleased]: https://github.com/aragonopendata/presupuesto/compare/master...develop 51 | [1.0.5]: https://github.com/aragonopendata/presupuesto/compare/v1.0.4...v1.0.5 52 | [1.0.4]: https://github.com/aragonopendata/presupuesto/compare/v1.0.3...v1.0.4 53 | [1.0.3]: https://github.com/aragonopendata/presupuesto/compare/v1.0.2...v1.0.3 54 | [1.0.2]: https://github.com/aragonopendata/presupuesto/compare/v1.0.1...v1.0.2 55 | [1.0.1]: https://github.com/aragonopendata/presupuesto/compare/v1.0.0...v1.0.1 56 | [1.0.0]: https://github.com/aragonopendata/presupuesto/releases/tag/v1.0.0 -------------------------------------------------------------------------------- /tests/spec/tax_calculator_spec.js: -------------------------------------------------------------------------------- 1 | describe("An average tax-payer", function() { 2 | var calc; 3 | var averageVATRate = 0.17657172; // Calculated by hand with Excel 4 | 5 | beforeEach(function() { 6 | calc = new TaxCalculator(); 7 | }); 8 | 9 | it("pays no income tax if she has no income", function() { 10 | expect(calc.getIncomeTaxPaid(0)).toBe(0); 11 | }); 12 | 13 | it("pays nothing up to a certain minimum (3000 euros deduction)", function() { 14 | expect(calc.getIncomeTaxPaid(6000)).toBe(0); 15 | }); 16 | 17 | it("pays 24.75% in the first bracket", function() { 18 | expect(calc.getIncomeTaxPaid(14000)).toBe(14000 * 0.2475 - 3000); 19 | }); 20 | 21 | it("pays 52% in the last bracket (100K extra)", function() { 22 | var taxBefore = calc.getIncomeTaxPaid(1000000); 23 | var taxAfter = calc.getIncomeTaxPaid(1100000); 24 | expect(taxAfter-taxBefore).toBe(100000 * 0.52); 25 | }); 26 | 27 | it("pays a fixed amount in excise (tobacco, alcohol...)", function() { 28 | expect(calc.getExciseTaxPaid().toFixed(2)).toBe('1062.18'); 29 | }); 30 | 31 | it("pays no VAT if there's no consumption", function() { 32 | expect(calc.getVATPaid(0)).toBe(0); 33 | }); 34 | 35 | it("pays a constant % of the amount saved", function() { 36 | expect(calc.getVATPaid(10000).toFixed(2)).toBe(Number(averageVATRate * 10000).toFixed(2)); 37 | expect(calc.getVATPaid(200).toFixed(2)).toBe(Number(averageVATRate * 200).toFixed(2)); 38 | }); 39 | 40 | it("pays total tax made up of VAT, income tax and excise", function() { 41 | // Results calculated with Excel by hand 42 | expect(calc.getIncomeTaxPaid(26000).toFixed(2)).toBe("3870.38"); 43 | expect(calc.getVATPaid(26000-3870.38).toFixed(2)).toBe(((26000 - 3870.38) * averageVATRate).toFixed(2)); // 3448.38 44 | expect(calc.getTaxPaid(26000).total.toFixed(2)).toBe((3870.38 + calc.getExciseTaxPaid() + 3448.38).toFixed(2)); 45 | }); 46 | }); -------------------------------------------------------------------------------- /budget_app/static/stylesheets/bootstrap/tooltip.less: -------------------------------------------------------------------------------- 1 | // 2 | // Tooltips 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base class 7 | .tooltip { 8 | position: absolute; 9 | z-index: @zindexTooltip; 10 | display: block; 11 | visibility: visible; 12 | padding: 5px; 13 | font-size: 11px; 14 | .opacity(0); 15 | &.in { .opacity(80); } 16 | &.top { margin-top: -3px; } 17 | &.right { margin-left: 3px; } 18 | &.bottom { margin-top: 3px; } 19 | &.left { margin-left: -3px; } 20 | } 21 | 22 | // Wrapper for the tooltip content 23 | .tooltip-inner { 24 | max-width: 200px; 25 | padding: 3px 8px; 26 | color: @tooltipColor; 27 | text-align: center; 28 | text-decoration: none; 29 | background-color: @tooltipBackground; 30 | .border-radius(4px); 31 | } 32 | 33 | // Arrows 34 | .tooltip-arrow { 35 | position: absolute; 36 | width: 0; 37 | height: 0; 38 | border-color: transparent; 39 | border-style: solid; 40 | } 41 | .tooltip { 42 | &.top .tooltip-arrow { 43 | bottom: 0; 44 | left: 50%; 45 | margin-left: -@tooltipArrowWidth; 46 | border-width: @tooltipArrowWidth @tooltipArrowWidth 0; 47 | border-top-color: @tooltipArrowColor; 48 | } 49 | &.right .tooltip-arrow { 50 | top: 50%; 51 | left: 0; 52 | margin-top: -@tooltipArrowWidth; 53 | border-width: @tooltipArrowWidth @tooltipArrowWidth @tooltipArrowWidth 0; 54 | border-right-color: @tooltipArrowColor; 55 | } 56 | &.left .tooltip-arrow { 57 | top: 50%; 58 | right: 0; 59 | margin-top: -@tooltipArrowWidth; 60 | border-width: @tooltipArrowWidth 0 @tooltipArrowWidth @tooltipArrowWidth; 61 | border-left-color: @tooltipArrowColor; 62 | } 63 | &.bottom .tooltip-arrow { 64 | top: 0; 65 | left: 50%; 66 | margin-left: -@tooltipArrowWidth; 67 | border-width: 0 @tooltipArrowWidth @tooltipArrowWidth; 68 | border-bottom-color: @tooltipArrowColor; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /budget_app/loaders/stat_loader.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | from budget_app.models import InflationStat, PopulationStat, Entity 3 | import csv 4 | import re 5 | import os.path 6 | 7 | 8 | class StatLoader: 9 | def load(self, path): 10 | self._delete_all() 11 | self.load_inflation(os.path.join(path, 'inflacion.csv')) 12 | self.load_population(os.path.join(path, 'poblacion.csv')) 13 | 14 | def load_inflation(self, filename): 15 | print "Cargando estadísticas oficiales de inflación de %s..." % filename 16 | reader = csv.reader(open(filename, 'rb')) 17 | for index, line in enumerate(reader): 18 | if re.match("^#", line[0]): # Ignore comments 19 | continue 20 | 21 | print " Cargando inflación para el año %s..." % line[0] 22 | stat = InflationStat(year=line[0], inflation=line[1]) 23 | stat.save() 24 | 25 | def load_population(self, filename): 26 | print "Cargando estadísticas oficiales de población de %s..." % filename 27 | reader = csv.reader(open(filename, 'rb')) 28 | for index, line in enumerate(reader): 29 | if re.match("^#", line[0]): # Ignore comments 30 | continue 31 | 32 | code = line[0] 33 | name = line[1] 34 | year = line[2] 35 | population = line[3] 36 | 37 | print " Cargando población para %s (%s)..." % (name, year) 38 | entity = self._get_entity(code, name) 39 | stat = PopulationStat(entity=entity, year=year, population=population) 40 | stat.save() 41 | 42 | def _delete_all(self): 43 | InflationStat.objects.all().delete() 44 | PopulationStat.objects.all().delete() 45 | 46 | def _get_entity(self, code, name): 47 | entity = Entity.objects.filter(code=code) 48 | if not entity: 49 | raise Exception("Entity (%s/%s) not found" % (code, name)) 50 | return entity[0] 51 | -------------------------------------------------------------------------------- /django_jasmine/views.py: -------------------------------------------------------------------------------- 1 | import logging 2 | import os 3 | 4 | import django 5 | from django.conf import settings 6 | from django.shortcuts import render_to_response 7 | from django.template import RequestContext 8 | 9 | if django.VERSION >= (1, 5): 10 | import json as simplejson 11 | else: 12 | from django.utils import simplejson 13 | 14 | logger = logging.getLogger("django_jasmine") 15 | 16 | 17 | def run_tests(request, path): 18 | """Run the jasmine tests and render index.html""" 19 | root = os.path.join(settings.JASMINE_TEST_DIRECTORY, path) 20 | # Get all files in spec dir and subdirs 21 | all_files = [] 22 | for curpath, dirs, files in os.walk(os.path.join(root, "spec")): 23 | for name in files: 24 | if not name.startswith("."): 25 | "We want to avoid .file.js.swp and co" 26 | curpath = curpath.replace(os.path.join(root, "spec"), "") 27 | all_files.append(os.path.join(curpath, name)) 28 | 29 | suite = {} 30 | 31 | # defaults 32 | suite['js_files'] = [] 33 | suite['static_files'] = [] 34 | 35 | # load files.json if present 36 | if os.path.exists(os.path.join(root, "files.json")): 37 | file = open(os.path.join(root, 'files.json'), 'r') 38 | json = file.read() 39 | try: 40 | json = simplejson.loads(json) 41 | except ValueError: 42 | logger.info("You might have a syntax error in your files.json, " 43 | "like a surplus comma") 44 | # Trick to call back the django handler500, couldn't find a way to 45 | # customize the Exception Type field in the debug Traceback 46 | json = simplejson.loads(json) 47 | suite.update(json) 48 | 49 | data = { 50 | 'files': [path + file for file in all_files if file.endswith('js')], 51 | 'suite': suite, 52 | } 53 | 54 | return render_to_response('jasmine/index.html', data, 55 | context_instance=RequestContext(request)) 56 | -------------------------------------------------------------------------------- /django_jasmine/templates/jasmine/base.html: -------------------------------------------------------------------------------- 1 | {% load url from future %} 2 | 3 | 4 | 5 | 6 | Jasmine Spec Runner 7 | 9 | 10 | {# core files #} 11 | 12 | 13 | 14 | 15 | {% block jasmine_preload %}{% endblock %} 16 | 17 | {# source files #} 18 | {% for url in suite.js_files %} 19 | 20 | {% endfor %} 21 | 22 | {# static files #} 23 | {% for url in suite.static_files %} 24 | 25 | {% endfor %} 26 | 27 | {# spec files #} 28 | {% for file in files %} 29 | 30 | {% endfor %} 31 | 32 | 33 | 34 | 35 | 68 | 69 | 70 | 71 | -------------------------------------------------------------------------------- /budget_app/static/javascripts/bootstrap/bootstrap-transition.js: -------------------------------------------------------------------------------- 1 | /* =================================================== 2 | * bootstrap-transition.js v2.1.0 3 | * http://twitter.github.com/bootstrap/javascript.html#transitions 4 | * =================================================== 5 | * Copyright 2012 Twitter, Inc. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * ========================================================== */ 19 | 20 | 21 | !function ($) { 22 | 23 | $(function () { 24 | 25 | "use strict"; // jshint ;_; 26 | 27 | 28 | /* CSS TRANSITION SUPPORT (http://www.modernizr.com/) 29 | * ======================================================= */ 30 | 31 | $.support.transition = (function () { 32 | 33 | var transitionEnd = (function () { 34 | 35 | var el = document.createElement('bootstrap') 36 | , transEndEventNames = { 37 | 'WebkitTransition' : 'webkitTransitionEnd' 38 | , 'MozTransition' : 'transitionend' 39 | , 'OTransition' : 'oTransitionEnd otransitionend' 40 | , 'transition' : 'transitionend' 41 | } 42 | , name 43 | 44 | for (name in transEndEventNames){ 45 | if (el.style[name] !== undefined) { 46 | return transEndEventNames[name] 47 | } 48 | } 49 | 50 | }()) 51 | 52 | return transitionEnd && { 53 | end: transitionEnd 54 | } 55 | 56 | })() 57 | 58 | }) 59 | 60 | }(window.jQuery); -------------------------------------------------------------------------------- /budget_app/static/stylesheets/bootstrap/labels-badges.less: -------------------------------------------------------------------------------- 1 | // 2 | // Labels and badges 3 | // -------------------------------------------------- 4 | 5 | 6 | // Base classes 7 | .label, 8 | .badge { 9 | font-size: @baseFontSize * .846; 10 | font-weight: bold; 11 | line-height: 14px; // ensure proper line-height if floated 12 | color: @white; 13 | vertical-align: baseline; 14 | white-space: nowrap; 15 | text-shadow: 0 -1px 0 rgba(0,0,0,.25); 16 | background-color: @grayLight; 17 | } 18 | // Set unique padding and border-radii 19 | .label { 20 | padding: 1px 4px 2px; 21 | .border-radius(3px); 22 | } 23 | .badge { 24 | padding: 1px 9px 2px; 25 | .border-radius(9px); 26 | } 27 | 28 | // Hover state, but only for links 29 | a { 30 | &.label:hover, 31 | &.badge:hover { 32 | color: @white; 33 | text-decoration: none; 34 | cursor: pointer; 35 | } 36 | } 37 | 38 | // Colors 39 | // Only give background-color difference to links (and to simplify, we don't qualifty with `a` but [href] attribute) 40 | .label, 41 | .badge { 42 | // Important (red) 43 | &-important { background-color: @errorText; } 44 | &-important[href] { background-color: darken(@errorText, 10%); } 45 | // Warnings (orange) 46 | &-warning { background-color: @orange; } 47 | &-warning[href] { background-color: darken(@orange, 10%); } 48 | // Success (green) 49 | &-success { background-color: @successText; } 50 | &-success[href] { background-color: darken(@successText, 10%); } 51 | // Info (turquoise) 52 | &-info { background-color: @infoText; } 53 | &-info[href] { background-color: darken(@infoText, 10%); } 54 | // Inverse (black) 55 | &-inverse { background-color: @grayDark; } 56 | &-inverse[href] { background-color: darken(@grayDark, 10%); } 57 | } 58 | 59 | // Quick fix for labels/badges in buttons 60 | .btn { 61 | .label, 62 | .badge { 63 | position: relative; 64 | top: -1px; 65 | } 66 | } 67 | .btn-mini { 68 | .label, 69 | .badge { 70 | top: 0; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /budget_app/static/javascripts/vis/budget-summary.js: -------------------------------------------------------------------------------- 1 | function BudgetSummary(selector, breakdown, areaNames, colorScale, field, year) { 2 | 3 | // Group breakdown by area 4 | var areaAmounts = {}; 5 | var totalAmount = 0; 6 | for (var i in breakdown.sub) { 7 | var policyAmount = breakdown.sub[i][field][year]; 8 | if ( policyAmount != undefined ) { 9 | var area = i[0]; 10 | areaAmounts[area] = (areaAmounts[area]||0) + policyAmount; 11 | totalAmount = totalAmount + policyAmount; 12 | } 13 | } 14 | 15 | // Sort areas 16 | var existingAreas = []; 17 | for (var area in areaAmounts) existingAreas.push(area); 18 | existingAreas.sort(function(a, b) { return areaAmounts[b] - areaAmounts[a] }); 19 | 20 | // Render 21 | $(selector).empty(); 22 | $(selector).append( ''+ 23 | ''+ 24 | ''+ 25 | '
'); 26 | var topRow = $(selector+" tr.budget-summary-top"); 27 | var bottomRow = $(selector+" tr.budget-summary-bottom"); 28 | 29 | for (var i = 0; i < existingAreas.length; i++) { 30 | var area = existingAreas[i]; 31 | var percentage = 100 * areaAmounts[area] / totalAmount; 32 | var label = (percentage > 4 ) ? (areaNames[area]+' ('+formatDecimal(percentage, 1)+'%)') : ''; 33 | if ( i%2 == 0 ) { 34 | topRow.append(''+label+''); 37 | bottomRow.append(''); 38 | } else { 39 | topRow.append(''); 42 | bottomRow.append(''+label+''); 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /budget_app/static/stylesheets/alerts.less: -------------------------------------------------------------------------------- 1 | /* Función para crear alertas (note, example, ok, error) */ 2 | .alert (@bgcolor: red, @bordercolor: black, @color: white, @linkcolor: blue, @img: none) { 3 | @shadow-alert: 4 | 0 1px 0 fadeout(@white, 20%) inset, 5 | 0 0 .5em darken(@bgcolor, 15%) inset; 6 | .box-shadow(@shadow-alert); 7 | background-color: @bgcolor; 8 | background-position: .5em .75em; 9 | background-repeat: no-repeat; 10 | border: 1px solid @bordercolor; 11 | color: darken(@color, 20%)/* Oscurecemos el color un poco para obtener más contraste de cara a la accesibilidad */; 12 | line-height: 1.25em; 13 | text-shadow: 0 1px 0 rgba(255,255,255,.75); 14 | /* Definir el color de los enlaces que van dentro */ 15 | a { 16 | color: @linkcolor; 17 | border-bottom: 1px dotted @linkcolor; 18 | font-weight: 700; 19 | } 20 | a:hover { 21 | color: darken(@linkcolor, 10%); 22 | border-bottom: none; 23 | } 24 | .title {background: url(@img) no-repeat 0 center; display: inline-block; .ie7-inline-block; font-size: 1em; font-weight: 700; margin: 0 0 .5em; padding: 0 0 0 21px;} 25 | ul, ol {margin-bottom: .5em;} 26 | li {margin: 0;} 27 | p {margin-bottom: .5em;} 28 | .btn {border-bottom: none; color: @white; text-shadow: none;} 29 | .btn span {padding-bottom: .125em; padding-top: .125em;} 30 | } 31 | 32 | .alert { 33 | font-size: .875em; 34 | margin: 0 0 1em; 35 | padding: .75em 1em; 36 | text-shadow: 1px 1px 1px @white; 37 | vertical-align: top; 38 | .rounded(.25em); 39 | } 40 | 41 | .alert.inline-block {display: inline-block; .ie7-inline-block;} 42 | 43 | .note {.alert(@warningBackground, @warningBorder, @warningText, spin(darken(@warningText, 10%), 10%), '../assets/sticky-note-text.png');} 44 | .example {.alert(@infoBackground, @infoBorder, @infoText, spin(darken(@infoText, 10%), 10%), '../assets/ui-address-bar-green.png');} 45 | .ok {.alert(@successBackground, @successBorder, @successText, spin(darken(@successText, 10%), 10%), '../assets/tick.png');} 46 | .error {.alert(@errorBackground, @errorBorder, @errorText, spin(darken(@errorText, 10%), 10%), '../assets/exclamation-red.png');} -------------------------------------------------------------------------------- /etls/presupuestos_locales/doc/carga_datos_presupuestos.md: -------------------------------------------------------------------------------- 1 | # Carga de datos de presupuestos 2 | 3 | ## Descripción 4 | La aplicación de presupuestos de Aragón utiliza unos scripts llamados “loaders” para cargar los datos de presupuestos en la base de datos. 5 | Estos loaders son ejecutados mediante comandos desde consola, por lo que primero será necesario colocar los ficheros en sus directorios correspondientes. 6 | 7 | ## Tareas 8 | 1. Creación de los directorios 9 | a. Se deberá crear en la siguiente ruta, un directorio que tenga por nombre el año del presupuesto de los municipios a cargar: 10 | `/data/apps/presupuesto/theme-aragon/data/municipio/` 11 | b. Se deberá crear en la siguiente ruta, un directorio que tenga por nombre el año del presupuesto de las comarcas a cargar: 12 | `/data/apps/presupuesto/theme-aragon/data/comarca/` 13 | 14 | 2. Subida de ficheros de carga 15 | a. Subir a cada uno de los directorios creados anteriormente los ficheros correspondientes a municipio y comarca respectivamente: 16 | i. `clasificacion_economica.csv` 17 | ii. `clasificacion_funcional.csv` 18 | iii. `no_xbrl.csv` 19 | > Los ficheros proceden de la ejecución de los jobs Talend situados en la máquina de salto. 20 | > Consultar credenciales de las máquinas en: '[AOD]_Passwords.xlsx', pestaña 'Otros', 'MÁQUINA DE SALTO PRESUPUESTOS (1 y 2)'. 21 | 3. Carga de presupuestos de municipios y comarcas 22 | a. Ir a la ruta de la aplicación: 23 | ``` 24 | cd /data/apps/presupuesto/ 25 | ``` 26 | b. Ejecutar las siguientes instrucciones: 27 | ``` 28 | python manage.py load_budget_data municipio [año] 29 | ``` 30 | y 31 | ``` 32 | python manage.py load_budget_data comarca [año] 33 | ``` 34 | > Donde `[año]` corresponde al nombre del directorio que creamos para municipios. 35 | 36 | 4. Borrar caché 37 | Tal vez sea necesario borrar el caché de la página para que se actualicen los resultados: 38 | ``` 39 | rm -r /tmp/presupuestos/ 40 | ``` 41 | 42 | -------------------------------------------------------------------------------- /templates/shared/data_sources.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

{{ _('Fuentes de datos') }}

4 |
5 |
6 | 11 |
12 |

{{ _('Nota') }}

13 |
    14 |
  • Aclaración sobre Gastos Financieros: En las páginas principales de Gastos Financieros e Ingresos y Gastos en el Gobierno de Aragón no se reflejan los gastos financieros (Capítulo 8 Activos Financieros y 9 Pasivos Financieros) porque el techo de gasto se acuerda sin estos gastos financieros. Para conocer con detalle estos gastos se debe acceder a cada partida.
  • 15 |
  • Total Ingresos: Se trata de los derechos reconocidos netos, que es la diferencia entre los derechos reconocidos brutos y los derechos anulados y cancelados.
  • 16 |
  • Los ingresos y gastos mostrados incluyen tanto a la Administración General de la Diputación General de Aragón como a los Organismos Autónomos IASS, INAEM, SALUD, IAM, IAJU y a las Entidades de Derecho Público AST, IAA, INAGA, ACES, Banco de Sangre, CITA e IACS.
  • 17 |
  • El ajuste de la inflación se realiza a 1 de enero de {{ last_inflation_year }}. La tasa de inflación anual es la media de la tasa interanual de los doce meses del año.
  • 18 |
  • Los presupuestos comarcales y municipales se actualizan con datos trimestrales a fechas 1 de enero, abril, julio y octubre.
  • 19 |
20 |
21 |
22 |
23 | -------------------------------------------------------------------------------- /budget_app/views/towns.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | from counties import counties_show, counties_compare 3 | from entities import entities_index, entities_show, entities_compare, entities_show_article, entities_show_policy 4 | from helpers import * 5 | import json 6 | 7 | 8 | def towns(request, render_callback=None): 9 | c = get_context(request, css_class='body-entities', title='Municipios') 10 | return entities_index(request, c, 'municipio', render_callback) 11 | 12 | 13 | def towns_show(request, town_slug, render_callback=None): 14 | town = _get_town(town_slug) 15 | return entities_show(request, _get_town_context(request, town), town, render_callback) 16 | 17 | 18 | def towns_show_income(request, town_slug, id, render_callback=None): 19 | town = _get_town(town_slug) 20 | c = _get_town_context(request, town) 21 | return entities_show_article(request, c, town, id, '', 'income', render_callback) 22 | 23 | 24 | def towns_show_expense(request, town_slug, id, render_callback=None): 25 | town = _get_town(town_slug) 26 | c = _get_town_context(request, town) 27 | return entities_show_article(request, c, town, id, '', 'expense', render_callback) 28 | 29 | 30 | def towns_show_fexpense(request, town_slug, id, render_callback=None): 31 | town = _get_town(town_slug) 32 | c = _get_town_context(request, town) 33 | return entities_show_policy(request, c, town, id, '', render_callback) 34 | 35 | 36 | def towns_compare(request, town_left_slug, town_right_slug): 37 | town_left = _get_town(town_left_slug) 38 | town_right = _get_town(town_right_slug) 39 | c = get_context(request, 40 | css_class='body-entities', 41 | title='Comparativa '+town_left.name+'/'+town_right.name+' - Municipios') 42 | return entities_compare(request, c, town_left, town_right) 43 | 44 | 45 | # Retrieve the entity to display from the given slug 46 | def _get_town(slug): 47 | return Entity.objects.get(level='municipio', slug=slug) 48 | 49 | 50 | # Get request context for a town page 51 | def _get_town_context(request, town): 52 | c = get_context(request, css_class='body-entities', title=town.name +' - Municipios') 53 | populate_entities(c, town.level) 54 | return c 55 | -------------------------------------------------------------------------------- /budget_app/static/stylesheets/jslider/jslider.less: -------------------------------------------------------------------------------- 1 | .jslider .jslider-bg i, .jslider .jslider-pointer {background: url(../assets/jslider/jslider.png) no-repeat 0 0;} 2 | .jslider {display: block; font-weight: 600; width: 100%; height: 1em; position: relative; top: .6em;} 3 | .jslider table {width: 100%; border-collapse: collapse; border: 0;} 4 | .jslider td, .jslider th {padding: 0; vertical-align: top; text-align: left; border: 0;} 5 | 6 | .jslider table, 7 | .jslider table tr, 8 | .jslider table tr td 9 | {width: 100%; vertical-align: top;} 10 | 11 | .jslider .jslider-bg {position: relative;} 12 | .jslider .jslider-bg i {height: 5px; position: absolute; font-size: 0; top: 0;} 13 | .jslider .jslider-bg .l {width: 50%; background-position: 0 0; left: 0;} 14 | .jslider .jslider-bg .r {width: 50%; left: 50%; background-position: right 0;} 15 | .jslider .jslider-bg .v {position: absolute; width: 60%; left: 20%; top: 0; height: 5px; background-position: 0 -20px;} 16 | .jslider .jslider-pointer {width: 13px; height: 15px; background-position: 0 -40px; position: absolute; left: 20%; top: -4px; margin-left: -6px; cursor: pointer; cursor: hand;} 17 | .jslider .jslider-pointer-hover {background-position: -20px -40px;} 18 | .jslider .jslider-pointer-to {left: 80%;} 19 | .jslider .jslider-label {font-size: .75em; line-height: 12px; color: black; .opacity(.4); white-space: nowrap; position: absolute; top: -1.5em; left: 0px;} 20 | .jslider .jslider-label-to {left: auto; right: 0;} 21 | .jslider .jslider-value {font-weight: 800; white-space: nowrap; padding: .25em; position: absolute; top: -2.25em; left: 20%; background: @black20; color: @white; line-height: 1.125; .border-radius(.25em);} 22 | .jslider .jslider-value-to {left: 80%;} 23 | .jslider .jslider-label small, .jslider .jslider-value small {position: relative; top: -0.4em;} 24 | .jslider .jslider-scale {position: relative; top: 9px;} 25 | .jslider .jslider-scale span {position: absolute; height: 5px; border-left: 1px solid #999;} 26 | .jslider .jslider-scale ins {font-size: .75em; text-decoration: none; position: absolute; left: 0; top: 5px; color: #999;} 27 | 28 | .jslider-single .jslider-pointer-to, 29 | .jslider-single .jslider-value-to, 30 | .jslider-single .jslider-bg .v, 31 | .jslider-limitless .jslider-label 32 | {display: none;} -------------------------------------------------------------------------------- /theme-aragon/data/inspect.rb: -------------------------------------------------------------------------------- 1 | # The files we have are quite shitty, and IDs are very noisy 2 | # - No flag to indicate expense vs income; but we know they are grouped: first income, then expense 3 | # - Several IDs are duplicated, because null fields are populated with '0' 4 | # 5 | # Because of the latter we can't go through the files and check that id->description is constant 6 | # for all IDs across all the files, since there are duplicates. 7 | # 8 | # What we can do is check (hold on) that description->id is constant. Apart from the fact that 9 | # a couple of descriptions appear both on the income and expense side. 10 | # 11 | # Then we can generate a mapping table. Run: 12 | # $ ruby inspect.rb > ../clasificacion_economica.csv 13 | # And then fix it manually... :/ 14 | 15 | require 'CSV' 16 | 17 | descriptions = {} 18 | current_mode = nil 19 | 20 | # Go through all the budget files, checking data is (kind of) consistent, as explained above 21 | Dir['*-presupuesto-*.csv'].each do |filename| 22 | CSV.foreach(filename) do |row| 23 | # Read the item 24 | chapter = row[0] 25 | article = row[1] 26 | concept = row[2] 27 | subconcept = row[3] 28 | description = row[4].strip 29 | 30 | # We need to differentiate expense and income 31 | if description == 'EconomicaIngresos' 32 | current_mode = 'I' 33 | next 34 | elsif description == 'EconomicaGastos' 35 | current_mode = 'G' 36 | next 37 | end 38 | id = "#{current_mode}.#{chapter}.#{article}.#{concept}.#{subconcept}" 39 | description = "#{current_mode}/#{description}" # Couple of descriptions appear on both sides 40 | 41 | # Check if it matches what we had already 42 | if descriptions[description] == nil 43 | descriptions[description] = [id, filename] 44 | else 45 | old_id = descriptions[description][0] 46 | old_filename = descriptions[description][1] 47 | if old_id != id 48 | puts "CONFLICT:" 49 | puts " Had [#{old_filename}] #{old_id} -> '#{description}'" 50 | puts " Got [#{filename}] #{id} -> '#{description}'" 51 | end 52 | end 53 | end 54 | end 55 | 56 | # Print the result 57 | descriptions.each do |description, metadata| 58 | id = metadata[0] 59 | puts [description.split('/'), id.split('.'), metadata[1]].flatten.to_csv 60 | end -------------------------------------------------------------------------------- /theme-aragon/static/sitemaps/VisionGlobal.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | http://presupuesto.aragon.es/articulos/i/10/sobre-la-renta 9 | yearly 10 | 11 | 12 | http://presupuesto.aragon.es/articulos/i/21/impuesto-sobre-el-valor-a%C3%B1adido-(iva) 13 | yearly 14 | 15 | 16 | http://presupuesto.aragon.es/articulos/i/22/sobre-consumos-especificos 17 | yearly 18 | 19 | 20 | http://presupuesto.aragon.es/articulos/i/40/de-la-administracion-del-estado-(cap4) 21 | yearly 22 | 23 | 24 | http://presupuesto.aragon.es/articulos/i/49/fondos-europeos-(cap4) 25 | yearly 26 | 27 | 28 | http://presupuesto.aragon.es/politicas#view=income 29 | yearly 30 | 31 | 32 | http://presupuesto.aragon.es/articulos/i/11/sobre-el-capital 33 | yearly 34 | 35 | 36 | http://presupuesto.aragon.es/politicas/241/sanidad 37 | yearly 38 | 39 | 40 | http://presupuesto.aragon.es/politicas/242/educacion 41 | yearly 42 | 43 | 44 | http://presupuesto.aragon.es/politicas/371/agricultura-y-ganaderia 45 | yearly 46 | 47 | 48 | http://presupuesto.aragon.es/politicas/231/seguridad-y-proteccion-social 49 | yearly 50 | 51 | 52 | http://presupuesto.aragon.es/politicas 53 | yearly 54 | 55 | 56 | http://presupuesto.aragon.es/politicas/001/deuda-publica 57 | yearly 58 | 59 | 60 | http://presupuesto.aragon.es/politicas/351/infraestructuras-basicas-y-del-transporte 61 | yearly 62 | 63 | -------------------------------------------------------------------------------- /templates/budgets/budgets_update_indicators.html: -------------------------------------------------------------------------------- 1 | // Update global budget indicators 2 | function updateIndicators(uiState) { 3 | function getSum(breakdown, ids, field, year) { 4 | // TODO: Take into account whether actual spending figures exists (and whether they cover whole year) 5 | return _.reduce(ids, function(sum, id) { return sum + (breakdown.sub[id][field][year]||0); }, 0); 6 | } 7 | 8 | function format(amount) { 9 | return formatAmount(adjustInflation(amount, stats, uiState.year)); 10 | } 11 | 12 | var gross_savings = getSum(chapterBreakdown, _.range(1, 6), 'income', uiState.year) - 13 | getSum(chapterBreakdown, _.range(1, 6), 'expense', uiState.year); 14 | var net_savings = gross_savings - getSum(chapterBreakdown, '9', 'expense', uiState.year); 15 | var funding_capacity = gross_savings + 16 | getSum(chapterBreakdown, [6, 7], 'income', uiState.year) - 17 | getSum(chapterBreakdown, [6, 7], 'expense', uiState.year); 18 | 19 | var total_incomes_budgeted = economicBreakdown.income[uiState.year]; 20 | var total_incomes_executed = economicBreakdown.income['actual_'+uiState.year]; 21 | var total_expenses_budgeted = economicBreakdown.expense[uiState.year]; 22 | var total_expenses_executed = economicBreakdown.expense['actual_'+uiState.year]; 23 | 24 | // Check if executed data is available 25 | if (total_incomes_executed){ 26 | $('#totals_panel .secondary-label, #total-incomes-budgeted, #total-expenses-budgeted').show(); 27 | $('#total-incomes-budgeted').text(format(total_incomes_budgeted)); 28 | $('#total-incomes-executed').text(format(total_incomes_executed)); 29 | $('#total-expenses-budgeted').text(format(total_expenses_budgeted)); 30 | $('#total-expenses-executed').text(format(total_expenses_executed)); 31 | } 32 | else{ 33 | $('#total-incomes-executed').text(format(total_incomes_budgeted)); 34 | $('#total-expenses-executed').text(format(total_expenses_budgeted)); 35 | $('#totals_panel .secondary-label, #total-incomes-budgeted, #total-expenses-budgeted').hide(); 36 | } 37 | 38 | $('#indicators-year, #totals-year').text(uiState.year); 39 | $('#total-gross-savings').text(format(gross_savings)); 40 | $('#total-net-savings').text(format(net_savings)); 41 | $('#total-funding-capacity').text(format(funding_capacity)); 42 | } -------------------------------------------------------------------------------- /budget_app/views/counties.py: -------------------------------------------------------------------------------- 1 | # -*- coding: UTF-8 -*- 2 | from coffin.shortcuts import render_to_response 3 | from budget_app.models import BudgetBreakdown, Entity 4 | from entities import entities_index, entities_show, entities_compare, entities_show_article, entities_show_policy 5 | from helpers import * 6 | 7 | 8 | def counties(request, render_callback=None): 9 | c = get_context(request, css_class='body-counties', title='Comarcas') 10 | return entities_index(request, c, 'comarca', render_callback) 11 | 12 | 13 | def counties_show(request, county_slug, render_callback=None): 14 | county = _get_county(county_slug) 15 | return entities_show(request, _get_county_context(request, county), county, render_callback) 16 | 17 | 18 | def counties_show_income(request, county_slug, id, render_callback=None): 19 | county = _get_county(county_slug) 20 | c = _get_county_context(request, county) 21 | return entities_show_article(request, c, county, id, '', 'income', render_callback) 22 | 23 | 24 | def counties_show_expense(request, county_slug, id, render_callback=None): 25 | county = _get_county(county_slug) 26 | c = _get_county_context(request, county) 27 | return entities_show_article(request, c, county, id, '', 'expense', render_callback) 28 | 29 | 30 | def counties_show_fexpense(request, county_slug, id, render_callback=None): 31 | county = _get_county(county_slug) 32 | c = _get_county_context(request, county) 33 | return entities_show_policy(request, c, county, id, '', render_callback) 34 | 35 | 36 | def counties_compare(request, county_left_slug, county_right_slug): 37 | county_left = _get_county(county_left_slug) 38 | county_right = _get_county(county_right_slug) 39 | c = get_context(request, 40 | css_class='body-counties', 41 | title='Comparativa '+county_left.name+'/'+county_right.name+' - Comarcas') 42 | return entities_compare(request, c, county_left, county_right) 43 | 44 | 45 | # Retrieve the entity to display from the given slug 46 | def _get_county(slug): 47 | return Entity.objects.get(level='comarca', slug=slug) 48 | 49 | 50 | # Get request context for a county page 51 | def _get_county_context(request, county): 52 | c = get_context(request, css_class='body-entities', title=county.name +' - Comarcas') 53 | populate_entities(c, county.level) 54 | return c 55 | -------------------------------------------------------------------------------- /budget_app/static/stylesheets/grid.less: -------------------------------------------------------------------------------- 1 | // Apply to an element to show grid elements 2 | .show-grid .row-fluid > [class*="span"], 3 | .show-grid label[class*="span"], .show-grid input[class*="span"], .show-grid select[class*="span"] 4 | {background: rgba(255, 128, 0, .15) url(../assets/show-grid.png); margin-bottom: 1em;} 5 | 6 | // Show the pads 7 | .show-grid [class*="pad-"] {background: rgba(255, 255, 0, .05);} 8 | 9 | 10 | 11 | // Block grids 12 | 13 | // For IE7/8 compatibility block-fluid items need to be 14 | // the same height. You can optionally uncomment the 15 | // lines below to support arbitrary height, but know 16 | // that IE7/8 do not support :nth-child. Si se mete 17 | // Selectivzr (http://selectivizr.com/) permite soportarlo. 18 | // Si no queremos usar :nth-child + Selectivzr podemos 19 | // usar la clase .row-clear en los items impares para 20 | // comenzar con una nueva fila. 21 | 22 | 23 | // Ejemplo de HTML: 24 | //
    25 | //
  • Item1
  • Item2
  • Item3
  • Item4
  • 26 | //
  • Item5
  • Item6
  • Item7
  • Item8
  • 27 | //
  • Item9
  • 28 | //
29 | 30 | .block-fluid {.clearfix;} 31 | .block-fluid>* {float: left; padding: 0;} 32 | 33 | // Clase auxiliar a aplicar cuando queremos que un elemento sea el primero de la fila. 34 | // No es necesario usar si se carga Selectivzr (http://selectivizr.com/) y se emplean las clases referidas a las columnas impares. 35 | .block-fluid .row-clear {clear: left;} 36 | 37 | .block-fluid.row-items2 {margin-left: -4%;} 38 | .block-fluid.row-items2>* {margin-left: 4%; width: 46%;} 39 | .block-fluid.row-items2>*:nth-child(2n+1) {clear: left;} 40 | 41 | .block-fluid.row-items3 {margin-left: -2%;} 42 | .block-fluid.row-items3>* {margin-left: 2%; width: 31.3%;} 43 | .block-fluid.row-items3>*:nth-child(3n+1) {clear: left;} 44 | 45 | .block-fluid.row-items4 {margin-left: -2%;} 46 | .block-fluid.row-items4>* {margin-left: 2%; width: 23%;} 47 | .block-fluid.row-items4>*:nth-child(4n+1) {clear: left;} 48 | 49 | .block-fluid.row-items5 {margin-left: -1.5%;} 50 | .block-fluid.row-items5>* {margin-left: 1.5%; width: 18.5%;} 51 | .block-fluid.row-items5>*:nth-child(5n+1) {clear: left;} 52 | 53 | @media (max-width: 48em) { 54 | .block-fluid[class*="row-items"] {margin-left: 0;} 55 | .block-fluid[class*="row-items"] > * {float: none; margin-left: 0; width: auto;} 56 | .block-fluid .row-clear {clear: none;} 57 | } -------------------------------------------------------------------------------- /budget_app/models/functional_category.py: -------------------------------------------------------------------------------- 1 | from django.template.defaultfilters import slugify 2 | from django.db import models 3 | from django.conf import settings 4 | 5 | 6 | class FunctionalCategoriesManager(models.Manager): 7 | def programmes(self): 8 | return self.filter(programme__isnull=False) 9 | 10 | def _search(self, query, budget, conditions): 11 | sql = "select * from functional_categories " \ 12 | "where " + conditions + " and " \ 13 | "to_tsvector('"+settings.SEARCH_CONFIG+"',description) @@ plainto_tsquery('"+settings.SEARCH_CONFIG+"',%s)" 14 | 15 | if budget: 16 | sql += " and budget_id='%s'" % budget.id 17 | 18 | return self.raw(sql, (query, )) 19 | 20 | def search_policies(self, query, budget=None): 21 | return self._search(query, budget, "policy is not null and function is null and policy<>'XX'") 22 | 23 | def search_programmes(self, query, budget=None): 24 | return self._search(query, budget, "programme is not null and programme<>'XXXX'") 25 | 26 | 27 | class FunctionalCategory(models.Model): 28 | budget = models.ForeignKey('Budget') 29 | area = models.CharField(max_length=1) 30 | policy = models.CharField(max_length=3, null=True) 31 | function = models.CharField(max_length=5, null=True) 32 | programme = models.CharField(max_length=5, null=True) 33 | description = models.CharField(max_length=200) 34 | updated_at = models.DateTimeField(auto_now=True) 35 | created_at = models.DateTimeField(auto_now_add=True) 36 | 37 | objects = FunctionalCategoriesManager() 38 | 39 | class Meta: 40 | app_label = "budget_app" 41 | db_table = "functional_categories" 42 | 43 | # Returns the 'budget domain' id, used to uniquely identify a category in 44 | # a given budget. 45 | # Note: this implementation garantees uniqueness only if children ids are 46 | # 'fully scoped' (for lack of a better name). I.e. a function id could 47 | # be '4.1', for example, meaning that it belongs to area '4'. 48 | def uid(self): 49 | if self.policy == None: 50 | return self.area 51 | elif self.function == None: 52 | return self.policy 53 | elif self.programme == None: 54 | return self.function 55 | return self.programme 56 | 57 | def slug(self): 58 | return slugify(self.description) 59 | 60 | def __unicode__(self): 61 | return self.description 62 | -------------------------------------------------------------------------------- /theme-aragon/scripts/carga_nuevos_datos.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Configuration 4 | SOURCE_DATA=/data/ckan_ficheros/opendata/general 5 | APP=/apps/presupuestos-aragon 6 | CACHE=/tmp/presupuesto 7 | 8 | # Development 9 | # SOURCE_DATA=/Users/David/src/presupuestos-aragon/source_data 10 | # APP=/Users/David/src/presupuestos-aragon 11 | # CACHE=/Users/David/src/presupuestos-aragon/cache 12 | 13 | cd $APP 14 | 15 | # Convenient variables 16 | COUNTY_DATA=$APP/theme-aragon/data/comarca/latest 17 | TOWN_DATA=$APP/theme-aragon/data/municipio/latest 18 | 19 | # Make sure target folders exist 20 | if test ! -d $COUNTY_DATA; then mkdir $COUNTY_DATA; fi 21 | if test ! -d $TOWN_DATA; then mkdir $TOWN_DATA; fi 22 | 23 | # Check if more recent data exists, for counties 24 | echo "Comprobando los datos de comarcas..." 25 | if test ! -e $COUNTY_DATA/clasificacion_economica.csv -o $SOURCE_DATA/clasificacion-economica-comarcas-2014-en-adelante.csv -nt $COUNTY_DATA/clasificacion_economica.csv 26 | then 27 | echo "Hay datos más recientes. Copiando ficheros..." 28 | cp $SOURCE_DATA/clasificacion-funcional-comarcas-2014-en-adelante.csv $COUNTY_DATA/clasificacion_funcional.csv 29 | cp $SOURCE_DATA/clasificacion-economica-comarcas-2014-en-adelante.csv $COUNTY_DATA/clasificacion_economica.csv 30 | cp $SOURCE_DATA/noxbrl-comarcas-2014-en-adelante.csv $COUNTY_DATA/no_xbrl.csv 31 | 32 | echo "Cargando datos de comarcas..." 33 | python manage.py load_budget_data comarca latest 34 | 35 | echo "Borrando la caché..." 36 | rm -rf $CACHE/* 37 | 38 | else 39 | echo "No hay datos recientes, nada que hacer." 40 | 41 | fi 42 | 43 | echo 44 | echo 45 | 46 | # Check if more recent data exists, for towns 47 | echo "Comprobando los datos de municipios..." 48 | if test ! -e $TOWN_DATA/clasificacion_economica.csv -o $SOURCE_DATA/clasificacion-economica-municipios-2014-en-adelante.csv -nt $TOWN_DATA/clasificacion_economica.csv 49 | then 50 | echo "Hay datos más recientes. Copiando ficheros..." 51 | cp $SOURCE_DATA/clasificacion-funcional-municipios-2014-en-adelante.csv $TOWN_DATA/clasificacion_funcional.csv 52 | cp $SOURCE_DATA/clasificacion-economica-municipios-2014-en-adelante.csv $TOWN_DATA/clasificacion_economica.csv 53 | cp $SOURCE_DATA/noxbrl-municipios-2014-en-adelante.csv $TOWN_DATA/no_xbrl.csv 54 | 55 | echo "Cargando datos de municipios..." 56 | python manage.py load_budget_data municipio latest 57 | 58 | echo "Borrando la caché..." 59 | rm -rf $CACHE/* 60 | 61 | else 62 | echo "No hay datos recientes, nada que hacer." 63 | 64 | fi 65 | -------------------------------------------------------------------------------- /budget_app/static/stylesheets/bootstrap/modals.less: -------------------------------------------------------------------------------- 1 | // 2 | // Modals 3 | // -------------------------------------------------- 4 | 5 | 6 | // Recalculate z-index where appropriate 7 | .modal-open { 8 | .dropdown-menu { z-index: @zindexDropdown + @zindexModal; } 9 | .dropdown.open { *z-index: @zindexDropdown + @zindexModal; } 10 | .popover { z-index: @zindexPopover + @zindexModal; } 11 | .tooltip { z-index: @zindexTooltip + @zindexModal; } 12 | } 13 | 14 | // Background 15 | .modal-backdrop { 16 | position: fixed; 17 | top: 0; 18 | right: 0; 19 | bottom: 0; 20 | left: 0; 21 | z-index: @zindexModalBackdrop; 22 | background-color: @black; 23 | // Fade for backdrop 24 | &.fade { opacity: 0; } 25 | } 26 | 27 | .modal-backdrop, 28 | .modal-backdrop.fade.in { 29 | .opacity(80); 30 | } 31 | 32 | // Base modal 33 | .modal { 34 | position: fixed; 35 | top: 50%; 36 | left: 50%; 37 | z-index: @zindexModal; 38 | overflow: auto; 39 | width: 560px; 40 | margin: -250px 0 0 -280px; 41 | background-color: @white; 42 | border: 1px solid #999; 43 | border: 1px solid rgba(0,0,0,.3); 44 | *border: 1px solid #999; /* IE6-7 */ 45 | .border-radius(6px); 46 | .box-shadow(0 3px 7px rgba(0,0,0,0.3)); 47 | .background-clip(padding-box); 48 | &.fade { 49 | .transition(e('opacity .3s linear, top .3s ease-out')); 50 | top: -25%; 51 | } 52 | &.fade.in { top: 50%; } 53 | } 54 | .modal-header { 55 | padding: 9px 15px; 56 | border-bottom: 1px solid #eee; 57 | // Close icon 58 | .close { margin-top: 2px; } 59 | // Heading 60 | h3 { 61 | margin: 0; 62 | line-height: 30px; 63 | } 64 | } 65 | 66 | // Body (where all modal content resides) 67 | .modal-body { 68 | overflow-y: auto; 69 | max-height: 400px; 70 | padding: 15px; 71 | } 72 | // Remove bottom margin if need be 73 | .modal-form { 74 | margin-bottom: 0; 75 | } 76 | 77 | // Footer (for actions) 78 | .modal-footer { 79 | padding: 14px 15px 15px; 80 | margin-bottom: 0; 81 | text-align: right; // right align buttons 82 | background-color: #f5f5f5; 83 | border-top: 1px solid #ddd; 84 | .border-radius(0 0 6px 6px); 85 | .box-shadow(inset 0 1px 0 @white); 86 | .clearfix(); // clear it in case folks use .pull-* classes on buttons 87 | 88 | // Properly space out buttons 89 | .btn + .btn { 90 | margin-left: 5px; 91 | margin-bottom: 0; // account for input[type="submit"] which gets the bottom margin like all other inputs 92 | } 93 | // but override that for button groups 94 | .btn-group .btn + .btn { 95 | margin-left: -1px; 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /tests/spec/grid_formatters_spec.js: -------------------------------------------------------------------------------- 1 | describe("Format a number", function() { 2 | 3 | it("null", function() { 4 | expect( formatNumber() ).toEqual( '' ); 5 | }); 6 | 7 | it("integer", function() { 8 | expect( formatNumber( 1980 ) ).toEqual( '1,980' ); 9 | }); 10 | 11 | it("decimal", function() { 12 | expect( formatNumber( 1980.7234 ) ).toEqual( '1,980' ); 13 | }); 14 | 15 | it("big decimal", function() { 16 | expect( formatNumber( 198019801980.7234 ) ).toEqual( '198,019,801,980' ); 17 | }); 18 | 19 | it("decimal with postfix", function() { 20 | expect( formatNumber( 1980.7234, ' €' ) ).toEqual( '1,980 €' ); 21 | }); 22 | }); 23 | 24 | 25 | describe("Format an amount", function() { 26 | 27 | it("null", function() { 28 | expect( formatAmount() ).toEqual( '' ); 29 | }); 30 | 31 | it("integer", function() { 32 | expect( formatAmount( 1456870 ) ).toEqual( '14,568\xA0€' ); 33 | }); 34 | 35 | it("decimal", function() { 36 | expect( formatAmount( 1456870.1234 ) ).toEqual( '14,568\xA0€' ); 37 | }); 38 | }); 39 | 40 | 41 | describe("Format a simplified amount", function() { 42 | 43 | it("null", function() { 44 | expect( formatSimplifiedAmount() ).toEqual( '' ); 45 | }); 46 | 47 | it("integer", function() { 48 | expect( formatSimplifiedAmount( 15687 ) ).toEqual( '156\xA0€' ); 49 | }); 50 | 51 | it("decimal", function() { 52 | expect( formatSimplifiedAmount( 15687.1234 ) ).toEqual( '156\xA0€' ); 53 | }); 54 | 55 | it("thousands", function() { 56 | expect( formatSimplifiedAmount( 1050000 ) ).toEqual( '10\xA0mil\xA0€' ); 57 | }); 58 | 59 | it("millions", function() { 60 | expect( formatSimplifiedAmount( 1070000000 ) ).toEqual( '10\xA0mill.\xA0€' ); 61 | }); 62 | }); 63 | 64 | 65 | describe("Format a decimal", function() { 66 | 67 | it("null", function() { 68 | expect( formatDecimal() ).toEqual( '' ); 69 | }); 70 | 71 | it("integer", function() { 72 | expect( formatDecimal( 1980 ) ).toEqual( '1,980.00' ); 73 | }); 74 | 75 | it("decimal", function() { 76 | expect( formatDecimal( 1980.6254 ) ).toEqual( '1,980.62' ); 77 | }); 78 | 79 | it("decimal without digits", function() { 80 | expect( formatDecimal( 1980.6254, 0 ) ).toEqual( '1,980' ); 81 | }); 82 | 83 | it("decimal with 1 digit", function() { 84 | expect( formatDecimal( 1980.6254, 1 ) ).toEqual( '1,980.6' ); 85 | }); 86 | 87 | it("decimal with 2 digits", function() { 88 | expect( formatDecimal( 1980.6254, 2 ) ).toEqual( '1,980.62' ); 89 | }); 90 | 91 | it("decimal with 3 digits", function() { 92 | expect( formatDecimal( 1980.6254, 3 ) ).toEqual( '1,980.625' ); 93 | }); 94 | }); -------------------------------------------------------------------------------- /budget_app/static/stylesheets/bootstrap/carousel.less: -------------------------------------------------------------------------------- 1 | // 2 | // Carousel 3 | // -------------------------------------------------- 4 | 5 | 6 | .carousel { 7 | position: relative; 8 | margin-bottom: @baseLineHeight; 9 | line-height: 1; 10 | } 11 | 12 | .carousel-inner { 13 | overflow: hidden; 14 | width: 100%; 15 | position: relative; 16 | } 17 | 18 | .carousel { 19 | 20 | .item { 21 | display: none; 22 | position: relative; 23 | .transition(.6s ease-in-out left); 24 | } 25 | 26 | // Account for jankitude on images 27 | .item > img { 28 | display: block; 29 | line-height: 1; 30 | } 31 | 32 | .active, 33 | .next, 34 | .prev { display: block; } 35 | 36 | .active { 37 | left: 0; 38 | } 39 | 40 | .next, 41 | .prev { 42 | position: absolute; 43 | top: 0; 44 | width: 100%; 45 | } 46 | 47 | .next { 48 | left: 100%; 49 | } 50 | .prev { 51 | left: -100%; 52 | } 53 | .next.left, 54 | .prev.right { 55 | left: 0; 56 | } 57 | 58 | .active.left { 59 | left: -100%; 60 | } 61 | .active.right { 62 | left: 100%; 63 | } 64 | 65 | } 66 | 67 | // Left/right controls for nav 68 | // --------------------------- 69 | 70 | .carousel-control { 71 | position: absolute; 72 | top: 40%; 73 | left: 15px; 74 | width: 40px; 75 | height: 40px; 76 | margin-top: -20px; 77 | font-size: 60px; 78 | font-weight: 100; 79 | line-height: 30px; 80 | color: @white; 81 | text-align: center; 82 | background: @grayDarker; 83 | border: 3px solid @white; 84 | .border-radius(23px); 85 | .opacity(50); 86 | 87 | // we can't have this transition here 88 | // because webkit cancels the carousel 89 | // animation if you trip this while 90 | // in the middle of another animation 91 | // ;_; 92 | // .transition(opacity .2s linear); 93 | 94 | // Reposition the right one 95 | &.right { 96 | left: auto; 97 | right: 15px; 98 | } 99 | 100 | // Hover state 101 | &:hover { 102 | color: @white; 103 | text-decoration: none; 104 | .opacity(90); 105 | } 106 | } 107 | 108 | 109 | // Caption for text below images 110 | // ----------------------------- 111 | 112 | .carousel-caption { 113 | position: absolute; 114 | left: 0; 115 | right: 0; 116 | bottom: 0; 117 | padding: 15px; 118 | background: @grayDark; 119 | background: rgba(0,0,0,.75); 120 | } 121 | .carousel-caption h4, 122 | .carousel-caption p { 123 | color: @white; 124 | line-height: @baseLineHeight; 125 | } 126 | .carousel-caption h4 { 127 | margin: 0 0 5px; 128 | } 129 | .carousel-caption p { 130 | margin-bottom: 0; 131 | } 132 | -------------------------------------------------------------------------------- /templates/shared/header.html: -------------------------------------------------------------------------------- 1 | 51 | -------------------------------------------------------------------------------- /budget_app/static/javascripts/bootstrap/bootstrap-alert.js: -------------------------------------------------------------------------------- 1 | /* ========================================================== 2 | * bootstrap-alert.js v2.1.0 3 | * http://twitter.github.com/bootstrap/javascript.html#alerts 4 | * ========================================================== 5 | * Copyright 2012 Twitter, Inc. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * ========================================================== */ 19 | 20 | 21 | !function ($) { 22 | 23 | "use strict"; // jshint ;_; 24 | 25 | 26 | /* ALERT CLASS DEFINITION 27 | * ====================== */ 28 | 29 | var dismiss = '[data-dismiss="alert"]' 30 | , Alert = function (el) { 31 | $(el).on('click', dismiss, this.close) 32 | } 33 | 34 | Alert.prototype.close = function (e) { 35 | var $this = $(this) 36 | , selector = $this.attr('data-target') 37 | , $parent 38 | 39 | if (!selector) { 40 | selector = $this.attr('href') 41 | selector = selector && selector.replace(/.*(?=#[^\s]*$)/, '') //strip for ie7 42 | } 43 | 44 | $parent = $(selector) 45 | 46 | e && e.preventDefault() 47 | 48 | $parent.length || ($parent = $this.hasClass('alert') ? $this : $this.parent()) 49 | 50 | $parent.trigger(e = $.Event('close')) 51 | 52 | if (e.isDefaultPrevented()) return 53 | 54 | $parent.removeClass('in') 55 | 56 | function removeElement() { 57 | $parent 58 | .trigger('closed') 59 | .remove() 60 | } 61 | 62 | $.support.transition && $parent.hasClass('fade') ? 63 | $parent.on($.support.transition.end, removeElement) : 64 | removeElement() 65 | } 66 | 67 | 68 | /* ALERT PLUGIN DEFINITION 69 | * ======================= */ 70 | 71 | $.fn.alert = function (option) { 72 | return this.each(function () { 73 | var $this = $(this) 74 | , data = $this.data('alert') 75 | if (!data) $this.data('alert', (data = new Alert(this))) 76 | if (typeof option == 'string') data[option].call($this) 77 | }) 78 | } 79 | 80 | $.fn.alert.Constructor = Alert 81 | 82 | 83 | /* ALERT DATA-API 84 | * ============== */ 85 | 86 | $(function () { 87 | $('body').on('click.alert.data-api', dismiss, Alert.prototype.close) 88 | }) 89 | 90 | }(window.jQuery); -------------------------------------------------------------------------------- /budget_app/static/stylesheets/home.less: -------------------------------------------------------------------------------- 1 | .home-intro { 2 | background: @white; 3 | margin: 0; 4 | padding: .75em; 5 | .rounded(.5em); 6 | 7 | .big-intro { 8 | margin: 0 0 1.5em; 9 | padding: 1.5em; 10 | .rounded(.75em); 11 | 12 | h1 { 13 | font-size: 2em; 14 | font-weight: 800; 15 | margin: 0 0 .25em; 16 | } 17 | 18 | .intro-text { 19 | font-size: 1.25em; 20 | font-weight: 300; 21 | margin: 0 0 .5em; 22 | } 23 | } 24 | 25 | 26 | .options { 27 | &:before, 28 | &:after { 29 | display: table; 30 | content: ""; 31 | line-height: 0; 32 | } 33 | 34 | &:after { 35 | clear: both; 36 | } 37 | 38 | > div { 39 | margin: 0 2% .25em 0; 40 | padding: 10em 5% 1px; 41 | width: 90%; 42 | font-size: 1.143em; 43 | text-align: center; 44 | .rounded( .5em ); 45 | .transition( .3s ); 46 | box-shadow: 47 | 0 0 0 1px @black95, 48 | 0 0 0 1px @white inset, 49 | 0 0 4px 8px fadeout(@black, 75%) inset; 50 | 51 | ul { 52 | .no-bullet; 53 | margin-bottom: 0.9em; 54 | } 55 | 56 | li { 57 | height: 1.4em; 58 | } 59 | 60 | &:hover { 61 | background-color: @black; 62 | color: @white; 63 | cursor: pointer; 64 | margin-top: -.25em; 65 | margin-bottom: 0; 66 | 67 | @shadow: 68 | 0 0 0 .25em @black80, 69 | 0 0 0 .5em @white90a inset; 70 | .box-shadow( @shadow ); 71 | 72 | a { 73 | color: @white; 74 | } 75 | 76 | .button { 77 | padding: .75em 1.5em; 78 | } 79 | } 80 | } 81 | 82 | .option-tax, 83 | .option-payments { 84 | margin: 0; 85 | } 86 | 87 | .desc{ 88 | margin: 0 0 1em; 89 | line-height: 1.357em; 90 | } 91 | 92 | .button { 93 | padding: .5em 1em; 94 | } 95 | } 96 | 97 | .option-global { 98 | background: url(../assets/option-global.png) no-repeat center -35px; 99 | 100 | .icon { 101 | color: @accented-1; 102 | } 103 | } 104 | 105 | .option-policies { 106 | background: url(../assets/option-policies.png) no-repeat center -38px; 107 | 108 | .icon { 109 | color: @accented-2; 110 | } 111 | } 112 | 113 | .option-tax { 114 | background: url(../assets/option-tax.png) no-repeat center 0; 115 | 116 | .icon { 117 | color: @complementary; 118 | } 119 | } 120 | 121 | .option-payments { 122 | background: url(../assets/option-payments.png) no-repeat center 0; 123 | 124 | .icon { 125 | color: @complementary; 126 | } 127 | } 128 | } 129 | 130 | @media screen and (min-width: 48em) { 131 | 132 | .home-intro .options > div{ 133 | float: left; 134 | width: 28%; 135 | padding-left: 2%; 136 | padding-right: 2%; 137 | } 138 | 139 | .home-intro .option-policies { 140 | background-position: 20px -20px; 141 | } 142 | } -------------------------------------------------------------------------------- /budget_app/static/stylesheets/vis/map.less: -------------------------------------------------------------------------------- 1 | // MAP 2 | // For panning: http://stackoverflow.com/questions/11909099/overlay-d3-paths-onto-google-maps 3 | .SvgOverlay { 4 | svg { 5 | position: absolute; 6 | top: -4000px; 7 | left: -4000px; 8 | width: 8000px; 9 | height: 8000px; 10 | } 11 | 12 | path { 13 | stroke: White; 14 | stroke-width: 1px; 15 | stroke-opacity: .5; 16 | fill-opacity: .5; 17 | } 18 | } 19 | 20 | #map-canvas { 21 | background: @black20; 22 | border: 1px solid @black90; 23 | height: 750px; 24 | margin: 0 0 1.5em; 25 | 26 | // Needed because of RWD. Prevent max-width from affecting Google Maps. 27 | img { 28 | max-width: none; 29 | } 30 | } 31 | 32 | .map-info { 33 | .rounded(.5em); 34 | @shadow: 35 | 0 .5em 0 0 fadeout(@black, 95%); 36 | .box-shadow(@shadow); 37 | bottom: 2em; 38 | left: 2.5%; 39 | position: absolute; 40 | right: 2.5%; 41 | z-index: 10; 42 | 43 | @media screen and (min-width: 481px) { 44 | left: 60%; 45 | } 46 | } 47 | 48 | .map-info__title { 49 | .border-radius-corners(.5em, 0, 0, .5em); 50 | .margin0; 51 | background: @black20; 52 | border: 1px solid @black20; 53 | border-bottom: .25em solid @black50; 54 | color: @white; 55 | font-weight: 700; 56 | margin: 0; 57 | padding: .5em .75em; 58 | } 59 | 60 | .map-info__content { 61 | .border-radius-corners(0, .5em, .5em, 0); 62 | .margin0; 63 | background: @white; 64 | border: 1px solid @black90; 65 | padding: .75em; 66 | } 67 | 68 | .map-info__basics { 69 | .no-bullet; 70 | margin: 0 0 .5em; 71 | } 72 | 73 | .map-info__quantitype { 74 | background: @black95; 75 | color: @black40; 76 | display: inline-block; .ie7-inline-block(); 77 | font-size: .75em; 78 | font-weight: 700; 79 | margin: 0; 80 | padding: .25em .5em; 81 | text-transform: uppercase; 82 | } 83 | 84 | .map-info__budgets { 85 | .no-bullet; 86 | .margin0; 87 | border-top: 1px solid @black95; 88 | font-size: 1.5em; 89 | 90 | li { 91 | padding: .25em 0; 92 | border-bottom: 1px solid @black95; 93 | 94 | &:before { 95 | color: @black70; 96 | } 97 | } 98 | } 99 | 100 | #map-legend { 101 | position: absolute; 102 | width: 100px; 103 | height: 260px; 104 | top: 100px; 105 | right: 20px; 106 | z-index: 10; 107 | 108 | .border-radius-corners(0, .5em, .5em, 0); 109 | .margin0; 110 | background: @white; 111 | border: 1px solid @black90; 112 | padding: .75em; 113 | @shadow: 114 | 0 .5em 0 0 fadeout(@black, 95%); 115 | .box-shadow(@shadow); 116 | } 117 | 118 | #map-legend path { 119 | display: none; 120 | } 121 | 122 | #map-legend line { 123 | stroke: #000; 124 | shape-rendering: crispEdges; 125 | } -------------------------------------------------------------------------------- /budget_app/models/budget_breakdown.py: -------------------------------------------------------------------------------- 1 | import json 2 | 3 | 4 | class BudgetBreakdown: 5 | def __init__(self, criteria=[]): 6 | self.criteria = criteria 7 | self.names = [] 8 | self.years = {} 9 | self.subtotals = {} 10 | self.total_expense = {} 11 | self.total_income = {} 12 | 13 | # Add a new budget item to the breakdown 14 | def add_item(self, column, item): 15 | # Check whether the column exist, and add if needed 16 | if not column in self.names: 17 | self.names.append(column) 18 | self.years[column] = item.year 19 | 20 | # Basic aggregation 21 | if item.expense: 22 | if column not in self.total_expense: 23 | self.total_expense[column] = 0 24 | self.total_expense[column] += item.amount 25 | else: 26 | if column not in self.total_income: 27 | self.total_income[column] = 0 28 | self.total_income[column] += item.amount 29 | 30 | # Breakdown aggregation 31 | if len(self.criteria) > 0: # We have a criteria to classify on 32 | # Sometimes the criteria is not a string, but a lambda 33 | if hasattr(self.criteria[0], '__call__'): 34 | value = self.criteria[0](item) 35 | else: # Sometimes it's just a string pointing to an attribute... 36 | value = getattr(item, self.criteria[0]) 37 | # ...or a method 38 | if hasattr(value, '__call__'): 39 | value = value() 40 | 41 | # Check we have an actual value for the given criteria 42 | if value == None: 43 | return 44 | 45 | if value not in self.subtotals: 46 | self.subtotals[value] = BudgetBreakdown(self.criteria[1:]) 47 | self.subtotals[value].add_item(column, item) 48 | 49 | # Simplified JSON output, for cleaner view code 50 | def to_json(self, labels=None, uid=None): 51 | data = { 52 | 'expense': self.total_expense, 53 | 'income': self.total_income 54 | } 55 | # Add year information only at the root level, waste of space otherwise 56 | if not uid: 57 | data['years'] = self.years 58 | 59 | # Add a label to the item, if provided 60 | if labels and uid: 61 | data['label'] = labels.get(uid) 62 | 63 | # Iterate through the children. Let them know the uid they represent 64 | if len(self.subtotals) > 0: 65 | data['sub'] = {} 66 | for subtotal in self.subtotals: 67 | data['sub'][subtotal] = self.subtotals[subtotal].to_json(uid=subtotal, labels=labels) 68 | 69 | # Since we're calling this method recursively, we only convert to JSON 70 | # at the root level, i.e. when the uid is null 71 | return data if uid else json.dumps(data) 72 | -------------------------------------------------------------------------------- /budget_app/static/javascripts/breakpoints.js: -------------------------------------------------------------------------------- 1 | /* 2 | Breakpoints.js 3 | version 1.0 4 | 5 | Creates handy events for your responsive design breakpoints 6 | 7 | Copyright 2011 XOXCO, Inc 8 | http://xoxco.com/ 9 | 10 | Documentation for this plugin lives here: 11 | http://xoxco.com/projects/code/breakpoints 12 | 13 | Licensed under the MIT license: 14 | http://www.opensource.org/licenses/mit-license.php 15 | 16 | */ 17 | (function($) { 18 | 19 | var lastSize = 0; 20 | var interval = null; 21 | 22 | $.fn.resetBreakpoints = function() { 23 | $(window).unbind('resize'); 24 | if (interval) { 25 | clearInterval(interval); 26 | } 27 | lastSize = 0; 28 | }; 29 | 30 | $.fn.setBreakpoints = function(settings) { 31 | var options = jQuery.extend({ 32 | distinct: true, 33 | breakpoints: new Array(320,480,768,1024) 34 | },settings); 35 | 36 | 37 | interval = setInterval(function() { 38 | 39 | var w = $(window).width(); 40 | var done = false; 41 | 42 | for (var bp in options.breakpoints.sort(function(a,b) { return (b-a) })) { 43 | 44 | // fire onEnter when a browser expands into a new breakpoint 45 | // if in distinct mode, remove all other breakpoints first. 46 | if (!done && w >= options.breakpoints[bp] && lastSize < options.breakpoints[bp]) { 47 | if (options.distinct) { 48 | for (var x in options.breakpoints.sort(function(a,b) { return (b-a) })) { 49 | if ($('body').hasClass('breakpoint-' + options.breakpoints[x])) { 50 | $('body').removeClass('breakpoint-' + options.breakpoints[x]); 51 | $(window).trigger('exitBreakpoint' + options.breakpoints[x]); 52 | } 53 | } 54 | done = true; 55 | } 56 | $('body').addClass('breakpoint-' + options.breakpoints[bp]); 57 | $(window).trigger('enterBreakpoint' + options.breakpoints[bp]); 58 | 59 | } 60 | 61 | // fire onExit when browser contracts out of a larger breakpoint 62 | if (w < options.breakpoints[bp] && lastSize >= options.breakpoints[bp]) { 63 | $('body').removeClass('breakpoint-' + options.breakpoints[bp]); 64 | $(window).trigger('exitBreakpoint' + options.breakpoints[bp]); 65 | 66 | } 67 | 68 | // if in distinct mode, fire onEnter when browser contracts into a smaller breakpoint 69 | if ( 70 | options.distinct && // only one breakpoint at a time 71 | w >= options.breakpoints[bp] && // and we are in this one 72 | w < options.breakpoints[bp-1] && // and smaller than the bigger one 73 | lastSize > w && // and we contracted 74 | lastSize >0 && // and this is not the first time 75 | !$('body').hasClass('breakpoint-' + options.breakpoints[bp]) // and we aren't already in this breakpoint 76 | ) { 77 | $('body').addClass('breakpoint-' + options.breakpoints[bp]); 78 | $(window).trigger('enterBreakpoint' + options.breakpoints[bp]); 79 | 80 | } 81 | } 82 | 83 | // set up for next call 84 | if (lastSize != w) { 85 | lastSize = w; 86 | } 87 | },250); 88 | }; 89 | 90 | })(jQuery); 91 | -------------------------------------------------------------------------------- /django_jasmine/static/jasmine-1.0.1/jasmine.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; 3 | } 4 | 5 | 6 | .jasmine_reporter a:visited, .jasmine_reporter a { 7 | color: #303; 8 | } 9 | 10 | .jasmine_reporter a:hover, .jasmine_reporter a:active { 11 | color: blue; 12 | } 13 | 14 | .run_spec { 15 | float:right; 16 | padding-right: 5px; 17 | font-size: .8em; 18 | text-decoration: none; 19 | } 20 | 21 | .jasmine_reporter { 22 | margin: 0 5px; 23 | } 24 | 25 | .banner { 26 | color: #303; 27 | background-color: #fef; 28 | padding: 5px; 29 | } 30 | 31 | .logo { 32 | float: left; 33 | font-size: 1.1em; 34 | padding-left: 5px; 35 | } 36 | 37 | .logo .version { 38 | font-size: .6em; 39 | padding-left: 1em; 40 | } 41 | 42 | .runner.running { 43 | background-color: yellow; 44 | } 45 | 46 | 47 | .options { 48 | text-align: right; 49 | font-size: .8em; 50 | } 51 | 52 | 53 | 54 | 55 | .suite { 56 | border: 1px outset gray; 57 | margin: 5px 0; 58 | padding-left: 1em; 59 | } 60 | 61 | .suite .suite { 62 | margin: 5px; 63 | } 64 | 65 | .suite.passed { 66 | background-color: #dfd; 67 | } 68 | 69 | .suite.failed { 70 | background-color: #fdd; 71 | } 72 | 73 | .spec { 74 | margin: 5px; 75 | padding-left: 1em; 76 | clear: both; 77 | } 78 | 79 | .spec.failed, .spec.passed, .spec.skipped { 80 | padding-bottom: 5px; 81 | border: 1px solid gray; 82 | } 83 | 84 | .spec.failed { 85 | background-color: #fbb; 86 | border-color: red; 87 | } 88 | 89 | .spec.passed { 90 | background-color: #bfb; 91 | border-color: green; 92 | } 93 | 94 | .spec.skipped { 95 | background-color: #bbb; 96 | } 97 | 98 | .messages { 99 | border-left: 1px dashed gray; 100 | padding-left: 1em; 101 | padding-right: 1em; 102 | } 103 | 104 | .passed { 105 | background-color: #cfc; 106 | display: none; 107 | } 108 | 109 | .failed { 110 | background-color: #fbb; 111 | } 112 | 113 | .skipped { 114 | color: #777; 115 | background-color: #eee; 116 | display: none; 117 | } 118 | 119 | 120 | /*.resultMessage {*/ 121 | /*white-space: pre;*/ 122 | /*}*/ 123 | 124 | .resultMessage span.result { 125 | display: block; 126 | line-height: 2em; 127 | color: black; 128 | } 129 | 130 | .resultMessage .mismatch { 131 | color: black; 132 | } 133 | 134 | .stackTrace { 135 | white-space: pre; 136 | font-size: .8em; 137 | margin-left: 10px; 138 | max-height: 5em; 139 | overflow: auto; 140 | border: 1px inset red; 141 | padding: 1em; 142 | background: #eef; 143 | } 144 | 145 | .finished-at { 146 | padding-left: 1em; 147 | font-size: .6em; 148 | } 149 | 150 | .show-passed .passed, 151 | .show-skipped .skipped { 152 | display: block; 153 | } 154 | 155 | 156 | #jasmine_content { 157 | position:fixed; 158 | right: 100%; 159 | } 160 | 161 | .runner { 162 | border: 1px solid gray; 163 | display: block; 164 | margin: 5px 0; 165 | padding: 2px 0 2px 10px; 166 | } 167 | -------------------------------------------------------------------------------- /django_jasmine/static/jasmine-1.1.0.rc1/jasmine.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: "Helvetica Neue Light", "Lucida Grande", "Calibri", "Arial", sans-serif; 3 | } 4 | 5 | 6 | .jasmine_reporter a:visited, .jasmine_reporter a { 7 | color: #303; 8 | } 9 | 10 | .jasmine_reporter a:hover, .jasmine_reporter a:active { 11 | color: blue; 12 | } 13 | 14 | .run_spec { 15 | float:right; 16 | padding-right: 5px; 17 | font-size: .8em; 18 | text-decoration: none; 19 | } 20 | 21 | .jasmine_reporter { 22 | margin: 0 5px; 23 | } 24 | 25 | .banner { 26 | color: #303; 27 | background-color: #fef; 28 | padding: 5px; 29 | } 30 | 31 | .logo { 32 | float: left; 33 | font-size: 1.1em; 34 | padding-left: 5px; 35 | } 36 | 37 | .logo .version { 38 | font-size: .6em; 39 | padding-left: 1em; 40 | } 41 | 42 | .runner.running { 43 | background-color: yellow; 44 | } 45 | 46 | 47 | .options { 48 | text-align: right; 49 | font-size: .8em; 50 | } 51 | 52 | 53 | 54 | 55 | .suite { 56 | border: 1px outset gray; 57 | margin: 5px 0; 58 | padding-left: 1em; 59 | } 60 | 61 | .suite .suite { 62 | margin: 5px; 63 | } 64 | 65 | .suite.passed { 66 | background-color: #dfd; 67 | } 68 | 69 | .suite.failed { 70 | background-color: #fdd; 71 | } 72 | 73 | .spec { 74 | margin: 5px; 75 | padding-left: 1em; 76 | clear: both; 77 | } 78 | 79 | .spec.failed, .spec.passed, .spec.skipped { 80 | padding-bottom: 5px; 81 | border: 1px solid gray; 82 | } 83 | 84 | .spec.failed { 85 | background-color: #fbb; 86 | border-color: red; 87 | } 88 | 89 | .spec.passed { 90 | background-color: #bfb; 91 | border-color: green; 92 | } 93 | 94 | .spec.skipped { 95 | background-color: #bbb; 96 | } 97 | 98 | .messages { 99 | border-left: 1px dashed gray; 100 | padding-left: 1em; 101 | padding-right: 1em; 102 | } 103 | 104 | .passed { 105 | background-color: #cfc; 106 | display: none; 107 | } 108 | 109 | .failed { 110 | background-color: #fbb; 111 | } 112 | 113 | .skipped { 114 | color: #777; 115 | background-color: #eee; 116 | display: none; 117 | } 118 | 119 | 120 | /*.resultMessage {*/ 121 | /*white-space: pre;*/ 122 | /*}*/ 123 | 124 | .resultMessage span.result { 125 | display: block; 126 | line-height: 2em; 127 | color: black; 128 | } 129 | 130 | .resultMessage .mismatch { 131 | color: black; 132 | } 133 | 134 | .stackTrace { 135 | white-space: pre; 136 | font-size: .8em; 137 | margin-left: 10px; 138 | max-height: 5em; 139 | overflow: auto; 140 | border: 1px inset red; 141 | padding: 1em; 142 | background: #eef; 143 | } 144 | 145 | .finished-at { 146 | padding-left: 1em; 147 | font-size: .6em; 148 | } 149 | 150 | .show-passed .passed, 151 | .show-skipped .skipped { 152 | display: block; 153 | } 154 | 155 | 156 | #jasmine_content { 157 | position:fixed; 158 | right: 100%; 159 | } 160 | 161 | .runner { 162 | border: 1px solid gray; 163 | display: block; 164 | margin: 5px 0; 165 | padding: 2px 0 2px 10px; 166 | } 167 | -------------------------------------------------------------------------------- /budget_app/models/payment.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.conf import settings 3 | 4 | 5 | class PaymentManager(models.Manager): 6 | def each_denormalized(self, additional_constraints=None, additional_arguments=None): 7 | # XXX: Note that this left join syntax works well even when the economic_category_id is null, 8 | # as opposed to the way we query for Budget Items. I should probably adopt this all around, 9 | # and potentially even stop using dummy categories on loaders. 10 | sql = \ 11 | "select " \ 12 | "p.id, p.area, p.date, p.payee, p.expense, p.amount, p.description, " \ 13 | "coalesce(ec.description, 'Otros') as ec_description, " \ 14 | "b.year " \ 15 | "from " \ 16 | "payments p " \ 17 | "left join budgets b on p.budget_id = b.id " \ 18 | "left join economic_categories ec on p.economic_category_id = ec.id " \ 19 | 20 | if additional_constraints: 21 | sql += " where " + additional_constraints 22 | 23 | return self.raw(sql, additional_arguments) 24 | 25 | # Do a full-text search in the database 26 | def search(self, query, year): 27 | sql = "select " \ 28 | "b.year, " \ 29 | "e.name, e.level, " \ 30 | "fc.programme, fc.description as fc_description, " \ 31 | "p.id, p.area, p.date, p.description, p.amount, p.expense " \ 32 | "from " \ 33 | "payments p " \ 34 | "left join budgets b on p.budget_id = b.id " \ 35 | "left join entities e on b.entity_id = e.id " \ 36 | "left join functional_categories fc on p.functional_category_id = fc.id " \ 37 | "where " \ 38 | "to_tsvector('"+settings.SEARCH_CONFIG+"',p.payee||' '||p.description) @@ plainto_tsquery('"+settings.SEARCH_CONFIG+"',%s)" 39 | if year: 40 | sql += " and b.year='%s'" % year 41 | sql += " order by p.amount desc" 42 | return self.raw(sql, (query, )) 43 | 44 | 45 | class Payment(models.Model): 46 | budget = models.ForeignKey('Budget') 47 | area = models.CharField(max_length=100) 48 | functional_category = models.ForeignKey('FunctionalCategory', db_column='functional_category_id', null=True) 49 | economic_category = models.ForeignKey('EconomicCategory', db_column='economic_category_id', null=True) 50 | economic_concept = models.CharField(max_length=10, null=True) 51 | date = models.DateField(null=True) 52 | contract_type = models.CharField(max_length=50) 53 | payee = models.CharField(max_length=200) 54 | description = models.CharField(max_length=300) 55 | expense = models.BooleanField() 56 | amount = models.BigIntegerField() 57 | updated_at = models.DateTimeField(auto_now=True) 58 | created_at = models.DateTimeField(auto_now_add=True) 59 | 60 | objects = PaymentManager() 61 | 62 | class Meta: 63 | app_label = "budget_app" 64 | db_table = "payments" 65 | 66 | def __unicode__(self): 67 | return self.payee 68 | -------------------------------------------------------------------------------- /budget_app/static/javascripts/bootstrap/bootstrap-button.js: -------------------------------------------------------------------------------- 1 | /* ============================================================ 2 | * bootstrap-button.js v2.1.0 3 | * http://twitter.github.com/bootstrap/javascript.html#buttons 4 | * ============================================================ 5 | * Copyright 2012 Twitter, Inc. 6 | * 7 | * Licensed under the Apache License, Version 2.0 (the "License"); 8 | * you may not use this file except in compliance with the License. 9 | * You may obtain a copy of the License at 10 | * 11 | * http://www.apache.org/licenses/LICENSE-2.0 12 | * 13 | * Unless required by applicable law or agreed to in writing, software 14 | * distributed under the License is distributed on an "AS IS" BASIS, 15 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | * See the License for the specific language governing permissions and 17 | * limitations under the License. 18 | * ============================================================ */ 19 | 20 | 21 | !function ($) { 22 | 23 | "use strict"; // jshint ;_; 24 | 25 | 26 | /* BUTTON PUBLIC CLASS DEFINITION 27 | * ============================== */ 28 | 29 | var Button = function (element, options) { 30 | this.$element = $(element) 31 | this.options = $.extend({}, $.fn.button.defaults, options) 32 | } 33 | 34 | Button.prototype.setState = function (state) { 35 | var d = 'disabled' 36 | , $el = this.$element 37 | , data = $el.data() 38 | , val = $el.is('input') ? 'val' : 'html' 39 | 40 | state = state + 'Text' 41 | data.resetText || $el.data('resetText', $el[val]()) 42 | 43 | $el[val](data[state] || this.options[state]) 44 | 45 | // push to event loop to allow forms to submit 46 | setTimeout(function () { 47 | state == 'loadingText' ? 48 | $el.addClass(d).attr(d, d) : 49 | $el.removeClass(d).removeAttr(d) 50 | }, 0) 51 | } 52 | 53 | Button.prototype.toggle = function () { 54 | var $parent = this.$element.parent('[data-toggle="buttons-radio"]') 55 | 56 | $parent && $parent 57 | .find('.active') 58 | .removeClass('active') 59 | 60 | this.$element.toggleClass('active') 61 | } 62 | 63 | 64 | /* BUTTON PLUGIN DEFINITION 65 | * ======================== */ 66 | 67 | $.fn.button = function (option) { 68 | return this.each(function () { 69 | var $this = $(this) 70 | , data = $this.data('button') 71 | , options = typeof option == 'object' && option 72 | if (!data) $this.data('button', (data = new Button(this, options))) 73 | if (option == 'toggle') data.toggle() 74 | else if (option) data.setState(option) 75 | }) 76 | } 77 | 78 | $.fn.button.defaults = { 79 | loadingText: 'loading...' 80 | } 81 | 82 | $.fn.button.Constructor = Button 83 | 84 | 85 | /* BUTTON DATA-API 86 | * =============== */ 87 | 88 | $(function () { 89 | $('body').on('click.button.data-api', '[data-toggle^=button]', function ( e ) { 90 | var $btn = $(e.target) 91 | if (!$btn.hasClass('btn')) $btn = $btn.closest('.btn') 92 | $btn.button('toggle') 93 | }) 94 | }) 95 | 96 | }(window.jQuery); --------------------------------------------------------------------------------