1
0
mirror of https://github.com/balkian/balkian.github.com.git synced 2024-12-25 09:48:12 +00:00

Migrated to Pelican

Now tags have pages, and all other visual goodies :)
This commit is contained in:
J. Fernando Sánchez 2015-01-22 23:37:23 +01:00
parent 31c7e9ec9a
commit 04c716e212
92 changed files with 1320 additions and 703 deletions

4
.gitignore vendored
View File

@ -1,2 +1,6 @@
_site
*.swp
cache
output
*.pid
*.pyc

110
Makefile Normal file
View File

@ -0,0 +1,110 @@
PY?=python
PELICAN?=pelican
PELICANOPTS=
BASEDIR=$(CURDIR)
INPUTDIR=$(BASEDIR)/content
OUTPUTDIR=$(BASEDIR)/output
CONFFILE=$(BASEDIR)/pelicanconf.py
PUBLISHCONF=$(BASEDIR)/publishconf.py
FTP_HOST=localhost
FTP_USER=anonymous
FTP_TARGET_DIR=/
SSH_HOST=localhost
SSH_PORT=22
SSH_USER=root
SSH_TARGET_DIR=/var/www
S3_BUCKET=my_s3_bucket
CLOUDFILES_USERNAME=my_rackspace_username
CLOUDFILES_API_KEY=my_rackspace_api_key
CLOUDFILES_CONTAINER=my_cloudfiles_container
DROPBOX_DIR=~/Dropbox/Public/
GITHUB_PAGES_BRANCH=master
DEBUG ?= 0
ifeq ($(DEBUG), 1)
PELICANOPTS += -D
endif
help:
@echo 'Makefile for a pelican Web site '
@echo ' '
@echo 'Usage: '
@echo ' make html (re)generate the web site '
@echo ' make clean remove the generated files '
@echo ' make regenerate regenerate files upon modification '
@echo ' make publish generate using production settings '
@echo ' make serve [PORT=8000] serve site at http://localhost:8000'
@echo ' make devserver [PORT=8000] start/restart develop_server.sh '
@echo ' make stopserver stop local server '
@echo ' make ssh_upload upload the web site via SSH '
@echo ' make rsync_upload upload the web site via rsync+ssh '
@echo ' make dropbox_upload upload the web site via Dropbox '
@echo ' make ftp_upload upload the web site via FTP '
@echo ' make s3_upload upload the web site via S3 '
@echo ' make cf_upload upload the web site via Cloud Files'
@echo ' make github upload the web site via gh-pages '
@echo ' '
@echo 'Set the DEBUG variable to 1 to enable debugging, e.g. make DEBUG=1 html'
@echo ' '
html:
$(PELICAN) $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS)
clean:
[ ! -d $(OUTPUTDIR) ] || rm -rf $(OUTPUTDIR)
regenerate:
$(PELICAN) -r $(INPUTDIR) -o $(OUTPUTDIR) -s $(CONFFILE) $(PELICANOPTS)
serve:
ifdef PORT
cd $(OUTPUTDIR) && $(PY) -m pelican.server $(PORT)
else
cd $(OUTPUTDIR) && $(PY) -m pelican.server
endif
devserver:
ifdef PORT
$(BASEDIR)/develop_server.sh restart $(PORT)
else
$(BASEDIR)/develop_server.sh restart
endif
stopserver:
kill -9 `cat pelican.pid`
kill -9 `cat srv.pid`
@echo 'Stopped Pelican and SimpleHTTPServer processes running in background.'
publish:
$(PELICAN) $(INPUTDIR) -o $(OUTPUTDIR) -s $(PUBLISHCONF) $(PELICANOPTS)
ssh_upload: publish
scp -P $(SSH_PORT) -r $(OUTPUTDIR)/* $(SSH_USER)@$(SSH_HOST):$(SSH_TARGET_DIR)
rsync_upload: publish
rsync -e "ssh -p $(SSH_PORT)" -P -rvzc --delete $(OUTPUTDIR)/ $(SSH_USER)@$(SSH_HOST):$(SSH_TARGET_DIR) --cvs-exclude
dropbox_upload: publish
cp -r $(OUTPUTDIR)/* $(DROPBOX_DIR)
ftp_upload: publish
lftp ftp://$(FTP_USER)@$(FTP_HOST) -e "mirror -R $(OUTPUTDIR) $(FTP_TARGET_DIR) ; quit"
s3_upload: publish
s3cmd sync $(OUTPUTDIR)/ s3://$(S3_BUCKET) --acl-public --delete-removed --guess-mime-type
cf_upload: publish
cd $(OUTPUTDIR) && swift -v -A https://auth.api.rackspacecloud.com/v1.0 -U $(CLOUDFILES_USERNAME) -K $(CLOUDFILES_API_KEY) upload -c $(CLOUDFILES_CONTAINER) .
github: publish
ghp-import -m "Generate Pelican site" -b $(GITHUB_PAGES_BRANCH) $(OUTPUTDIR)
git push origin $(GITHUB_PAGES_BRANCH)
.PHONY: html help clean regenerate serve devserver publish ssh_upload rsync_upload dropbox_upload ftp_upload s3_upload cf_upload github

View File

@ -1,4 +0,0 @@
name: Balkian's Log
pygments: true
paginate: 5
markdown: kramdown

View File

@ -1,18 +0,0 @@
---
layout: default
---
<div class="postnav">
<a href="{{ page.previous.url }}"><span class="previouspost"><i class="icon-chevron-sign-left"></i> {{ page.previous.title }}</a></span>
<span class='nextpost'><a href="{{ page.next.url }}">{{ page.next.title }} <i class="icon-chevron-sign-right"></i></span></a>
</div>
<div class="posthead">
<h2 class="title">{{ page.title }}</h2>
<span class="meta date">{{ page.date | date_to_string }}</span>
{% for c in page.tags %}
<span class="label label-success">{{ c }}</span>
{% endfor %}
</div>
<div class="post">
{{ content }}
</div>

View File

@ -1,31 +0,0 @@
---
layout: post
title: "Creating my web"
date: 2013-08-22 14:14:22
tags: starters javascript ruby github git
---
Finally, I've decided to set up a decent personal page. I have settled for github-pages because I like the idea of keeping my site in a repository and having someone else host and deploy it for me. The site will be really simple, mostly static files.
Thanks to Github, [Jekyll](http://jekyllrb.com) will automatically generate static pages for my posts every time I commit anything new to this repository.
But Jekyll can be used independently, so if I ever choose to host the site myself, I can do it quite easily. Another thing that I liked about this approach is that the generated html files can be used in the future, and I will not need Jekyll to serve it.
Jekyll is really simple and most of the things are written in plain html.
That means that everything could be easily reused if I ever choose to change to another blogging framework (e.g. pelical).
But, for the time being, I like the fact that Github takes care of the compilation as well, so I can simply modify or add files through the web interface should I need to.
I hadn't played with HTML and CSS for a while now, so I also wanted to use this site as a playground.
At some point, I realised I was doing mostly everything in plain HTML and CSS, and decided to keep it like that for as long as possible. As of this writing, I haven't included any Javascript code in the page. Probably I will use some to add my [gists](http://gist.github.com/balkian) and [repositories](http://github.com/balkian), but we will see about that.
I think the code speaks for itself, so you can check out [my repository on Github](http://github.com/balkian/balkian.github.com). You can clone and deploy it easily like this:
{% highlight bash %}
git clone https://github.com/balkian/balkian.github.com
cd balkian.github.com
jekyll serve -w
{% endhighlight %}
I will keep updating this post with information about:
* Some Jekyll plugins that might be useful
* What CSS tricks I learnt
* The webfonts I used
* The badge on the left side of the page

View File

@ -1,11 +0,0 @@
---
layout: post
title: "Remove git files with globbing"
date: 2013-08-22 23:14:00
tags: git
---
A simple trick. If you want to remove all the '.swp' files from a git repository, just use:
{% highlight bash %}
git rm --cached '\*\*.swp'
{% endhighlight %}

View File

@ -1,94 +0,0 @@
---
layout: post
title: "Updating EuroLoveMap"
date: 2014-03-27 14:00:00
tags: javascript python heroku
---
As part of the [OpeNER hackathon](http://www.opener-project.org/2013/07/18/opener-hackathon-in-amsterdam/) we decided to build a prototype that would allow us to compare how different countries feel about several topics.
We used the OpeNER pipeline to get the sentiment from a set of newspaper articles we gathered from media in several languages.
Then we aggregated those articles by category and country (using the source of the article or the language it was written in), obtaining the "overall feeling" of each country about each topic.
Then, we used some fancy JavaScript to make sense out of the raw information.
It didn't go too bad, it turns out [we won](http://eurosentiment.eu/wp-content/uploads/2013/07/BOLv9qnCIAAJEek.jpg).
Now, it was time for a face-lift.
I used this opportunity to play with new technologies and improve it:
* Using Flask, this time using python 3.3 and Bootstrap 3.0
* Cool HTML5+JS cards (thanks to [pastetophone](http://pastetophone.com))
* Automatic generation of fake personal data to test the interface
* Obfuscation of personal emails
Publishing a Python 3 app on Heroku
-----------------------------------
[seen here](http://eurolovemap.herokuapp.com/)
{% highlight bash %}
mkvirtualenv -p /usr/bin/python3.3 eurolovemap
{% endhighlight %}
Since Heroku uses python 2.7 by default, we have to tell it which version we want, although it supports python 3.4 as well.
I couldn't get python 3.4 working using the [deadsnakes](https://launchpad.net/~fkrull/+archive/deadsnakes) ppa, so I used python 3.3 instead, which works fine but is not officially supported.
Just create a file named *runtime.txt* in your project root, with the python version you want to use:
{% highlight bash %}
python-3.3.1
{% endhighlight %}
Don't forget to freeze your dependencies so Heroku can install them:
{% highlight bash %}
pip freze > requirements.txt
{% endhighlight %}
Publishing personal emails
--------------------------
There are really sophisticated and effective ways to obfuscate personal emails so that spammers cannot easily grab yours.
However, this time I needed something really simple to hide our emails from the simplest form of crawlers.
Most of the team are in academia somehow, so in the end all our emails are available in sites like Google Scholar.
Anyway, nobody likes getting spammed so I settled for a custom [Caesar cipher](http://en.wikipedia.org/wiki/Caesar_cipher).
Please, don't use it for any serious application if you are concerned about being spammed.
{% highlight python %}
def blur_email(email):
return "".join([chr(ord(i)+5) for i in email])
{% endhighlight %}
And this is the client side:
{% highlight javascript %}
window.onload = function(){
elems = document.getElementsByClassName('profile-email');
for(var e in elems){
var blur = elems[e].innerHTML;
var email = "";
for(var s in blur){
var a = blur.charCodeAt(s)
email = email+String.fromCharCode(a-5);
}
elems[e].innerHTML = email;
}
}
{% endhighlight %}
Unfortunately, this approach does not hide your email from anyone using [PhantomJS](http://phantomjs.org/), [ZombieJS](http://zombie.labnotes.org/) or similar.
For that, other approaches like generating a picture with the address would be necessary.
Nevertheless, it is overkill for a really simple ad-hoc application with custom formatting and just a bunch of emails that would easily be grabbed manually.
Generation of fake data
-----------------------
To test the contact section of the site, I wanted to populate it with fake data.
[Fake-Factory](https://github.com/joke2k/faker) is an amazing library that can generate fake data of almost any kind: emails, association names, acronyms...
It even lets you localise the results (get Spanish names, for instance) and generate factories for certain classes (à la Django).
But I also wanted pictures, enter [Lorem Pixel](http://lorempixel.com/).
With its API you can generate pictures of almost any size, for different topics (e.g. nightlife, people) and with a custom text.
You can even use an index, so it will always show the same picture.
For instance, the picture below is served through Lorem Pixel.
![This picture is generated with LoremIpsum](http://lorempixel.com/400/200/nightlife/)
By the way, if you only want cat pictures, take a look at [Placekitten](http://placekitten.com/).
And for NSFW text, there's the [Samuel L. Jackson Ipsum](http://slipsum.com/)

View File

@ -1,106 +0,0 @@
---
layout: post
title: "Publishing in PyPi"
date: 2014-09-27 10:00:00
tags: github python pypi
---
Developing a python module and publishing it on Github is cool, but most of the times you want others to download and use it easily.
That is the role of PyPi, the python package repository.
In this post I show you how to publish your package in less than 10 minutes.
## Choose a fancy name
If you haven't done so yet, take a minute or two to think about this.
To publish on PyPi you need a name for your package that isn't taken.
What's more, a catchy and unique name will help people remember your module and feel more inclined to at least try it.
The package name should hint what your module does, but that's not always the case.
That's your call.
I personally put uniqueness and memorability over describing the functionality.
## Create a .pypirc configuration file
{% highlight cfg %}
[distutils] # this tells distutils what package indexes you can push to
index-servers =
pypi # the live PyPI
pypitest # test PyPI
[pypi] # authentication details for live PyPI
repository = https://pypi.python.org/pypi
username = { your_username }
password = { your_password } # not necessary
[pypitest] # authentication details for test PyPI
repository = https://testpypi.python.org/pypi
username = { your_username }
{% endhighlight %}
As you can see, you need to register both in the [main pypi repository](https://pypi.python.org/pypi?%3Aaction=register_form) and the [testing server](https://testpypi.python.org/pypi?%3Aaction=register_form).
The usernames and passwords might be different, that is up to you!
## Prepare your package
{% highlight raw %}
root-dir/ # Any name you want
setup.py
setup.cfg
LICENSE.txt
README.md
mypackage/
__init__.py
foo.py
bar.py
baz.py
{% endhighlight %}
### setup.cfg
{% highlight cfg %}
[metadata]
description-file = README.md
{% endhighlight %}
The markdown README is the _de facto_ standard in Github, but you can also use rST (reStructuredText), the standard in the python community.
### setup.py
{% highlight python %}
from distutils.core import setup
setup(
name = 'mypackage',
packages = ['mypackage'], # this must be the same as the name above
version = '{ version }',
description = '{ description }',
author = '{ name }',
author_email = '{ email }',
url = 'https://github.com/{user}/{package}', # URL to the github repo
download_url = 'https://github.com/{user}/{repo}/tarball/{version}',
keywords = ['websockets', 'display', 'd3'], # list of keywords that represent your package
classifiers = [],
)
{% endhighlight %}
You might notice that the download_url points to a Github URL.
We could host our package anywhere, but Github is a convenient option.
To create the tarball and the zip packages, you only need to tag a tag in your repository and push it to github:
```
git tag {version} -m "{ Description of this tag/version}"
git push --tags origin master
```
## Push to the testing/main pypi server
It is advisable that you try your package on the test repository and fix any problems first.
The process is simple:
```
python setup.py register -r {pypitest/pypi}
python setup.py sdist upload -r {pypitest/pypi}
```
If everything went as expected, you can now install your package through pip and browse your package's page.
For instance, check my senpy package: [https://pypi.python.org/pypi/senpy](https://pypi.python.org/pypi/senpy)
```
pip install senpy
```

View File

@ -1,67 +0,0 @@
---
layout: post
title: "Proxies with Apache and python"
date: 2014-10-09 10:00:00
tags: python apache proxy gunicorn uwsgi
---
This is a quick note on proxying a local python application (e.g. flask) to a subdirectory in Apache.
This assumes that the file wsgi.py contains a WSGI application with the name *application*. Hence, wsgi:application.
## Gunicorn
{% highlight apache %}
<Location /myapp/>
ProxyPass http://127.0.0.1:8888/myapp/
ProxyPassReverse http://127.0.0.1:8888/myapp/
RequestHeader set SCRIPT_NAME "/myapp/"
</Location>
{% endhighlight %}
**Important**: *SCRIPT_NAME* and the end of *ProxyPass* URL **MUST BE THE SAME**. Otherwise, Gunicorn will fail miserably.
Try it with:
{% highlight bash %}
venv/bin/gunicorn -w 4 -b 127.0.0.1:8888 --log-file - --access-logfile - wsgi:application
{% endhighlight %}
## UWSGI
This is a very simple configuration. I will try to upload one with more options for uwsgi (in a .ini file).
{% highlight apache %}
<Location /myapp/>
SetHandler uwsgi_handler
uWSGISocker 127.0.0.1:8888
</Location>
{% endhighlight %}
Try it with:
{% highlight bash %}
uwsgi --socket 127.0.0.1:8888 -w wsgi:application
{% endhighlight %}
### Extra: Supervisor
If everything went as expected, you can wrap your command in a supervisor config file and let it handle the server for you.
{% highlight ini %}
[unix_http_server]
file=/tmp/myapp.sock ; path to your socket file
[supervisord]
logfile = %(here)s/logs/supervisor.log
childlogdir = %(here)s/logs/
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[supervisorctl]
logfile = %(here)s/logs/supervisorctl.log
serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL for a unix socket
[program:myapp]
command = venv/bin/gunicorn -w 4 -b 0.0.0.0:5000 --log-file %(here)s/logs/gunicorn.log --access-logfile - wsgi:application
directory = %(here)s
environment = PATH=%(here)s/venv/bin/
logfile = %(here)s/logs/myapp.log
{% endhighlight %}

View File

@ -1,78 +0,0 @@
---
layout: post
title: "Zotero"
date: 2014-12-09 12:12:12
tags: zotero webdav nginx apache
---
[Zotero](https://www.zotero.org/) is an Open Source tool that lets you organise your bibliography, syncing it with the cloud.
Unlike other alternatives such as [Mendeley](http://www.mendeley.com), Zotero can upload the attachments and data to a private cloud via WebDav.
If you use nginx as your web server, know that even though it provides partial support for webdav, Zotero needs more than that.
Hence, you will need another webdav server, and optionally let nginx proxy to it.
This short post provides the basics to get that set-up working under Debian/Ubuntu.
## Setting up Apache
First we need to install Apache:
sudo apt-get install apache2
Change the head of "/etc/apache2/sites-enabled/000-default" to:
<VirtualHost *:880>
Then, create a file /etc/apache2/sites-available/webdav:
Alias /dav /home/webdav/dav
<Location /dav>
Dav on
Order Allow,Deny
Allow from all
Dav On
Options +Indexes
AuthType Basic
AuthName DAV
AuthBasicProvider file
AuthUserFile /home/webdav/.htpasswd
Require valid-user
</Location>
Ideally, you want your webdav folders to be private, adding authentication to them.
So you need to create the webdav and zotero users and add the passwords to an htpasswd file.
Even though you could use a single user, since you will be configuring several clients with your credentials I encourage you to create the zotero user as well.
This way you can always change the password for zotero without affecting any other application using webdav.
sudo adduser webdav
sudo htpasswd -c /home/webdav/.htpasswd webdav
sudo htpasswd /home/webdav/.htpasswd zotero
sudo mkdir -p /home/webdav/dav/zotero
Enable the site and restart apache:
sudo a2enmod webdav
sudo a2enmod dav_fs
sudo a2ensite webdav
sudo service apache2 restart
At this point everything should be working at http://\<your_host\>:880/dav/zotero
## Setting up NGINX
After the Apache side is working, we can use nginx as a proxy to get cleaner URIs.
In your desired site/location, add this:
location /dav {
client_max_body_size 20M;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $host;
proxy_pass http://127.0.0.1:880;
}
Now just reload nginx:
sudo service nginx force-reload
## Extras
* [Zotero Reader](http://zoteroreader.com/) - HTML5 client
* [Zandy](https://github.com/ajlyon/zandy) - Android Open Source client

View File

@ -221,31 +221,6 @@ header {
padding: 0;
}
#headline {
margin: 0em;
font-size: 4em;
padding-left: 300px;
padding-top: 5px;
font-family: comfortaa;
}
#headline a {
color: black;
padding: 0;
margin: 0;
}
#headline a:hover {
color: white;
text-shadow: 1px 1px #000, -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000;
}
#social {
margin: 0 auto;
padding: 0;
}
/*#social:after {*/
/*content: ".";*/
/*display: block;*/
@ -283,16 +258,6 @@ header {
z-index: 100;
}
.entries dt {
font-weight: bold;
clear: both;
border-top: dashed 1px #CCC;
}
.entries dd {
float: right;
margin: 2px;
}
#navbar {
font-size: 1.2em;
padding-left: 300px;
@ -354,11 +319,6 @@ a {
text-decoration: none;
}
a:hover {
color: #FFF;
text-shadow: 1px 1px #069, -1px -1px 0 #069, 1px -1px 0 #069, -1px 1px 0 #069;
}
.label {
font-family: comfortaa;
border-radius: 10px 0px 0px 10px;
@ -397,7 +357,6 @@ a:hover {
display:inline;
width: 33.333%;
}
@-webkit-keyframes toright {
from {
padding-left: 0px;
@ -420,6 +379,28 @@ a:hover {
}
}
@-webkit-keyframes toleft {
to {
left: -10px;
opacity: 1;
}
from {
left: 300px;
opacity: 0;
}
}
@keyframes toleft {
to {
left: -10px;
opacity: 1;
}
from {
left: 300px;
opacity: 0;
}
}
@-webkit-keyframes appear{
to {
opacity: 1;
@ -438,27 +419,46 @@ a:hover {
}
}
@-webkit-keyframes disappear{
to {
opacity: 0 ;
}
from {
opacity: 1;
}
}
@keyframes disappear{
to {
opacity: 0;
}
from {
opacity: 1;
}
}
#subheadline {
-webkit-animation-name: toright;
-webkit-animation-duration: 4s;
position: absolute;
-webkit-animation-name: toleft;
-webkit-animation-duration: 3s;
-webkit-animation-iteration-count: 1;
-webkit-animation-timing-function: linear;
-webkit-animation-fill-mode: forwards;
animation-name: toright;
animation-duration: 4s;
animation-name: toleft;
animation-duration: 3s;
animation-iteration-count: 1;
animation-timing-function: linear;
animation-fill-mode: forwards;
}
.disappear {
-webkit-animation-name: appear;
-webkit-animation-duration: 4s;
-webkit-animation-name: disappear;
-webkit-animation-duration: 3s;
-webkit-animation-timing-function: linear;
-webkit-animation-iteration-count: 1;
-webkit-animation-fill-mode: forwards;
animation-name: appear;
animation-duration: 4s;
animation-name: disappear;
animation-duration: 3s;
animation-iteration-count: 1;
animation-timing-function: linear;
animation-fill-mode: forwards;

View File

@ -217,39 +217,9 @@ header {
}
#headline {
margin: 0em;
font-size: 4em;
padding-left: 20%;;
padding-top: 5px;
font-family: comfortaa;
}
#headline a {
color: black;
padding: 0;
margin: 0;
}
#headline a:hover {
color: white;
text-shadow: 2px 2px #000, -2px -2px 0 #000, 2px -2px 0 #000, -2px 2px 0 #000;
}
#social {
margin: 0 auto;
padding: 0;
}
/*#social:after {*/
/*content: ".";*/
/*display: block;*/
/*clear: both;*/
/*visibility: hidden;*/
/*line-height: 0;*/
/*height: 0;*/
/*}*/
#social li {
position: relative;
list-style-type: none;
@ -278,15 +248,6 @@ header {
z-index: 100;
}
.entries dt {
font-weight: bold;
clear: both;
border-top: dashed 1px #CCC;
}
.entries dd {
float: right;
margin: 2px;
}
#navbar {
font-size: 1em;
@ -349,11 +310,6 @@ a {
text-decoration: none;
}
a:hover {
color: #FFF;
text-shadow: 1px 1px #069, -1px -1px 0 #069, 1px -1px 0 #069, -1px 1px 0 #069;
}
.label {
font-family: comfortaa;
border-radius: 10px 0px 0px 10px;

View File

@ -10,7 +10,11 @@
#headline {
position: relative;
width: 100%;
margin: 0.5em;
font-size: 2em;
padding: 0.1em;
margin: 0.1em;
font-family: comfortaa;
overflow: auto;
}
#headline a {
@ -104,11 +108,6 @@ a {
text-decoration: none;
}
a:hover {
color: #FFF;
text-shadow: 1px 1px #069, -1px -1px 0 #069, 1px -1px 0 #069, -1px 1px 0 #069;
}
.navbar li.active a:hover {
color: #c00;
}
@ -182,3 +181,7 @@ footer {
/*top: -50px;*/
z-index: 100;
}
.highlighttable {
width: 100%;
}

View File

@ -61,13 +61,14 @@ body {
padding-left: 0.5em;
}
code {
.highlighttable {
display: block;
overflow: auto;
position: relative;
background-color: #EEE;
margin: 0 auto;
width: 80%;
padding: 1.5em 2em;
padding: 0;
border: dashed 1px #AAA;
border-radius: 5px 0px 5px 5px;
-webkit-box-sizing: border-box; /* Safari/Chrome, other WebKit */
@ -75,6 +76,25 @@ code {
box-sizing: border-box; /* Opera/IE 8+ */
}
.highlight {
/*background-color: #EEE;*/
overflow: auto;
margin: 0;
padding: 0.5em;
position: relative;
overflow: auto;
}
.code {
width: 100%;
}
.linenos {
color: #AAA;
padding-right: 0.5em;
border-right: solid 1px #DDD;
}
#navbar {
position: relative;
padding: 0.5em;
@ -122,3 +142,67 @@ code {
margin-left: auto;
margin-right: auto
}
#headline {
position: relative;
margin: 0em;
font-size: 4em;
padding-left: 300px;
padding-top: 5px;
font-family: comfortaa;
}
#headline a {
color: black;
padding: 0;
margin: 0;
}
#headline a.inv {
font-size: 0.8em;
color: white;
text-shadow: 1px 1px #000, -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000;
}
#headline a:hover {
color: white;
text-shadow: 1px 1px #000, -1px -1px 0 #000, 1px -1px 0 #000, -1px 1px 0 #000;
}
#social {
margin: 0 auto;
padding: 0;
}
.entries dt {
font-weight: bold;
clear: both;
border-top: dashed 1px #CCC;
}
.entries dd {
float: right;
margin: 2px;
}
a:hover {
text-shadow: 0.5px 0.5px #CCC, -0.5px -0.5px 0 #CCC, 0.5px -0.5px 0 #CCC, -0.5px 0.5px 0 #CCC;
}
.tag:hover * {
-webkit-transform: rotate(-4deg);
-moz-transform: rotate(-4deg);
transform: rotate(-4deg);
color: black;
text-shadow: none ;
transform-origin: 95% 50%;
-moz-transform-origin: 95% 50%;
-webkit-transform-origin: 95% 50%;
}
.tag * {
display: inline-block;
}
.hll {
background: yellow;
}

View File

@ -0,0 +1 @@
.highlight{background-color:#073642;color:#93a1a1}.highlight .c{color:#586e75 !important;font-style:italic !important}.highlight .cm{color:#586e75 !important;font-style:italic !important}.highlight .cp{color:#586e75 !important;font-style:italic !important}.highlight .c1{color:#586e75 !important;font-style:italic !important}.highlight .cs{color:#586e75 !important;font-weight:bold !important;font-style:italic !important}.highlight .err{color:#dc322f !important;background:none !important}.highlight .k{color:#cb4b16 !important}.highlight .o{color:#93a1a1 !important;font-weight:bold !important}.highlight .p{color:#93a1a1 !important}.highlight .ow{color:#2aa198 !important;font-weight:bold !important}.highlight .gd{color:#93a1a1 !important;background-color:#372c34 !important;display:inline-block}.highlight .gd .x{color:#93a1a1 !important;background-color:#4d2d33 !important;display:inline-block}.highlight .ge{color:#93a1a1 !important;font-style:italic !important}.highlight .gr{color:#aa0000}.highlight .gh{color:#586e75 !important}.highlight .gi{color:#93a1a1 !important;background-color:#1a412b !important;display:inline-block}.highlight .gi .x{color:#93a1a1 !important;background-color:#355720 !important;display:inline-block}.highlight .go{color:#888888}.highlight .gp{color:#555555}.highlight .gs{color:#93a1a1 !important;font-weight:bold !important}.highlight .gu{color:#6c71c4 !important}.highlight .gt{color:#aa0000}.highlight .kc{color:#859900 !important;font-weight:bold !important}.highlight .kd{color:#268bd2 !important}.highlight .kp{color:#cb4b16 !important;font-weight:bold !important}.highlight .kr{color:#d33682 !important;font-weight:bold !important}.highlight .kt{color:#2aa198 !important}.highlight .n{color:#268bd2 !important}.highlight .na{color:#268bd2 !important}.highlight .nb{color:#859900 !important}.highlight .nc{color:#d33682 !important}.highlight .no{color:#b58900 !important}.highlight .ni{color:#800080}.highlight .nl{color:#859900 !important}.highlight .ne{color:#268bd2 !important;font-weight:bold !important}.highlight .nf{color:#268bd2 !important;font-weight:bold !important}.highlight .nn{color:#b58900 !important}.highlight .nt{color:#268bd2 !important;font-weight:bold !important}.highlight .nx{color:#b58900 !important}.highlight .bp{color:#999999}.highlight .vc{color:#008080}.highlight .vg{color:#268bd2 !important}.highlight .vi{color:#268bd2 !important}.highlight .nv{color:#268bd2 !important}.highlight .w{color:#bbbbbb}.highlight .mf{color:#2aa198 !important}.highlight .m{color:#2aa198 !important}.highlight .mh{color:#2aa198 !important}.highlight .mi{color:#2aa198 !important}.highlight .mo{color:#009999}.highlight .s{color:#2aa198 !important}.highlight .sb{color:#d14}.highlight .sc{color:#d14}.highlight .sd{color:#2aa198 !important}.highlight .s2{color:#2aa198 !important}.highlight .se{color:#dc322f !important}.highlight .sh{color:#d14}.highlight .si{color:#268bd2 !important}.highlight .sx{color:#d14}.highlight .sr{color:#2aa198 !important}.highlight .s1{color:#2aa198 !important}.highlight .ss{color:#990073}.highlight .il{color:#009999}.highlight div .gd,.highlight div .gd .x,.highlight div .gi,.highlight div .gi .x{display:inline-block;width:100%}

View File

@ -0,0 +1,69 @@
.hll { background-color: #ffffcc }
.c { color: #586E75 } /* Comment */
.err { color: #93A1A1 } /* Error */
.g { color: #93A1A1 } /* Generic */
.k { color: #859900 } /* Keyword */
.l { color: #93A1A1 } /* Literal */
.n { color: #93A1A1 } /* Name */
.o { color: #859900 } /* Operator */
.x { color: #CB4B16 } /* Other */
.p { color: #93A1A1 } /* Punctuation */
.cm { color: #586E75 } /* Comment.Multiline */
.cp { color: #859900 } /* Comment.Preproc */
.c1 { color: #586E75 } /* Comment.Single */
.cs { color: #859900 } /* Comment.Special */
.gd { color: #2AA198 } /* Generic.Deleted */
.ge { color: #93A1A1; font-style: italic } /* Generic.Emph */
.gr { color: #DC322F } /* Generic.Error */
.gh { color: #CB4B16 } /* Generic.Heading */
.gi { color: #859900 } /* Generic.Inserted */
.go { color: #93A1A1 } /* Generic.Output */
.gp { color: #93A1A1 } /* Generic.Prompt */
.gs { color: #93A1A1; font-weight: bold } /* Generic.Strong */
.gu { color: #CB4B16 } /* Generic.Subheading */
.gt { color: #93A1A1 } /* Generic.Traceback */
.kc { color: #CB4B16 } /* Keyword.Constant */
.kd { color: #268BD2 } /* Keyword.Declaration */
.kn { color: #859900 } /* Keyword.Namespace */
.kp { color: #859900 } /* Keyword.Pseudo */
.kr { color: #268BD2 } /* Keyword.Reserved */
.kt { color: #DC322F } /* Keyword.Type */
.ld { color: #93A1A1 } /* Literal.Date */
.m { color: #2AA198 } /* Literal.Number */
.s { color: #2AA198 } /* Literal.String */
.na { color: #93A1A1 } /* Name.Attribute */
.nb { color: #B58900 } /* Name.Builtin */
.nc { color: #268BD2 } /* Name.Class */
.no { color: #CB4B16 } /* Name.Constant */
.nd { color: #268BD2 } /* Name.Decorator */
.ni { color: #CB4B16 } /* Name.Entity */
.ne { color: #CB4B16 } /* Name.Exception */
.nf { color: #268BD2 } /* Name.Function */
.nl { color: #93A1A1 } /* Name.Label */
.nn { color: #93A1A1 } /* Name.Namespace */
.nx { color: #93A1A1 } /* Name.Other */
.py { color: #93A1A1 } /* Name.Property */
.nt { color: #268BD2 } /* Name.Tag */
.nv { color: #268BD2 } /* Name.Variable */
.ow { color: #859900 } /* Operator.Word */
.w { color: #93A1A1 } /* Text.Whitespace */
.mf { color: #2AA198 } /* Literal.Number.Float */
.mh { color: #2AA198 } /* Literal.Number.Hex */
.mi { color: #2AA198 } /* Literal.Number.Integer */
.mo { color: #2AA198 } /* Literal.Number.Oct */
.sb { color: #586E75 } /* Literal.String.Backtick */
.sc { color: #2AA198 } /* Literal.String.Char */
.sd { color: #93A1A1 } /* Literal.String.Doc */
.s2 { color: #2AA198 } /* Literal.String.Double */
.se { color: #CB4B16 } /* Literal.String.Escape */
.sh { color: #93A1A1 } /* Literal.String.Heredoc */
.si { color: #2AA198 } /* Literal.String.Interpol */
.sx { color: #2AA198 } /* Literal.String.Other */
.sr { color: #DC322F } /* Literal.String.Regex */
.s1 { color: #2AA198 } /* Literal.String.Single */
.ss { color: #2AA198 } /* Literal.String.Symbol */
.bp { color: #268BD2 } /* Name.Builtin.Pseudo */
.vc { color: #268BD2 } /* Name.Variable.Class */
.vg { color: #268BD2 } /* Name.Variable.Global */
.vi { color: #268BD2 } /* Name.Variable.Instance */
.il { color: #2AA198 } /* Literal.Number.Integer.Long */

View File

Before

Width:  |  Height:  |  Size: 193 KiB

After

Width:  |  Height:  |  Size: 193 KiB

View File

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

Before

Width:  |  Height:  |  Size: 134 KiB

After

Width:  |  Height:  |  Size: 134 KiB

View File

Before

Width:  |  Height:  |  Size: 3.2 KiB

After

Width:  |  Height:  |  Size: 3.2 KiB

View File

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 11 KiB

View File

Before

Width:  |  Height:  |  Size: 134 KiB

After

Width:  |  Height:  |  Size: 134 KiB

View File

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

Before

Width:  |  Height:  |  Size: 194 KiB

After

Width:  |  Height:  |  Size: 194 KiB

View File

Before

Width:  |  Height:  |  Size: 218 KiB

After

Width:  |  Height:  |  Size: 218 KiB

View File

Before

Width:  |  Height:  |  Size: 207 KiB

After

Width:  |  Height:  |  Size: 207 KiB

View File

@ -0,0 +1,22 @@
{% extends "base.html" %}
{% block content %}
<div class="postnav">
{% if article.prev_article %}
<a href="{{ article.prev_article.url }}"><span class="previouspost"><i class="icon-chevron-sign-left"></i> {{ article.prev_article.title }}</a></span>
{% endif %}
{% if article.next_article %}
<span class='nextpost'><a href="{{ SITE_URL }}/{{ article.next_article.url }}">{{ article.next_article.title }} <i class="icon-chevron-sign-right"></i></span></a>
{%endif%}
</div>
<div class="posthead">
<h2 class="title">{{ article.title }}</h2>
<span class="meta date">{{ article.date | date_to_string }}</span>
{% for c in article.tags %}
<a class="tag" href="/tag/{{ c }}.html"><span class="label label-default">{{ c }}</span></a>
{% endfor %}
</div>
<div class="post">
{{ article.content }}
</div>
{% endblock %}

View File

@ -3,38 +3,43 @@
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<title>{{ page.title }}</title>
<title>{% block content_title %}{% endblock %}</title>
<meta name="viewport" content="width=device-width">
<!-- syntax highlighting CSS -->
<link rel="stylesheet" href="/css/syntax.css">
<link rel="stylesheet" href="{{ SITE_URL }}/theme/css/solarized-dark.css">
<!--<link href="/css/bootstrap.css" rel="stylesheet">-->
<link rel="stylesheet" href="/font-awesome/css/font-awesome.min.css">
<link rel="stylesheet" href="{{ SITE_URL }}/theme/font-awesome/css/font-awesome.min.css">
<!--<link rel="stylesheet" href="/css/bootstrap-responsive.min.css">-->
<!-- Custom CSS -->
<link rel="stylesheet" media="only screen" href="/css/main.css">
<link rel="stylesheet" media="only screen and (min-width: 0px) and (max-width: 599px)" href="/css/main-xs.css">
<link rel="stylesheet" media="only screen and (min-width: 600px) and (max-width: 1199px)" href="/css/main-medium.css">
<link rel="stylesheet" media="only screen and (min-width: 1200px)" href="/css/main-desktop.css">
<link rel="stylesheet" media="only screen" href="{{ SITE_URL }}/theme/css/main.css">
<link rel="stylesheet" media="only screen and (min-width: 0px) and (max-width: 599px)" href="{{ SITE_URL }}/theme/css/main-xs.css">
<link rel="stylesheet" media="only screen and (min-width: 600px) and (max-width: 1199px)" href="{{ SITE_URL }}/theme/css/main-medium.css">
<link rel="stylesheet" media="only screen and (min-width: 1200px)" href="{{ SITE_URL }}/theme/css/main-desktop.css">
<link href='http://fonts.googleapis.com/css?family=Open+Sans:300|Comfortaa' rel='stylesheet' type='text/css'>
</head>
<body>
<div id="container" class="container">
<header id="header">
<h1 id="headline"><a href="/">balkian</a><a href="/">.com</a></h1>
<h1 id="headline"><a href="/">balkian</a><a class="inv" href="/">.com</a></h1>
<div id="navbar" class="navbar navbar-inverse navbar-static-bottom">
<div class="container">
<div class="navbar-header">
<ul class="nav navbar-nav">
<li {% if page.categories contains "blog" %}class="active" {% endif %}>
<li {% if article or not page %}class="active" {% endif %}>
<a href="/"><i class="icon-home icon-large"></i> Blog</a>
</li>
{% for p in site.pages reversed %} {% if p.url contains "/page" %} {% else %} {% if p.url != "/index.html" %}
<li {% if p.url == page.url %} class="active" {% else %} {% if page.categories contains p.categories %} class="active" {% endif %} {% endif %} >
<a href="{{ p.url | remove: "/index.html" }}">{{ p.title }}</a>
</li> {% endif %} {% endif %}
{% for p in pages %} {% if "/page" in p.url %}
{% else %}
{% if p.url != "/index.html" %}
<li {% if page and p.url == page.url %} class="active"
{% endif %} >
<a href="/{{ p.url }}">{{ p.title }}</a>
</li>
{% endif %}
{% endif %}
{% endfor %}
<!--<li class="dropdown">-->
<!--<a href="#" class="dropdown-toggle" data-toggle="dropdown">Dropdown <b class="caret"></b></a>-->
@ -58,21 +63,21 @@
<div class="flipper sticky">
<div class="front">
<!-- front content -->
<img id="avatar" width=100% src="/img/me.png">
<img id="avatar" width=100% src="{{ SITE_URL }}/theme/img/me.png">
</div>
<div class="back">
<!-- back content -->
<img id="picture" width=100% src="/img/me.jpg">
<img id="picture" width=100% src="{{ SITE_URL }}/theme/img/me.jpg">
</div>
</div>
</div>
<div class="entries">
<h1 class="title">Latest entries</h1>
<dl>
{% for p in site.posts limit: 5 %}
<dt><a href="{{ p.url }}">{{ p.title }}</a></dt>
{% for p in articles %}
<dt><a href="{{ SITE_URL }}/{{ p.url }}">{{ p.title }}</a></dt>
{% for c in p.tags %}
<dd class="label label-default">{{ c }}</dd>
<a class="tag" href="/tag/{{ c }}.html"><dd class="label label-default">{{ c }}</dd></a>
{% endfor %}
{% endfor %}
</dl>
@ -81,7 +86,8 @@
</div>
<div id="content">
<!--Body content-->
{{ content }}
{% block content %}
{% endblock %}
</div>
<div class="clear"></div>
</div>
@ -105,7 +111,7 @@
</footer>
</div>
<script src="/js/jquery-2.0.2.min.js"></script>
<script src="{{ SITE_URL }}/theme/js/jquery-2.0.2.min.js"></script>
<!--<script src="/js/bootstrap.min.js"></script>-->
</body>
</html>

View File

@ -0,0 +1,52 @@
{% extends "base.html" %}
<!-- Pagination links -->
{% block content %}
{% if articles_paginator.num_pages > 0 %}
<div class="pagination pag-top">
{% if articles_page.has_previous() %}
<span class="previouspage"><i class="icon-chevron-sign-left"></i><a href="/{% if articles_page.has_previous() > 2 %}page{{ articles_page.previous_page_number() }}{% endif %}"> Newer Posts</a></span>
{% else %}
<span class="previouspage" style="visibility:hidden;"><i class="icon-chevron-sign-left"></i> Newer Posts</span>
{% endif %}
<span class="page_number ">Page {{ articles_page.number }} of {{ articles_paginator.num_pages }}</span>
{% if articles_page.has_next() %}
<span class="nextpage"><a href="/page{{ articles_page.next_page_number() }}"> Older Posts </a> <i class="icon-chevron-sign-right"></i></span>
{% else %}
<span class="nextpage" style="visibility:hidden;">Older Posts <i class="icon-chevron-sign-right"></i></span>
{% endif %}
</div>
{% endif %}
{% block pre_articles %}
{% endblock %}
<!-- This loops through the paginated posts -->
{% for post in articles_page.object_list %}
<div class="posthead">
<h2><a href="{{ SITE_URL }}/{{ post.url }}" class="title">{{ post.title }}</a></h2>
<span class="date">{{ post.date | date_to_string }}</span>
{% for c in post.tags %}
<a class="tag" href="{{ SITE_URL }}/tag/{{ c }}.html"><span class="label label-success tag">{{ c }}</span></a>
{% endfor %}
</div>
<div class="excerpt">
{{ post.summary }}
</div>
<span><a href="{{ SITE_URL }}/{{ post.url }}"><i class="icon-pl2s"></i> Read more...</a></span>
{% endfor %}
{% if articles_paginator.num_pages > 0 %}
<div class="pagination pag-bottom">
{% if articles_page.has_previous() %}
<span class="previouspage"><i class="icon-chevron-sign-left"></i><a href="/{% if articles_page.next_page_number() > 2 %}page{{ articles_page.previous_page_number }}{% endif %}"> Newer Posts</a></span>
{% else %}
<span class="previouspage" style="display:none;"><i class="icon-chevron-sign-left"></i> Newer Posts</span>
{% endif %}
<span class="page_number ">Page {{ articles_page.number }} of {{ articles_paginator.num_pages }}</span>
{% if articles_page.has_next() %}
<span class="nextpage"><a href="/page{{ articles_page.next_page_number() }}"> Older Posts </a> <i class="icon-chevron-sign-right"></i></span>
{% else %}
<span class="nextpage" style="display:none;">Older Posts <i class="icon-chevron-sign-right"></i></span>
{% endif %}
</div>
{% endif %}
{% endblock %}

View File

@ -0,0 +1,4 @@
{% extends "base.html" %}
{% block content %}
{{ page.content }}
{% endblock %}

View File

@ -0,0 +1,5 @@
{% extends "index.html" %}
{% block title %}{{ SITENAME }} - {{ tag }}{% endblock %}
{% block pre_articles %}
<h1>Entries tagged: {{ tag }}</h1>
{% endblock %}

View File

@ -0,0 +1,44 @@
Creating my web
###############
:date: 2013-08-22 14:14:22
:tags: starters, javascript, ruby, github, git
Finally, I've decided to set up a decent personal page. I have settled
for github-pages because I like the idea of keeping my site in a
repository and having someone else host and deploy it for me. The site
will be really simple, mostly static files. Thanks to Github,
`Jekyll <http://jekyllrb.com>`__ will automatically generate static
pages for my posts every time I commit anything new to this repository.
But Jekyll can be used independently, so if I ever choose to host the
site myself, I can do it quite easily. Another thing that I liked about
this approach is that the generated html files can be used in the
future, and I will not need Jekyll to serve it. Jekyll is really simple
and most of the things are written in plain html. That means that
everything could be easily reused if I ever choose to change to another
blogging framework (e.g. pelical). But, for the time being, I like the
fact that Github takes care of the compilation as well, so I can simply
modify or add files through the web interface should I need to.
I hadn't played with HTML and CSS for a while now, so I also wanted to
use this site as a playground. At some point, I realised I was doing
mostly everything in plain HTML and CSS, and decided to keep it like
that for as long as possible. As of this writing, I haven't included any
Javascript code in the page. Probably I will use some to add my
`gists <http://gist.github.com/balkian>`__ and
`repositories <http://github.com/balkian>`__, but we will see about
that.
I think the code speaks for itself, so you can check out `my repository
on Github <http://github.com/balkian/balkian.github.com>`__. You can
clone and deploy it easily like this:
.. code-block:: bash
git clone
https://github.com/balkian/balkian.github.com cd balkian.github.com
jekyll serve -w
I will keep updating this post with information about: \* Some Jekyll
plugins that might be useful \* What CSS tricks I learnt \* The webfonts
I used \* The badge on the left side of the page

View File

@ -0,0 +1,12 @@
Remove git files with globbing
##############################
:date: 2013-08-22 23:14:00
:tags: git
A simple trick. If you want to remove all the '.swp' files from a git
repository, just use:
.. code-block:: bash
git rm --cached '\*\*.swp'

View File

@ -0,0 +1,121 @@
Updating EuroLoveMap
####################
:date: 2014-03-27 14:00:00
:tags: javascript, python, heroku
As part of the `OpeNER
hackathon <http://www.opener-project.org/2013/07/18/opener-hackathon-in-amsterdam/>`__
we decided to build a prototype that would allow us to compare how
different countries feel about several topics. We used the OpeNER
pipeline to get the sentiment from a set of newspaper articles we
gathered from media in several languages. Then we aggregated those
articles by category and country (using the source of the article or the
language it was written in), obtaining the "overall feeling" of each
country about each topic. Then, we used some fancy JavaScript to make
sense out of the raw information.
It didn't go too bad, it turns out `we
won <http://eurosentiment.eu/wp-content/uploads/2013/07/BOLv9qnCIAAJEek.jpg>`__.
Now, it was time for a face-lift. I used this opportunity to play with
new technologies and improve it:
- Using Flask, this time using python 3.3 and Bootstrap 3.0
- Cool HTML5+JS cards (thanks to
`pastetophone <http://pastetophone.com>`__)
- Automatic generation of fake personal data to test the interface
- Obfuscation of personal emails
Publishing a Python 3 app on Heroku
-----------------------------------
`seen here <http://eurolovemap.herokuapp.com/>`__
.. code-block:: bash
mkvirtualenv -p /usr/bin/python3.3 eurolovemap
Since Heroku uses python 2.7 by default, we have to tell it which
version we want, although it supports python 3.4 as well. I couldn't get
python 3.4 working using the
`deadsnakes <https://launchpad.net/~fkrull/+archive/deadsnakes>`__ ppa,
so I used python 3.3 instead, which works fine but is not officially
supported. Just create a file named *runtime.txt* in your project root,
with the python version you want to use:
.. code-block:: bash
python-3.3.1
Don't forget to freeze your dependencies so Heroku can install them:
``bash pip freze > requirements.txt``
Publishing personal emails
--------------------------
There are really sophisticated and effective ways to obfuscate personal
emails so that spammers cannot easily grab yours. However, this time I
needed something really simple to hide our emails from the simplest form
of crawlers. Most of the team are in academia somehow, so in the end all
our emails are available in sites like Google Scholar. Anyway, nobody
likes getting spammed so I settled for a custom `Caesar
cipher <http://en.wikipedia.org/wiki/Caesar_cipher>`__. Please, don't
use it for any serious application if you are concerned about being
spammed.
.. code-block:: python
def blur_email(email):
return "".join([chr(ord(i)+5) for i in email])
And this is the client side:
.. code-block:: javascript
window.onload = function(){
elems = document.getElementsByClassName('profile-email');
for(var e in elems){
var blur = elems[e].innerHTML;
var email = "";
for(var s in blur){
var a = blur.charCodeAt(s)
email = email+String.fromCharCode(a-5);
}
elems[e].innerHTML = email;
}
}
Unfortunately, this approach does not hide your email from anyone using
`PhantomJS <http://phantomjs.org/>`__,
`ZombieJS <http://zombie.labnotes.org/>`__ or similar. For that, other
approaches like generating a picture with the address would be
necessary. Nevertheless, it is overkill for a really simple ad-hoc
application with custom formatting and just a bunch of emails that would
easily be grabbed manually.
Generation of fake data
-----------------------
To test the contact section of the site, I wanted to populate it with
fake data. `Fake-Factory <https://github.com/joke2k/faker>`__ is an
amazing library that can generate fake data of almost any kind: emails,
association names, acronyms... It even lets you localise the results
(get Spanish names, for instance) and generate factories for certain
classes (à la Django).
But I also wanted pictures, enter `Lorem
Pixel <http://lorempixel.com/>`__. With its API you can generate
pictures of almost any size, for different topics (e.g. nightlife,
people) and with a custom text. You can even use an index, so it will
always show the same picture.
For instance, the picture below is served through Lorem Pixel.
.. figure:: http://lorempixel.com/400/200/nightlife/
:alt: This picture is generated with LoremIpsum
This picture is generated with LoremIpsum
By the way, if you only want cat pictures, take a look at
`Placekitten <http://placekitten.com/>`__. And for NSFW text, there's
the `Samuel L. Jackson Ipsum <http://slipsum.com/>`__

View File

@ -0,0 +1,106 @@
Publishing in PyPi
##################
:date: 2014-09-27 10:00:00
:tags: github, python, pypi
Developing a python module and publishing it on Github is cool, but most
of the times you want others to download and use it easily. That is the
role of PyPi, the python package repository. In this post I show you how
to publish your package in less than 10 minutes.
Choose a fancy name
-------------------
If you haven't done so yet, take a minute or two to think about this. To
publish on PyPi you need a name for your package that isn't taken.
What's more, a catchy and unique name will help people remember your
module and feel more inclined to at least try it.
The package name should hint what your module does, but that's not
always the case. That's your call. I personally put uniqueness and
memorability over describing the functionality.
Create a .pypirc configuration file
-----------------------------------
.. code:: cfg
[distutils] # this tells distutils what package indexes you can push to
index-servers =
pypi # the live PyPI
pypitest # test PyPI
[pypi] # authentication details for live PyPI
repository = https://pypi.python.org/pypi
username = { your_username }
password = { your_password } # not necessary
[pypitest] # authentication details for test PyPI
repository = https://testpypi.python.org/pypi
username = { your_username }
As you can see, you need to register both in the `main pypi
repository <https://pypi.python.org/pypi?%3Aaction=register_form>`__ and
the `testing
server <https://testpypi.python.org/pypi?%3Aaction=register_form>`__.
The usernames and passwords might be different, that is up to you!
Prepare your package
--------------------
::
root-dir/ # Any name you want
setup.py
setup.cfg
LICENSE.txt
README.md
mypackage/
__init__.py
foo.py
bar.py
baz.py
setup.cfg
~~~~~~~~~
.. code:: cfg
[metadata]
description-file = README.md
The markdown README is the *de facto* standard in Github, but you can
also use rST (reStructuredText), the standard in the python community.
setup.py
~~~~~~~~
{% highlight python %} from distutils.core import setup setup( name =
'mypackage', packages = ['mypackage'], # this must be the same as the
name above version = '{ version }', description = '{ description }',
author = '{ name }', author\_email = '{ email }', url =
'https://github.com/{user}/{package}', # URL to the github repo
download\_url = 'https://github.com/{user}/{repo}/tarball/{version}',
keywords = ['websockets', 'display', 'd3'], # list of keywords that
represent your package classifiers = [], ) {% endhighlight %}
You might notice that the download\_url points to a Github URL. We could
host our package anywhere, but Github is a convenient option. To create
the tarball and the zip packages, you only need to tag a tag in your
repository and push it to github:
::
git tag {version} -m "{ Description of this tag/version}"
git push --tags origin master
Push to the testing/main pypi server
------------------------------------
It is advisable that you try your package on the test repository and fix
any problems first. The process is simple:
``python setup.py register -r {pypitest/pypi} python setup.py sdist upload -r {pypitest/pypi}``
If everything went as expected, you can now install your package through
pip and browse your package's page. For instance, check my senpy
package: https://pypi.python.org/pypi/senpy ``pip install senpy``

View File

@ -0,0 +1,72 @@
Proxies with Apache and python
##############################
:date: 2014-10-09 10:00:00
:tags: python, apache, proxy, gunicorn, uwsgi
This is a quick note on proxying a local python application (e.g. flask)
to a subdirectory in Apache. This assumes that the file wsgi.py contains
a WSGI application with the name *application*. Hence, wsgi:application.
Gunicorn
--------
.. code-block:: apache
<Location /myapp/>
ProxyPass http://127.0.0.1:8888/myapp/
ProxyPassReverse http://127.0.0.1:8888/myapp/
RequestHeader set SCRIPT_NAME "/myapp/"
</Location>
**Important**: *SCRIPT\_NAME* and the end of *ProxyPass* URL **MUST BE
THE SAME**. Otherwise, Gunicorn will fail miserably.
Try it with:
``bash venv/bin/gunicorn -w 4 -b 127.0.0.1:8888 --log-file - --access-logfile - wsgi:application``
UWSGI
-----
This is a very simple configuration. I will try to upload one with more
options for uwsgi (in a .ini file).
.. code-block:: apache
<Location /myapp/>
SetHandler uwsgi_handler
uWSGISocker 127.0.0.1:8888
</Location>
Try it with:
.. code-block:: bash
uwsgi --socket 127.0.0.1:8888 -w wsgi:application
Extra: Supervisor
~~~~~~~~~~~~~~~~~
If everything went as expected, you can wrap your command in a
supervisor config file and let it handle the server for you.
.. code-block:: ini
[unix_http_server]
file=/tmp/myapp.sock ; path to your socket file
[supervisord]
logfile = %(here)s/logs/supervisor.log
childlogdir = %(here)s/logs/
[rpcinterface:supervisor]
supervisor.rpcinterface_factory = supervisor.rpcinterface:make_main_rpcinterface
[supervisorctl]
logfile = %(here)s/logs/supervisorctl.log
serverurl=unix:///tmp/supervisor.sock ; use a unix:// URL for a unix socket
[program:myapp]
command = venv/bin/gunicorn -w 4 -b 0.0.0.0:5000 --log-file %(here)s/logs/gunicorn.log --access-logfile - wsgi:application
directory = %(here)s
environment = PATH=%(here)s/venv/bin/
logfile = %(here)s/logs/myapp.log

View File

@ -0,0 +1,105 @@
Zotero
######
:date: 2014-12-09 12:12:12
:tags: zotero, webdav, nginx, apache
`Zotero <https://www.zotero.org/>`__ is an Open Source tool that lets
you organise your bibliography, syncing it with the cloud. Unlike other
alternatives such as `Mendeley <http://www.mendeley.com>`__, Zotero can
upload the attachments and data to a private cloud via WebDav.
If you use nginx as your web server, know that even though it provides
partial support for webdav, Zotero needs more than that. Hence, you will
need another webdav server, and optionally let nginx proxy to it. This
short post provides the basics to get that set-up working under
Debian/Ubuntu.
Setting up Apache
-----------------
First we need to install Apache:
.. code-block:: bash
sudo apt-get install apache2
Change the head of "/etc/apache2/sites-enabled/000-default" to:
.. code-block:: apache
<VirtualHost *:880>
Then, create a file /etc/apache2/sites-available/webdav:
.. code-block:: apache
Alias /dav /home/webdav/dav
<Location /dav>
Dav on
Order Allow,Deny
Allow from all
Dav On
Options +Indexes
AuthType Basic
AuthName DAV
AuthBasicProvider file
AuthUserFile /home/webdav/.htpasswd
Require valid-user
</Location>
Ideally, you want your webdav folders to be private, adding
authentication to them. So you need to create the webdav and zotero
users and add the passwords to an htpasswd file. Even though you could
use a single user, since you will be configuring several clients with
your credentials I encourage you to create the zotero user as well. This
way you can always change the password for zotero without affecting any
other application using webdav.
.. code-block:: bash
sudo adduser webdav
sudo htpasswd -c /home/webdav/.htpasswd webdav
sudo htpasswd /home/webdav/.htpasswd zotero
sudo mkdir -p /home/webdav/dav/zotero
Enable the site and restart apache:
.. code-block:: bash
sudo a2enmod webdav
sudo a2enmod dav_fs
sudo a2ensite webdav
sudo service apache2 restart
At this point everything should be working at
http://<your\_host>:880/dav/zotero
Setting up NGINX
----------------
After the Apache side is working, we can use nginx as a proxy to get
cleaner URIs. In your desired site/location, add this:
.. code-block:: nginx
location /dav {
client_max_body_size 20M;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
proxy_set_header Host $host;
proxy_pass http://127.0.0.1:880;
}
Now just reload nginx:
.. code-block:: bash
sudo service nginx force-reload
Extras
------
- `Zotero Reader <http://zoteroreader.com/>`__ - HTML5 client
- `Zandy <https://github.com/ajlyon/zandy>`__ - Android Open Source
client

View File

Before

Width:  |  Height:  |  Size: 4.2 KiB

After

Width:  |  Height:  |  Size: 4.2 KiB

View File

Before

Width:  |  Height:  |  Size: 50 KiB

After

Width:  |  Height:  |  Size: 50 KiB

View File

@ -1,9 +1,6 @@
---
layout: default
title: About
---
<h2>This is me<span class="disappear">...</span></h2>
<h3 id="subheadline">... I mean, my website</h3>
<h2>This is <span class="disappear">me.</span><span id="subheadline"><span class="disappear">... I mean,</span> my website</span></h2>
<div id="about">
<ul>

103
content/pages/cv.md Normal file
View File

@ -0,0 +1,103 @@
title: CV
<link rel="stylesheet" media="only screen and (min-width: 0px) and (max-width: 599)" href="/theme/css/cv-xs.css">
<link rel="stylesheet" media="only screen and (min-width: 600px) and (max-width: 1200px)" href="/theme/css/cv-medium.css">
<link rel="stylesheet" media="only screen and (min-width: 1200px)" href="/theme/css/cv-desktop.css">
<div id="post">
<div id="contact-info" class="vcard">
<!-- Microformats! -->
<h1 class="fn">J. Fernando Sánchez</h1>
<p>
Email: <a class="email" href="mailto:admin@balkian.com">admin@balkian.com</a>
</p>
</div>
<div id="objective">
<p>
I am a curious young engineer who happens to enjoy IT both as a career and as a hobby.
</p>
</div>
<div class="clear"></div>
<dl>
<dd class="clear"></dd>
<dt>Education</dt>
<!--<dd>-->
<!--<h2>Postgraduate Researcher (PhD) - Technical University of Madrid (UPM) <span>2012-Present</span></h2>-->
<!--</dd>-->
<dd>
<h2>Telecommunications Engineering <span> <a href="http://www.etsit.upm.es">Technical University of Madrid (UPM)</a> - 2007-2012</span></h2>
</dd>
<dd class="clear"></dd>
<dt>Skills</dt>
<dd>
<h2>Programming Languages</h2>
<p>Used frequently: Python, Javascript/CoffeeScript, Bash/Shell, Java and Ruby</p>
<p>Also programmed in: PHP, C, C++, Objective C and Haskell</p>
<h2>Frameworks and libraries</h2>
<p>Node.js, Django, Ruby on Rails, QT, GTK2, JASON, RDFLib, Weka</p>
<h2>Development tools</h2>
<p>Git, Eclipse, Netbeans, Android SDK</p>
<h2>Others</h2>
<p>Latex, XMPP, GIMP, Inkscape</p>
<h2>Social Skills</h2>
<p>Working with and leading international teams. Presentation and communication skills: I conducted several presentations to audiences of ~100 people.</p>
</dd>
<dd class="clear"></dd>
<dt>Experience</dt>
<dd>
<h2>Graduate Research Fellow<span><a href="http://gsi.dit.upm.es">Intelligent Systems Group (GSI)</a> (GSI) - 2008-2012</span></h2>
<ul>
<li>Worked with Agent and Semantic technologies</li>
<li>Conducted my master thesis: Design and Implementation of an Agent Architecture Based on Web Hooks</li>
</ul>
<h2>IT Coordinator<span><a href="http://eestec.net">EESTEC International</a> - 2012-2013</span></h2>
<ul>
<li>Coordinated the work of a small international IT Team</li>
<li>In charge of the administration of the IT infrastructure of EESTEC: Plone portal, Mailman mailing lists, etc.</li>
</ul>
<h2>Oversight Committee Member<span><a href="http://eestec.net">EESTEC International</a> - 2012-2013</span></h2>
<ul>
<li>Supervised the work of the International Board</li>
</ul>
<h2>Vice Chairman for External Affairs <span><a href="http://eestec.net">EESTEC International</a> - 2012-2013</span></h2>
<ul>
<li>Established connections with other Student Associations</li>
<li>Helped found new Observers (Local Groups)</li>
<li>Carried out International Board duties</li>
</ul>
</dd>
<dd class="clear"></dd>
<!--<dt>Hobbies</dt>-->
<!--<dd>Music, Sports and Cinema</dd>-->
<!--<dd class="clear"></dd>-->
<dt>References</dt>
<dd>Available on request</dd>
<dd class="clear"></dd>
</dl>
<div class="clear"></div>
</div>

View File

@ -1,7 +1,5 @@
---
title: Projects
layout: default
title: "Projects"
---
Ongoing Projects
================

View File

@ -1,7 +1,5 @@
---
layout: default
title: "To-Do"
---
title: To-Do
<h2>Things To Do</h2>
This is intended as my public "todo.txt". Both to keep it accessible and to feel ashamed of the many things I leave undone:
<ul class="todo">

View File

@ -1,109 +0,0 @@
---
layout: default
title: "CV"
date: 2013-07-03 14:14:22
categories: cv resume
---
<link rel="stylesheet" media="only screen and (min-width: 0px) and (max-width: 599)" href="/css/cv-xs.css">
<link rel="stylesheet" media="only screen and (min-width: 600px) and (max-width: 1200px)" href="/css/cv-medium.css">
<link rel="stylesheet" media="only screen and (min-width: 1200px)" href="/css/cv-desktop.css">
<div id="post">
<!--<img src="images/cthulu.png" alt="Photo of Cthulu" id="pic" />-->
<div id="contact-info" class="vcard">
<!-- Microformats! -->
<h1 class="fn">J. Fernando Sánchez</h1>
<p>
Email: <a class="email" href="mailto:admin@balkian.com">admin@balkian.com</a>
</p>
</div>
<div id="objective">
<p>
I am a curious young engineer who happens to enjoy IT both as a career and as a hobby.
</p>
</div>
<div class="clear"></div>
<dl>
<dd class="clear"></dd>
<dt>Education</dt>
<!--<dd>-->
<!--<h2>Postgraduate Researcher (PhD) - Technical University of Madrid (UPM) <span>2012-Present</span></h2>-->
<!--</dd>-->
<dd>
<h2>Telecommunications Engineering <span> <a href="http://www.etsit.upm.es">Technical University of Madrid (UPM)</a> - 2007-2012</span></h2>
</dd>
<dd class="clear"></dd>
<dt>Skills</dt>
<dd>
<h2>Programming Languages</h2>
<p>Used frequently: Python, Javascript/CoffeeScript, Bash/Shell, Java and Ruby</p>
<p>Also programmed in: PHP, C, C++, Objective C and Haskell</p>
<h2>Frameworks and libraries</h2>
<p>Node.js, Django, Ruby on Rails, QT, GTK2, JASON, RDFLib, Weka</p>
<h2>Development tools</h2>
<p>Git, Eclipse, Netbeans, Android SDK</p>
<h2>Others</h2>
<p>Latex, XMPP, GIMP, Inkscape</p>
<h2>Social Skills</h2>
<p>Working with and leading international teams. Presentation and communication skills: I conducted several presentations to audiences of ~100 people.</p>
</dd>
<dd class="clear"></dd>
<dt>Experience</dt>
<dd>
<h2>Graduate Research Fellow<span><a href="http://gsi.dit.upm.es">Intelligent Systems Group (GSI)</a> (GSI) - 2008-2012</span></h2>
<ul>
<li>Worked with Agent and Semantic technologies</li>
<li>Conducted my master thesis: Design and Implementation of an Agent Architecture Based on Web Hooks</li>
</ul>
<h2>IT Coordinator<span><a href="http://eestec.net">EESTEC International</a> - 2012-2013</span></h2>
<ul>
<li>Coordinated the work of a small international IT Team</li>
<li>In charge of the administration of the IT infrastructure of EESTEC: Plone portal, Mailman mailing lists, etc.</li>
</ul>
<h2>Oversight Committee Member<span><a href="http://eestec.net">EESTEC International</a> - 2012-2013</span></h2>
<ul>
<li>Supervised the work of the International Board</li>
</ul>
<h2>Vice Chairman for External Affairs <span><a href="http://eestec.net">EESTEC International</a> - 2012-2013</span></h2>
<ul>
<li>Established connections with other Student Associations</li>
<li>Helped found new Observers (Local Groups)</li>
<li>Carried out International Board duties</li>
</ul>
</dd>
<dd class="clear"></dd>
<!--<dt>Hobbies</dt>-->
<!--<dd>Music, Sports and Cinema</dd>-->
<!--<dd class="clear"></dd>-->
<dt>References</dt>
<dd>Available on request</dd>
<dd class="clear"></dd>
</dl>
<div class="clear"></div>
</div>

103
develop_server.sh Executable file
View File

@ -0,0 +1,103 @@
#!/usr/bin/env bash
##
# This section should match your Makefile
##
PY=${PY:-python}
PELICAN=${PELICAN:-pelican}
PELICANOPTS=
BASEDIR=$(pwd)
INPUTDIR=$BASEDIR/content
OUTPUTDIR=$BASEDIR/output
CONFFILE=$BASEDIR/pelicanconf.py
###
# Don't change stuff below here unless you are sure
###
SRV_PID=$BASEDIR/srv.pid
PELICAN_PID=$BASEDIR/pelican.pid
function usage(){
echo "usage: $0 (stop) (start) (restart) [port]"
echo "This starts Pelican in debug and reload mode and then launches"
echo "an HTTP server to help site development. It doesn't read"
echo "your Pelican settings, so if you edit any paths in your Makefile"
echo "you will need to edit your settings as well."
exit 3
}
function alive() {
kill -0 $1 >/dev/null 2>&1
}
function shut_down(){
PID=$(cat $SRV_PID)
if [[ $? -eq 0 ]]; then
if alive $PID; then
echo "Stopping HTTP server"
kill $PID
else
echo "Stale PID, deleting"
fi
rm $SRV_PID
else
echo "HTTP server PIDFile not found"
fi
PID=$(cat $PELICAN_PID)
if [[ $? -eq 0 ]]; then
if alive $PID; then
echo "Killing Pelican"
kill $PID
else
echo "Stale PID, deleting"
fi
rm $PELICAN_PID
else
echo "Pelican PIDFile not found"
fi
}
function start_up(){
local port=$1
echo "Starting up Pelican and HTTP server"
shift
$PELICAN --debug --autoreload -r $INPUTDIR -o $OUTPUTDIR -s $CONFFILE $PELICANOPTS &
pelican_pid=$!
echo $pelican_pid > $PELICAN_PID
cd $OUTPUTDIR
$PY -m pelican.server $port &
srv_pid=$!
echo $srv_pid > $SRV_PID
cd $BASEDIR
sleep 1
if ! alive $pelican_pid ; then
echo "Pelican didn't start. Is the Pelican package installed?"
return 1
elif ! alive $srv_pid ; then
echo "The HTTP server didn't start. Is there another service using port" $port "?"
return 1
fi
echo 'Pelican and HTTP server processes now running in background.'
}
###
# MAIN
###
[[ ($# -eq 0) || ($# -gt 2) ]] && usage
port=''
[[ $# -eq 2 ]] && port=$2
if [[ $1 == "stop" ]]; then
shut_down
elif [[ $1 == "restart" ]]; then
shut_down
start_up $port
elif [[ $1 == "start" ]]; then
if ! start_up $port; then
shut_down
fi
else
usage
fi

73
fabfile.py vendored Normal file
View File

@ -0,0 +1,73 @@
from fabric.api import *
import fabric.contrib.project as project
import os
import sys
import SimpleHTTPServer
import SocketServer
# Local path configuration (can be absolute or relative to fabfile)
env.deploy_path = 'output'
DEPLOY_PATH = env.deploy_path
# Remote server configuration
production = 'root@localhost:22'
dest_path = '/var/www'
# Rackspace Cloud Files configuration settings
env.cloudfiles_username = 'my_rackspace_username'
env.cloudfiles_api_key = 'my_rackspace_api_key'
env.cloudfiles_container = 'my_cloudfiles_container'
def clean():
if os.path.isdir(DEPLOY_PATH):
local('rm -rf {deploy_path}'.format(**env))
local('mkdir {deploy_path}'.format(**env))
def build():
local('pelican -s pelicanconf.py')
def rebuild():
clean()
build()
def regenerate():
local('pelican -r -s pelicanconf.py')
def serve():
os.chdir(env.deploy_path)
PORT = 8000
class AddressReuseTCPServer(SocketServer.TCPServer):
allow_reuse_address = True
server = AddressReuseTCPServer(('', PORT), SimpleHTTPServer.SimpleHTTPRequestHandler)
sys.stderr.write('Serving on port {0} ...\n'.format(PORT))
server.serve_forever()
def reserve():
build()
serve()
def preview():
local('pelican -s publishconf.py')
def cf_upload():
rebuild()
local('cd {deploy_path} && '
'swift -v -A https://auth.api.rackspacecloud.com/v1.0 '
'-U {cloudfiles_username} '
'-K {cloudfiles_api_key} '
'upload -c {cloudfiles_container} .'.format(**env))
@hosts(production)
def publish():
local('pelican -s publishconf.py')
project.rsync_project(
remote_dir=dest_path,
exclude=".DS_Store",
local_dir=DEPLOY_PATH.rstrip('/') + '/',
delete=True,
extra_opts='-c',
)

View File

@ -1,52 +0,0 @@
---
layout: default
title: Blog
categories: blog
---
<!-- Pagination links -->
{% if paginator.total_pages > 0 %}
<div class="pagination pag-top">
{% if paginator.previous_page %}
<span class="previouspage"><i class="icon-chevron-sign-left"></i><a href="/{% if paginator.previous_page > 2 %}page{{ paginator.previous_page }}{% endif %}"> Newer Posts</a></span>
{% else %}
<span class="previouspage" style="visibility:hidden;"><i class="icon-chevron-sign-left"></i> Newer Posts</span>
{% endif %}
<span class="page_number ">Page {{ paginator.page }} of {{ paginator.total_pages }}</span>
{% if paginator.next_page %}
<span class="nextpage"><a href="/page{{ paginator.next_page }}"> Older Posts </a> <i class="icon-chevron-sign-right"></i></span>
{% else %}
<span class="nextpage" style="visibility:hidden;">Older Posts <i class="icon-chevron-sign-right"></i></span>
{% endif %}
</div>
{% endif %}
<!-- This loops through the paginated posts -->
{% for post in paginator.posts %}
<div class="posthead">
<h2><a href="{{ post.url }}" class="title">{{ post.title }}</a></h2>
<span class="date">{{ post.date | date_to_string }}</span>
{% for c in post.tags %}
<span class="label label-success">{{ c }}</span>
{% endfor %}
</div>
<div class="excerpt">
{{ post.excerpt }}
</div>
<span><a href="{{ post.url }}"><i class="icon-pl2s"></i> Read more...</a></span>
{% endfor %}
{% if paginator.total_pages > 0 %}
<div class="pagination pag-bottom">
{% if paginator.previous_page %}
<span class="previouspage"><i class="icon-chevron-sign-left"></i><a href="/{% if paginator.previous_page > 2 %}page{{ paginator.previous_page }}{% endif %}"> Newer Posts</a></span>
{% else %}
<span class="previouspage" style="display:none;"><i class="icon-chevron-sign-left"></i> Newer Posts</span>
{% endif %}
<span class="page_number ">Page {{ paginator.page }} of {{ paginator.total_pages }}</span>
{% if paginator.next_page %}
<span class="nextpage"><a href="/page{{ paginator.next_page }}"> Older Posts </a> <i class="icon-chevron-sign-right"></i></span>
{% else %}
<span class="nextpage" style="display:none;">Older Posts <i class="icon-chevron-sign-right"></i></span>
{% endif %}
</div>
{% endif %}

56
pelicanconf.py Normal file
View File

@ -0,0 +1,56 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*- #
from __future__ import unicode_literals
AUTHOR = u'J. Fernando S\xe1nchez'
SITENAME = u'balkian.com'
SITEURL = ''
PATH = 'content'
TIMEZONE = 'Europe/Paris'
DEFAULT_LANG = u'en'
# Feed generation is usually not desired when developing
FEED_ALL_ATOM = None
CATEGORY_FEED_ATOM = None
TRANSLATION_FEED_ATOM = None
AUTHOR_FEED_ATOM = None
AUTHOR_FEED_RSS = None
# Blogroll
LINKS = (('Pelican', 'http://getpelican.com/'),
('Python.org', 'http://python.org/'),
('Jinja2', 'http://jinja.pocoo.org/'),
('You can modify those links in your config file', '#'),)
# Social widget
SOCIAL = (('You can add links in your config file', '#'),
('Another social link', '#'),)
DEFAULT_PAGINATION = 10
# Uncomment following line if you want document-relative URLs when developing
#RELATIVE_URLS = True
THEME = "balkiantheme"
def date_to_string(date):
return date.strftime('%Y-%m-%d')
JINJA_FILTERS = {'date_to_string': date_to_string}
PLUGIN_PATH = ["plugins/",]
PLUGINS = ["neighbors",]
PYGMENTS_RST_OPTIONS = {'linenos': 'table'}
STATIC_PATHS = [
'extra/CNAME',
'extra/favicon',
'fonts'
]
EXTRA_PATH_METADATA = {
'extra/CNAME': {'path': 'CNAME'},
'extra/favicon': {'path': 'favicon'},
}

59
plugins/neighbors.py Normal file
View File

@ -0,0 +1,59 @@
# -*- coding: utf-8 -*-
"""
Neighbor Articles Plugin for Pelican
====================================
This plugin adds ``next_article`` (newer) and ``prev_article`` (older)
variables to the article's context
"""
from pelican import signals
def iter3(seq):
it = iter(seq)
nxt = None
cur = next(it)
for prv in it:
yield nxt, cur, prv
nxt, cur = cur, prv
yield nxt, cur, None
def get_translation(article, prefered_language):
if not article:
return None
for translation in article.translations:
if translation.lang == prefered_language:
return translation
return article
def set_neighbors(articles, next_name, prev_name):
for nxt, cur, prv in iter3(articles):
exec("cur.{} = nxt".format(next_name))
exec("cur.{} = prv".format(prev_name))
for translation in cur.translations:
exec(
"translation.{} = get_translation(nxt, translation.lang)".format(
next_name))
exec(
"translation.{} = get_translation(prv, translation.lang)".format(
prev_name))
def neighbors(generator):
set_neighbors(generator.articles, 'next_article', 'prev_article')
for category, articles in generator.categories:
articles.sort(key=(lambda x: x.date), reverse=(True))
set_neighbors(
articles, 'next_article_in_category', 'prev_article_in_category')
if hasattr(generator, 'subcategories'):
for subcategory, articles in generator.subcategories:
articles.sort(key=(lambda x: x.date), reverse=(True))
index = subcategory.name.count('/')
next_name = 'next_article_in_subcategory{}'.format(index)
prev_name = 'prev_article_in_subcategory{}'.format(index)
set_neighbors(articles, next_name, prev_name)
def register():
signals.article_generator_finalized.connect(neighbors)

24
publishconf.py Normal file
View File

@ -0,0 +1,24 @@
#!/usr/bin/env python
# -*- coding: utf-8 -*- #
from __future__ import unicode_literals
# This file is only used if you use `make publish` or
# explicitly specify it as your config file.
import os
import sys
sys.path.append(os.curdir)
from pelicanconf import *
SITEURL = 'http://balkian.com'
RELATIVE_URLS = False
FEED_ALL_ATOM = 'feeds/all.atom.xml'
CATEGORY_FEED_ATOM = 'feeds/%s.atom.xml'
DELETE_OUTPUT_DIRECTORY = True
# Following items are often useful when publishing
#DISQUS_SITENAME = ""
#GOOGLE_ANALYTICS = ""