PK Ϋ}EF= = bookie-latest/index.html
Bookie is a young open source project to help replace Delicious.
See the features page for full length details.
Github: | http://github.com/bookieio/Bookie |
---|---|
Mailing List: | https://groups.google.com/forum/?hl=en#!forum/bookie_bookmarks |
See it live: | https://bmark.us |
ToDo List: | https://trello.com/board/bookie/4f18c1ac96c79ec27105f228 |
Bookie App: | http://github.com/bookieio/Bookie |
---|---|
Readable App: | https://github.com/bookieio/bookie_parser |
Readable Lib: | https://github.com/bookieio/breadability |
Bookie Cli: | https://github.com/bookieio/bookie_api |
Bookie Android: | https://github.com/bookieio/Bookie-Android |
This assumes you’re on Ubuntu or can figure out the difference between Ubuntu and your distro for the following:
$ git clone git://github.com/bookieio/Bookie.git
$ cd Bookie && make sysdeps && make install
# THIS WILL TAKE A WHILE, GET A COFFEE
$ make run
$ (YOUR BROWSER) http://127.0.0.1:6543/
You should have JavaScipt enabled.
Login as admin
user: admin
pass: admin
You can create new users by writing a script or going through the signup process. You just need to install inbox (bin/pip install inbox) and run make smtp to get a fake email server to catch the activation emails.
Well, you might want to import a backup of your delicious bookmarks. You can do that by visiting the Import link in the footer of your site installation. Make sure you know the API key that you’ve set in your bookie install’s .ini configuration file.
You can view your recent bookmarks at: http://127.0.0.1:6543/recent
You probably also want to install a browser extension to be able to store new bookmarks going forward. Once you install the extension, you’ll need to set the options for it to work. See the browser extension docs for those settings.
You can setup Bookie to run in a variety of ways. Make sure to check out some samples in the hosting docs
There are some required packages that need to be installed so you can build bookie. These are:
Note: right we we support three databases - mysql, postgres, and sqlite - and the database bindings need to be built into the virtualenv. Out of the box, Bookie will setup a Sqlite version for you to get started with.
Bookie uses the Celery library to handle background processing tasks that might be expensive. Currently, it’s setup to use redis as the backend for this. Please check the bookie.ini for the connection string to the redis database. Any time a bookmark is saved it will background fulltext indexing for the bookmark. During import, it will attempt to fetch content for the imported urls as well. Emails and stats generation also go through this system. By default, make run will start up celery in the background. An exmaple manual command to run celery safely with the sqlite default database is:
celery worker --app=bookie.bcelery -B -l info --purge -c 1
Adjust the command to your own needs. You might need to increase or lower the debug level, for instance, to suit your needs.
If you’re using Postgres or MySQL as your database for Bookie you’ll also want to grab the dev package for your db so that the Python drivers for them can compile.
$ sudo apt-get install libmysqlclient-dev
- OR -
$ sudo apt-get install postgresql-server-dev-8.4
You will also need to install python db drivers for MySql.
$ bin/pip install MySQL-python
Then you’ll need to update the database connection string in your bookie.ini file. The database and user account need to exist in order for it to bootstrap the database for you. Once you’re ready run:
$ make db_up
First, follow the steps above to set up an empty MySQL/Postgresql database.
To prepare for the migration, we first need to empty the alembic_version and users tables which are not fully empty. To do this in MySQL:
TRUNCATE `alembic_version`;
DELETE FROM `users` WHERE 1;
Then, install the migration tools:
$ apt-get install ruby-sqlite3 ruby-mysql
- OR -
$ apt-get install ruby-sqlite3 ruby-pg
$ gem install rack -v 1.4.5
$ gem install taps
Next, let’s publish our existing sqlite database:
$ taps server sqlite:///home/user/bookie/bookie/bookie.db tmpuser tmppass &
And finally we pull the data into our new MySQL/Postgresql database:
$ taps pull -s mysql://bookie:***@localhost/bookie http://tmpuser:tmppass@localhost:5000
- OR -
$ taps pull -s postgres://bookie:***@localhost/bookie http://tmpuser:tmppass@localhost:5000
Bookie currently supports the Delicious export, Google Bookmarks export, Google Chrome bookmarks, and Firefox JSON backups.
The importer might take a bit on large sets of bookmarks. Let us know if you run into any issues so we can improve our import process.
One of Bookie’s best features is that it will fetch the content of your bookmarks and attempt to parse/fulltext index it. A bookmark import will cause the system to go in and start fetching content for the new bookmarks. There’s also a background task that will (by default) attempt to find any bookmarks missing content and fetch it on an hourly basis.
# run readable parsing on new bookmarks each morning at 1am
0 1 * * * /path/to/bookie/env/bin/python /path/to/Bookie/scripts/readability/existing.py --ini=myconfig.ini --new
# retry error'd parsing at 1am on the 1st of each month
0 1 1 * * /path/to/bookie/env/bin/python /path/to/Bookie/scripts/readability/existing.py --ini=myconfig.ini --retry-errors
There’s a quick/dirty sample script you can use to backup your bookmarks. It just calls the /export url on your installation and creates a .gz backup file.
This obviously doesn’t store things like the fulltext indexes and such. So if you are using the Readable versions you might want to keep a backup of your database itself instead of just dumping your export file.
A sample of cron’ing this to run at 6am every day would be:
0 6 * * * /usr/bin/python /path/to/Bookie/scripts/misc/backup.py
To use the bookmarklet, log into your account page and drag the link at the bottom to your bookmark bar. In the Android browser, you can long-press on the link and bookmark it.
After that, you can bookmark any page you’re currently viewing by clicking on the bookmark in your browser. It will load the current url and page title into an add form on the website.
Once you stored the bookmark with tags and description, you’ll be redirected back to the page you were originally viewing.
If you do not use Google Chrome, make sure to check out using the Bookie bookmarklet.
Provides Bookie bookmarks into Google Chrome
In order to setup the extension you’ll need to set a couple of options. To pull up the options page right-click on the extension in the tool bar and select the Options menu.
set this to the installed url for your bookie instance. In dev mode it’s http://127.0.0.1:6543/api/v1. If you do not set the api url you should get an error about not being able to find a bookie instance at that url.
By default the extension attempts to hook you up to the bmark.us instance.
The Firefox extension is starting over from scratch. You can track it at:
Right now, we’re in full developer mode so hosting is up to you. We help you get started by running it with the built-in paste webserver locally.
However Bookie is a WSGI application and can be hosted with web servers such as Apache, Nginx, and Cherokee. As Bookie matures we’ll try to get cookbook docs for using each. For now, it’s up to you to figure out, but feel free to drop by the #bookie# irc channel on Freenode for assistance..
To start out, the easiest thing to do is to put a web server in front the paster development server. So you run the web server with the command:
paster serve --daemon bookie.ini
Then to serve it behind Nginx you need to setup a new virtual host config.
server {
listen 80;
server_name bookie;
#set your default location
location / {
proxy_pass http://127.0.0.1:6543/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
}
}
Now you can create an alias in your /etc/hosts
127.0.0.1 bookie
now http://bookie should be served through Nginx to your bookie instance.
uWSGI is a great way to run WSGI apps. Nginx is then setup to be the front end and communicate with the uWSGI processes running.
Warning, this isn’t the easiest way to set things up, but it’s pretty fast and decent to run.
First, you need a wsgi.py file that tells uWSGI where your environemnt and application are to run.
Please this file into your application’s bookie app directory. If you’ve used the normal bootstrap process it should be in:
bookie/bookie/bookie/wsgi.py
bookie/bookie/bookie/combo.py
With the following:
wsgi.py
#!/usr/bin/python env
import os
from os.path import dirname
# Add the virtual Python environment site-packages directory to the path
import site
ve_dir = dirname(dirname(dirname(dirname(__file__))))
install_dir = dirname(dirname(__file__))
site.addsitedir(os.path.join(ve_dir, 'lib/python2.6/site-packages'))
# Avoid ``[Errno 13] Permission denied: '/var/www/.python-eggs'`` messages
os.environ['PYTHON_EGG_CACHE'] = os.path.join(install_dir, 'egg-cache')
# Load the application
from paste.deploy import loadapp
application = loadapp('config:' + os.path.join(install_dir, 'production.ini'))
combo.py
"""WSGI file to serve the combo JS out of convoy"""
from convoy.combo import combo_app
JS_FILES = 'bookie/static/js/build'
application = combo_app(JS_FILES)
Now we need to add the uwsgi daemon settings for this application. We’ll create a file /etc/init/bookie.conf that will give us an upstart enabled service to run the app through.
description "uWSGI Bookie Install"
start on runlevel [2345]
stop on runlevel [!2345]
respawn
exec /usr/bin/uwsgi26 --socket /tmp/bookie.sock \
-H /home/$username/bookie/ \
--chmod-socket --module wsgi \
--pythonpath /home/$username/bookie/bookie/bookie \
-p 4
combo loader
description "uWSGI Convoy"
start on runlevel [2345]
stop on runlevel [!2345]
respawn
exec /usr/bin/uwsgi --socket /tmp/convoy.sock \
-H /home/$username/bookie \
--chmod-socket --module combo \
-p 4 --threads 2
We should not be able to start up the server with uWSGI command there.
sudo /usr/bin/uwsgi26 --socket /tmp/rick.bmark.sock \
-H /home/$username/bookie/ \
--chmod-socket --module wsgi \
--pythonpath /home/$username/bookie/bookie/Bookie/bookie \
-p 4
This will help bring up any potential errors. If all starts up well you can launch the daemon with:
$ sudo service bookie start
$ sudo service combo start
Once that’s started we just need to tell Nginx where to go access the application.
server {
listen 80;
server_name bookie;
charset utf-8;
root /home/$username/bookie/bookie/bookie/static;
index index.html index.htm;
# Remove trailing slash by doing a 301 redirect
rewrite ^/(.*)/$ /$1 permanent;
location ~*/(img|js|iepng|css)/ {
root /home/$username/bookie/bookie/bookie;
expires max;
add_header Cache-Control "public";
break;
}
location /combo {
include uwsgi_params;
uwsgi_pass unix:///tmp/convoy.sock;
uwsgi_param UWSGI_SCHEME $scheme;
break;
}
location / {
include uwsgi_params;
uwsgi_pass unix:///tmp/bookie.sock;
uwsgi_param SCRIPT_NAME /;
uwsgi_param UWSGI_SCHEME $scheme;
}
## Compression
# src: http://www.ruby-forum.com/topic/141251
# src: http://wiki.brightbox.co.uk/docs:nginx
gzip on;
gzip_http_version 1.0;
gzip_comp_level 2;
gzip_proxied any;
gzip_min_length 1100;
gzip_buffers 16 8k;
gzip_types text/plain text/html text/css application/x-javascript application/xml application/xml+rss text/javascript;
# Some version of IE 6 don't handle compression well on some mime-types, so just disable for them
gzip_disable "MSIE [1-6].(?!.*SV1)";
# Set a vary header so downstream proxies don't send cached gzipped content to IE6
gzip_vary on;
## /Compression
}
From there we just need to check Nginx for any issues and reload it.
sudo nginx -t
sudo service nginx reload
Apache and the mod_wsgi Apache module is the tried-and-true standard for WSGI serving. It also happens to be really easy to get your Bookie app working with it.
First you need to install Apache and mod_wsgi:
Then you need to create a pyramid.wsgi file in the root of your Bookie virtualenv. Something like
import os
os.environ['NLTK_DATA'] = '/home/user/bookie/bookie/download-cache/nltk'
from pyramid.paster import get_app
application = get_app('/home/user/bookie/bookie/mybookie.ini', 'bookie')
A couple of things to check:
Next you need to add a virtualhost to your Apache config. You can either put this right in your httpd.conf or create a virtualhost for it.
WSGIApplicationGroup %{GLOBAL}
WSGIPassAuthorization On
WSGIDaemonProcess pyramid user=ben group=ben threads=4 \
python-path=/home/user/bookie/lib/python2.6/site-packages
WSGIScriptAlias / /home/user/bookie/pyramid.wsgi
<Directory /home/user/bookie>
WSGIProcessGroup pyramid
Order allow,deny
Allow from all
</Directory>
A couple of things you need to check:
Finally, all you have to do is restart Apache and off you go!
For more help running Bookie under mod_wsgi on Apache, check out the modwsgi Pyramid Docs.
Bookie is a Python web application written using the Pyramid web framework.
Bookie’s git repository is managed using a tool/process called git flow. It basically sets standards for how the git repository is set up. You’ll find the most up to date working code in the develop branch. Individual features that are being worked on are in branches prefixed by feature/. As these features get to a workable state they might get merged into the develop branch.
The master branch is only for releases and we’re a long away from that. So when you check out Bookie, make sure to start out using the develop branch.
If you want to help out with the hacking of Bookie, here’s some info you might want to check out:
Ok, so I’m going to try to summarize changes here, but for full changes see the commit log. Commit often and all that.
0.4 was all about the port from jQuery to YUI for the Javascript side of things. This meant that the UI got a big facelift, we’ve moved to the YUI MVC framework, and the extensions needed to be updated to use the new library of code.
We also spent time getting most of the fabric tasks moved over to the Makefile to help aid in running and managing the installation.
Celery has been introduced as a background task processor and we track stats for the system in there. Long running tasks, such as imports, and handled through the Celery background system.
The database migrations have been collapsed and ported over to run under alembic vs sqlalchemy-migrate. This really cleans up a lot of the old migrations where we used per-database fulltext support instead of Whoosh.
The main goals of 0.3 were to add a full JSON API and to add authentication so that a single Bookie install could support multiple users. This involved a ton of changed code and so a lot more has changed in the process.
Nearly everything about Bookie is managed via the Makefile. If you’re not familiar with Makefiles, it’s worth a little time to get your head around.
This command will start up the Bookie application along with the combo loader needed to serve the Javascript for Bookie.
This will kill the running servers started up from the make run.
This command will check for updated Javascript library files and, if required, copy changed files to the build directory and minimize them.
When doing development you might want some help keeping things “built” while you work. This command will also start up the sass watch process and a python script that will auto build changed Javascript files for you. This is how I tend to work and debug. For production purposes though, make run does everything you need.
This will kill things started via make run_dev.
Run the Python tests.
Open up all of the Javascript tests in the browser, one per tab.
Run any database migrations.
Start out a new migration file. Make sure to pass desc=”What is this migration”.
This will wipe the majority of the built files and resources. Think of it as a little bit of a hard reset.
Should recover froma a make clean and perform steps just as checking all deps are installed, the database is up to date, and the Javascript and CSS are up to date.
To work with code in the signup process it’s often best to setup email communication for the site. It does not do this automatically and you need an smtp server for it to send email out to test accounts.
One way to setup an smtp server is to use a small tool, msmtp.
This assumes that you’re on Ubuntu. You’ll need to adjust these instructions for your own platform. If you get it working please feel free to submit a pull request with your platform as we’ll happily add it to the docs.
$ sudo apt-get install msmtp
$ touch ~/.msmtprc
$ chmod 0600 ~/.msmtprc
Next, edit the ~/.msmtprc file with your favourite text editor. The configuration file should contain the following lines.
defaults
account examplemail
host smtp.examplemail.com
tls on
tls_certcheck off
port 587
auth login
from somebody@example.com
user somebody@example.com
password somesecret
account default: examplemail
In the above example, the host has to be replaced with your email service smtp host. The from, user, and password, needs to be valid for your own email account.
Once that’s complete, you can perform simple test to ensure your configuration is correct. Copy and paste these lines to your command prompt modifying the email address to your own address:
cat <<EOF | msmtp someone@example.com
Subject: test
This is a test!
EOF
If all the instructions are followed correctly, you can now receive the activation mail to the specified email address.
Running the test suite for Bookie is very simple:
# Init the db first to prepare for running tests
$ INI=test.ini make test_bookie.db
$ make test
$ make jstestserver
# open a new tab
$ make jstest
Unit tests are small tests that should test small bits of code. These should be setup in the same directory that the file you’re testing is setup. So if you’re working on a file in lib/feature.py you’d have a matching file test_feature.py. This file should be runnable via the test runner by itself.
Functional tests are larger scope tests that make sure the application is responding correctly as a whole. These are run through the fabric command fab test. It will run all tests defined in the tests directory.
Note: All unit tests should be added to the tests/__init__.py so that they get run during the large test run. This way the ci server will just need to run the one test pass and all tests will run during each build.
A bit confusing. There’s lots of docs, but none of them seem to agree on how to bootstrap the environment properly.
For best performance, and so that we can implement an api that meets the features we hold important we have our own api you can implement. It’s JSON based and will return a standard JSON response for any call
All api calls should be against https://$yoursite.com/api/v1.
Remember, the only authentication method is the api key. If your site is not hosted behind secure http server then it’s likely to get stolen. Please think about this before setting up a server exposed to the internet.
POST /api/v1/admin/bmark
Submit a new bookmark for storing
query param: | api_key required - the api key for your account to make the call with |
---|---|
query param: | callback - wrap JSON response in an optional callback |
post param: | url required |
post param: | description |
post param: | extended |
post param: | tags - space separated tag string |
post param: | content - html content of the page to be parsed as the readable version. if not provided, will be rendered by the celery job at some point in the future (or never if celery is not running). |
post param: | is_private - specifies whether the bookmark is private or not. By default the bookmarks are stored as private |
success 200: | If successful a “200 OK” will be returned |
---|---|
error 403: | if the api key is not valid or missing then this is an unauthorized request |
All error responses will have a json body with an error message string and possibly other helpful information.
requests.post('http://127.0.0.1:6543/api/v1/admin/bmark?api_key=12345...')
>>> {
"bmark": {
"username": "admin",
"updated": "",
"extended": "Extended notes",
"description": "Bookie",
"tags": [
{
"tid": 2,
"name": "bookmarks"
}
],
"bid": 1,
"stored": "2011-08-06 20:35:54",
"inserted_by": "unknown_api",
"is_private": true,
"tag_str": "bookmarks",
"clicks": 0,
"hash_id": "c5c21717c99797"
},
"location": "http://localhost/bmark/readable/c5c21717c99797"
}
GET /api/v1/admin/bmark/c605a21cf19560
Get the information about this bookmark.
query param: | api_key optional - the api key for your account to make the call with |
---|---|
query param: | with_content - do you wish the readable content of the urls if available |
query param: | url - This is the url of the page that you are trying to bookmark.This is used to supply tags in the Chrome extension. |
query param: | description - This is the title of the page.This is used to supply tags in the Chrome extension. |
query param: | callback - wrap JSON response in an optional callback |
success 200: | If successful a “200 OK” will be returned |
---|---|
error 404: | if the hash id can not be found you’ll get a 404 |
error 403: | if the api key is not valid or missing then this is an unauthorized request |
All error responses will have a json body with an error message string and possibly other helpful information.
requests.get('http://127.0.0.1:6543/api/v1/admin/bmark/c605a21cf19560?api_key=12345...')
>>> {
"bmark": {
"bid": 2,
"clicks": 1,
"description": "Bookie: Recent Bookmarks",
"extended": "",
"hash_id": "c605a21cf19560",
"inserted_by": null,
"is_private": true,
"stored": "2011-06-21 13:20:26",
"tag_str": "test bookmarks",
"tags": [
{
"name": "test",
"tid": 3
},
{
"name": "bookmarks",
"tid": 2
}
],
"updated": "2011-07-29 22:23:42",
"username": "admin"
}
}
requests.get('http://127.0.0.1:6543/api/v1/admin/bmark/c605a21cf19560?api_key=000')
>>> {"error": "Not authorized for request."}
POST /api/v1/bmark/admin/c605a21cf19560
Update the stored bookmark with new information.
query param: | api_key required - the api key for your account to make the call with |
---|---|
query param: | callback - wrap JSON response in an optional callback |
post param: | description |
post param: | extended |
post param: | tags - space separated tag string |
post param: | content - html content of the page to readable parse |
success 200: | If successful a “200 OK” will be returned |
---|---|
error 404: | if the hash id can not be found you’ll get a 404 |
error 403: | if the api key is not valid or missing then this is an unauthorized request |
All error responses will have a json body with an error message string and possibly other helpful information.
requests.post('http://127.0.0.1:6543/api/v1/bmark/admin/c605a21cf19560?api_key=12345...')
>>> {
"bmark": {
"username": "admin",
"updated": "",
"extended": "Extended notes",
"description": "Bookie",
"tags": [
{
"tid": 2,
"name": "bookmarks"
}
],
"bid": 1,
"stored": "2011-08-06 20:35:54",
"inserted_by": "unknown_api",
"is_private": true,
"tag_str": "bookmarks",
"clicks": 0,
"hash_id": "c5c21717c99797"
},
"location": "http://localhost/bmark/readable/c5c21717c99797"
}
DELETE /api/v1/bmark/admin/c605a21cf19560
Remove the bookmark from the user’s list
query param: | api_key required - the api key for your account to make the call with |
---|---|
query param: | callback - wrap JSON response in an optional callback |
success 200: | If successful a “200 OK” will be returned, with json body of message: done |
---|---|
error 404: | if the hash id can not be found you’ll get a 404 |
error 403: | if the api key is not valid or missing then this is an unauthorized request |
All error responses will have a json body with an error message string and possibly other helpful information.
requests.delete('http://127.0.0.1:6543/api/v1/bmark/admin/c605a21cf19560?api_key=12345...')
>>> {
"message": "done",
}
GET /api/v1/admin/bmarks
Return a list of the most recent bookmarks
query param: | api_key optional - the api key for your account to make the call with |
---|---|
query param: | count - the number in the result you wish to return |
query param: | page - the page number to get results for based off of the count specified |
query param: | with_content - do you wish the readable content of the urls if available |
query param: | callback - wrap JSON response in an optional callback |
success 200: | If successful a “200 OK” will be returned, with json body of message: done |
---|---|
error 403: | if the api key is not valid or missing then this is an unauthorized request |
requests.get('http://127.0.0.1:6543/api/v1/admin/bmarks?count=2&api_key=12345...')
>>>{
"count": 2,
"bmarks": [
{
"username": "admin",
"updated": "2011-07-29 22:23:42",
"extended": "",
"description": "Bookie: Recent Bookmarks",
"tags": [
{
"tid": 3,
"name": "test"
},
{
"tid": 2,
"name": "bookmarks"
}
],
"bid": 2,
"stored": "2011-06-21 13:20:26",
"inserted_by": null,
"is_private": true,
"tag_str": "test bookmarks",
"clicks": 1,
"hash_id": "c605a21cf19560",
"url": "https://bmark.us/recent",
"total_clicks": 5
},
{
"username": "admin",
"updated": "2011-07-15 14:25:16",
"extended": "Bookie Documentation Home",
"description": "Bookie Website",
"tags": [
{
"tid": 2,
"name": "bookmarks"
}
],
"bid": 1,
"stored": "2011-06-20 11:42:47",
"inserted_by": null,
"is_private": true,
"tag_str": "bookmarks",
"clicks": 1,
"hash_id": "c5c21717c99797",
"http://docs.bmark.us",
"total_clicks": 4
}
],
"tag_filter": null,
"page": 0,
"max_count": 10
}
GET /api/v1/admin/bmarks/export
Get a json dump of all of the bookmarks for a user’s account. This will include all content that we have available. It will take a while to build and we will be limited this call to only a few times a day at some point.
query param: | api_key required - the api key for your account to make the call with |
---|---|
query param: | callback - wrap JSON response in an optional callback |
success 200: | If successful a “200 OK” will be returned, with json body of message: done |
---|---|
error 403: | if the api key is not valid or missing then this is an unauthorized request |
requests.get('http://127.0.0.1:6543/api/v1/admin/bmarks/export?api_key=12345...')
>>> {
"bmarks": [
{
"bid": 1,
"clicks": 1,
"description": "Bookie Website",
"extended": "Bookie Documentation Home",
"hash_id": "c5c21717c99797",
"hashed": {
"clicks": 4,
"hash_id": "c5c21717c99797",
"url": "http://bmark.us"
},
"inserted_by": null,
"is_private": true,
"stored": "2011-06-20 11:42:47",
"tag_str": "bookmarks",
"updated": "2011-07-15 14:25:16",
"username": "admin"
},
{
"bid": 2,
"clicks": 1,
"description": "Bookie: Recent Bookmarks",
"extended": "",
"hash_id": "c605a21cf19560",
"hashed": {
"clicks": 1,
"hash_id": "c605a21cf19560",
"url": "https://bmark.us/recent"
},
"inserted_by": null,
"is_private": true,
"stored": "2011-06-21 13:20:26",
"tag_str": "test bookmarks",
"updated": "2011-07-29 22:23:42",
"username": "admin"
},
...
],
"count": 137,
"date": "2011-08-08 20:11:43.648699"
}
GET /api/v1/admin/bmarks/popular
Return a list of the most clicked on bookmarks for the user.
query param: | api_key optional - the api key for your account to make the call with |
---|---|
query param: | count - the number in the result you wish to return |
query param: | page - the page number to get results for based off of the count specified |
query param: | with_content - do you wish the readable content of the urls if available |
query param: | callback - wrap JSON response in an optional callback |
success 200: | If successful a “200 OK” will be returned, with json body of message: done |
---|---|
error 403: | if the api key is not valid or missing then this is an unauthorized request |
requests.get('http://127.0.0.1:6543/api/v1/admin/bmarks/popular?count=2&api_key=12345...')
>>>{
"count": 2,
"bmarks": [
{
"username": "admin",
"updated": "2011-07-29 22:23:42",
"extended": "",
"description": "Bookie: Recent Bookmarks",
"tags": [
{
"tid": 3,
"name": "test"
},
{
"tid": 2,
"name": "bookmarks"
}
],
"bid": 2,
"stored": "2011-06-21 13:20:26",
"inserted_by": null,
"is_private": true,
"tag_str": "test bookmarks",
"clicks": 3,
"hash_id": "c605a21cf19560",
"url": "https://bmark.us/recent",
"total_clicks": 5
},
{
"username": "admin",
"updated": "2011-07-15 14:25:16",
"extended": "Bookie Documentation Home",
"description": "Bookie Website",
"tags": [
{
"tid": 2,
"name": "bookmarks"
}
],
"bid": 1,
"stored": "2011-06-20 11:42:47",
"inserted_by": null,
"is_private": true,
"tag_str": "bookmarks",
"clicks": 1,
"hash_id": "c5c21717c99797",
"http://docs.bmark.us",
"total_clicks": 4
}
],
"tag_filter": null,
"page": 0,
"max_count": 10
}
GET /api/v1/admin/extension/sync
This is experimental and very likely to change, so use at your own risk. We’re investigating syncing bookmarks with browsers via their extensions. This api call will be the trigger point to allow a browser to request all of the data it needs for loading knowledge of existing bookmarks into a new browser installation.
query param: | api_key required - the api key for your account to make the call with |
---|---|
query param: | callback - wrap JSON response in an optional callback |
success 200: | If successful a “200 OK” will be returned, with json body of message: done |
---|---|
error 403: | if the api key is not valid or missing then this is an unauthorized request |
requests.get('http://127.0.0.1:6543/api/v1/admin/extension/sync?api_key=12345...')
>>> {
"94a2b635d965bc",
"cf01b934863be8",
...
}
GET /api/v1/admin/bmarks/search/:terms
Return a list of the user’s bookmarks based on the fulltext search of the given terms. There can be one or more search terms. All search terms are OR‘d together. Fulltext search will find matches in the description, extended, and tag_string fields of a bookmark. You can also perform fulltext search against the readable content of pages with the correct query parameter from below.
query param: | api_key optional - the api key for your account to make the call with |
---|---|
query param: | count - the number in the result you wish to return |
query param: | page - the page number to get results for based off of the count specified |
query param: | with_content - include the readable text in the fulltext search. This can slow down the response. |
query param: | callback - wrap JSON response in an optional callback |
success 200: | If successful a “200 OK” will be returned, with json body of message: done |
---|---|
error 403: | if the api key is not valid or missing then this is an unauthorized request |
requests.get('http://127.0.0.1:6543/api/v1/admin/bmarks/search/ubuntu/linux?api_key=12345...')
>>>> {
"page": null,
"phrase": "ubuntu",
"result_count": 2,
"search_results": [
{
"bid": 3,
"clicks": 0,
"description": "nickelanddime.png (PNG Image, 1200x1400 pixels) - Scaled (64%)",
"extended": "This is the extended description",
"hash_id": "adb017923e1f56",
"inserted_by": "importer",
"is_private": true,
"stored": "2011-02-25 15:13:00",
"tag_str": "nickelanddime kerfuffle banshee amazon ubuntu ubuntu-one canonical",
"tags": [
{
"name": "nickelanddime",
"tid": 4
},
{
"name": "canonical",
"tid": 10
}
],
"total_clicks": 0,
"updated": "",
"url": "http://www.ndftz.com/nickelanddime.png",
"username": "admin"
},
{
"bid": 77,
"clicks": 0,
"description": "My title: ubuntu forum archive about echolinux",
"extended": "",
"hash_id": "3e9a37d4f7cd74",
"inserted_by": "importer",
"is_private": true,
"stored": "2010-07-08 19:30:18",
"tag_str": "ham linux",
"tags": [
{
"name": "ham",
"tid": 89
},
{
"name": "linux",
"tid": 103
}
],
"total_clicks": 0,
"updated": "",
"url": "http://ubuntuforums.org/archive/index.php/t-973929.html",
"username": "admin"
}
],
"username": "admin",
"with_content": false
}
GET /api/v1/admin/social_connections/
Get a json dump of the social connections count for a user’s account, usernames used in the social connections and refresh date i.e last time respective bot parsed the data from the social connection.
query param: | api_key required - the api key for your account to make the call with |
---|
success 200: | If successful a “200 OK” will be returned |
---|---|
error 403: | if the api key is not valid or missing then this is an unauthorized request |
requests.get('http://127.0.0.1:6543/api/v1/admin/social_connections/api_key=12345..')
>>> {
"count": 2
"social_connections": [{
"username": "admin",
"last_connection": "2014-06-12 17:39:41.855184",
"uid": "1234",
"type": "TwitterConnection"
"twitterConnection": {
"twitter_username": "bookie",
"refresh_date": "2014-06-12 17:39:41.855202"
}
},{
"username": "admin",
"last_connection": "2014-06-12 17:41:09.720954",
"uid": "1234",
"type": "TwitterConnection"
"twitterConnection": {
"twitter_username": "bookie",
"refresh_date": "2014-06-12 17:41:09.720954"
}
}]
}
GET /api/v1/admin/stats/bmarkcount
Get a json dump of the bookmark count for a user’s account for a time period. The time period can be specified or else a json dump of the bookmark count of the past 30 days will be returned. If the start_date is specified to be the first day of the month and the end_date is not supplied, a json response of the bookmark count of the whole month will be returned.
query param: | api_key required - the api key for your account to make the call with |
---|---|
query param: | start_date optional - Find the bookmark count in the specified time window, beginning with start_date. |
query param: | end_date optional - Find the bookmark count in the specified time window, ending with end_date. |
success 200: | If successful a “200 OK” will be returned |
---|---|
error 403: | if the api key is not valid or missing then this is an unauthorized request |
requests.get('http://127.0.0.1:6543/api/v1/admin/stats/bmarkcount?start_date=2014-03-01&end_date=2014-03-05&api_key=12345..')
>>> {
"count": [
{
"attrib": "user_bookmarks_admin",
"data": 0,
"id": 1,
"tstamp": "2014-03-02 20:50:52"
},
{
"attrib": "user_bookmarks_admin",
"data": 3,
"id": 10,
"tstamp": "2014-03-03 20:50:52"
},
{
"attrib": "user_bookmarks_admin",
"data": 5,
"id": 21,
"tstamp": "2014-03-04 20:50:52"
}
]
}
GET /api/v1/admin/tags/complete
Return a list of potential tags to use for the given tag.
query param: | api_key optional - the api key for your account to make the call with |
---|---|
query param: | tag required - the part of the word we want completions for |
query param: | current - a space separated list of the current tags selected that we should take into account when selecting a potential completion option. |
query param: | callback - wrap JSON response in an optional callback |
success 200: | If successful a “200 OK” will be returned, with json body of message: done |
---|---|
error 403: | if the api key is not valid or missing then this is an unauthorized request |
requests.get('http://127.0.0.1:6543/api/v1/admin/tags/complete?api_key=12345...&tag=ubu')
>>> {
current: "",
tags: [
"ubuntu",
"ubuntuone"
]
},
GET /api/v1/admin/account
Return the name and email for the given user account.
query param: | api_key required - the api key for your account to make the call with |
---|---|
query param: | callback - wrap JSON response in an optional callback |
success 200: | If successful a “200 OK” will be returned, with json body of message: done |
---|---|
error 403: | if the api key is not valid or missing then this is an unauthorized request |
requests.get('http://127.0.0.1:6543/api/v1/admin/account?api_key=12345...')
>>> {
"username": "admin",
"name": null,
"signup": null,
"activated": true,
"last_login": null,
"email": "testing@dummy.com"
}
POST /api/v1/admin/account
Update the user’s name or email address
query param: | api_key required - the api key for your account to make the call with |
---|---|
query param: | callback - wrap JSON response in an optional callback |
post param: | name - a new name for the user account |
post param: | email - a new email for the user account |
success 200: | If successful a “200 OK” will be returned, with json body of message: done |
---|---|
error 403: | if the api key is not valid or missing then this is an unauthorized request |
requests.post('http://127.0.0.1:6543/api/v1/admin/account?api_key=12345...')
>>> {
"username": "admin",
"name": null,
"signup": null,
"activated": true,
"last_login": null,
"email": "testing@dummy.com"
}
GET /api/v1/admin/api_key
Fetch the api key for the user from the system. We don’t go waving the api key around so we have to ask for it on its own. Keep this safe. If it’s exposed someone can get at about anything in the system for that user.
I know it’s strange to require the api key to get the api key, but hey, you tell me how to fix it.
query param: | api_key required - the api key for your account to make the call with |
---|---|
query param: | callback - wrap JSON response in an optional callback |
success 200: | If successful a “200 OK” will be returned, with json body of message: done |
---|---|
error 403: | if the api key is not valid or missing then this is an unauthorized request |
requests.post('http://127.0.0.1:6543/api/v1/admin/api_key?api_key=12345...')
>>> {
"username": "someuser",
"api_key": "12345..."
}
POST /api/v1/admin/reset_api_key
Request a brand new API key. The old API key will be invalidated. A new key will be generated and tied to your account. Please do not forget to update the API key in the browser extensions and other places where the API is used.
post param: | api_key required - the api key for your account to make the call with |
---|---|
post param: | username required - the username whose api key has to be reset |
success 200: | If successful a “200 OK” will be returned, with json body of message: done |
---|---|
error 403: | If the api key is not valid or missing then this is an unauthorized request |
requests.post('http://127.0.0.1:6543/api/v1/admin/api_key?api_key=12345...')
>>> {
"api_key": "98765...",
"message": "API key was..."
}
POST /api/v1/admin/account/password
Change the user’s password to the new value provided. Note that the current password is required to perform the step.
query param: | api_key required - the api key for your account to make the call with |
---|---|
query param: | callback - wrap JSON response in an optional callback |
post param: | current_password required - the current password string from the user |
post param: | new_password required - the string to change the password to |
success 200: | If successful a “200 OK” will be returned, with json body of message: done |
---|---|
error 403: | if the api key is not valid or missing then this is an unauthorized request |
error 406: | if the new password is not of acceptable strength. We’re not letting 2 char passwords to be set, sorry. |
requests.post('http://127.0.0.1:6543/api/v1/admin/password?api_key=12345...')
>>> {
"username": "someuser",
"api_key": "12345..."
}
GET /api/v1/bmarks
Return a list of the most recent bookmarks
query param: | api_key optional - the api key for your account to make the call with |
---|---|
query param: | count - the number in the result you wish to return |
query param: | page - the page number to get results for based off of the count specified |
query param: | with_content - do you wish the readable content of the urls if available |
query param: | callback - wrap JSON response in an optional callback |
success 200: | If successful a “200 OK” will be returned, with json body of message: done |
---|---|
error 403: | if the api key is not valid or missing then this is an unauthorized request |
requests.get('http://127.0.0.1:6543/api/v1/bmarks?count=2&api_key=12345...')
>>>{
"count": 2,
"bmarks": [
{
"username": "admin",
"updated": "2011-07-29 22:23:42",
"extended": "",
"description": "Bookie: Recent Bookmarks",
"tags": [
{
"tid": 3,
"name": "test"
},
{
"tid": 2,
"name": "bookmarks"
}
],
"bid": 2,
"stored": "2011-06-21 13:20:26",
"inserted_by": null,
"tag_str": "test bookmarks",
"clicks": 1,
"hash_id": "c605a21cf19560"
},
{
"username": "admin",
"updated": "2011-07-15 14:25:16",
"extended": "Bookie Documentation Home",
"description": "Bookie Website",
"tags": [
{
"tid": 2,
"name": "bookmarks"
}
],
"bid": 1,
"stored": "2011-06-20 11:42:47",
"inserted_by": null,
"tag_str": "bookmarks",
"clicks": 1,
"hash_id": "c5c21717c99797"
}
],
"tag_filter": null,
"page": 0,
"max_count": 10
}
GET /api/v1/bmarks/popular
Return a list of the most clicked on bookmarks.
query param: | api_key optional - the api key for your account to make the call with |
---|---|
query param: | count - the number in the result you wish to return |
query param: | page - the page number to get results for based off of the count specified |
query param: | with_content - do you wish the readable content of the urls if available |
query param: | callback - wrap JSON response in an optional callback |
success 200: | If successful a “200 OK” will be returned, with json body of message: done |
---|---|
error 403: | if the api key is not valid or missing then this is an unauthorized request |
requests.get('http://127.0.0.1:6543/api/v1/bmarks/popular?count=2&api_key=12345...')
>>>{
"count": 2,
"bmarks": [
{
"username": "admin",
"updated": "2011-07-29 22:23:42",
"extended": "",
"description": "Bookie: Recent Bookmarks",
"tags": [
{
"tid": 3,
"name": "test"
},
{
"tid": 2,
"name": "bookmarks"
}
],
"bid": 2,
"stored": "2011-06-21 13:20:26",
"inserted_by": null,
"tag_str": "test bookmarks",
"clicks": 3,
"hash_id": "c605a21cf19560",
"url": "https://bmark.us/recent",
"total_clicks": 5
},
{
"username": "admin",
"updated": "2011-07-15 14:25:16",
"extended": "Bookie Documentation Home",
"description": "Bookie Website",
"tags": [
{
"tid": 2,
"name": "bookmarks"
}
],
"bid": 1,
"stored": "2011-06-20 11:42:47",
"inserted_by": null,
"tag_str": "bookmarks",
"clicks": 1,
"hash_id": "c5c21717c99797",
"http://docs.bmark.us",
"total_clicks": 4
}
],
"tag_filter": null,
"page": 0,
"max_count": 10
}
GET /api/v1/bmarks/search/:terms
Return a list of the user’s bookmarks based on the fulltext search of the given terms. There can be one or more search terms. All search terms are OR‘d together. Fulltext search will find matches in the description, extended, and tag_string fields of a bookmark. You can also perform fulltext search against the readable content of pages with the correct query parameter from below.
query param: | api_key optional - the api key for your account to make the call with |
---|---|
query param: | count - the number in the result you wish to return |
query param: | page - the page number to get results for based off of the count specified |
query param: | search_content - include the readable text in the fulltext search. This can slow down the response. |
query param: | with_content - do you wish the readable content of the urls if available |
query param: | callback - wrap JSON response in an optional callback |
success 200: | If successful a “200 OK” will be returned, with json body of message: done |
---|---|
error 403: | if the api key is not valid or missing then this is an unauthorized request |
requests.get('http://127.0.0.1:6543/api/v1/bmarks/search/ubuntu?api_key=12345...')
>>>> {
"page": null,
"phrase": "ubuntu",
"result_count": 2,
"search_results": [
{
"bid": 3,
"clicks": 0,
"description": "nickelanddime.png (PNG Image, 1200x1400 pixels) - Scaled (64%)",
"extended": "This is the extended description",
"hash_id": "adb017923e1f56",
"inserted_by": "importer",
"stored": "2011-02-25 15:13:00",
"tag_str": "nickelanddime kerfuffle banshee amazon ubuntu ubuntu-one canonical",
"tags": [
{
"name": "ubuntu",
"tid": 4
},
{
"name": "canonical",
"tid": 10
}
],
"total_clicks": 0,
"updated": "",
"url": "http://www.ndftz.com/nickelanddime.png",
"username": "admin"
},
{
"bid": 77,
"clicks": 0,
"description": "My title: ubuntu forum archive about echolinux",
"extended": "",
"hash_id": "3e9a37d4f7cd74",
"inserted_by": "importer",
"stored": "2010-07-08 19:30:18",
"tag_str": "ham linux",
"tags": [
{
"name": "ham",
"tid": 89
},
{
"name": "linux",
"tid": 103
}
],
"total_clicks": 0,
"updated": "",
"url": "http://ubuntuforums.org/archive/index.php/t-973929.html",
"username": "admin"
}
],
"username": "admin",
"with_content": false
}
POST /api/v1/suspend
Creates a reset of the account. The user account is locked, an email is fired to the user’s email address on file, and an activation code is contained within that is required to unlock the account.
query param: | api_key required - the api key for your account to make the call with |
---|---|
query param: | email required - the email address of the user we’re wanting to reset |
query param: | callback - wrap JSON response in an optional callback |
success 200: | If successful a “200 OK” will be returned, with json body of message: done |
---|---|
error 404: | Could not find a user for this email address to suspend the account |
error 406: | No email address submitted in the request so we can’t suspend anyone |
requests.post('http://127.0.0.1:6543/api/v1/suspend?api_key=12345...&email=testing@dummy.com')
>>> {
"message": """Your account has been marked for reactivation. Please check your email for instructions to reset your password""",
}
requests.post('http://127.0.0.1:6543/api/v1/suspend?api_key=12345...')
>>> {
"error": "Please submit an email address",
}
requests.post('http://127.0.0.1:6543/api/v1/suspend?api_key=12345...&email=testing@dummy.com')
>>> {
"error": "You've already marked your account for reactivation. Please check your email for the reactivation link. Make sure to check your spam folder.",
"username": admin
}
DELETE /api/v1/suspend
Reactive the account. Basically we’re “deleting the suspend” on the account. This requires the reactivation key that was sent to the user in the activation email.
query param: | username - string username of the user we’re activating |
---|---|
query param: | activation - string activation code returned emailed from the POST call |
query param: | password - a new password to reactivate this account to |
query param: | callback - wrap JSON response in an optional callback |
success 200: | If successful a “200 OK” will be returned, with json body of message: done |
---|---|
error 406: | The password supplied doesn’t satisfy complexity requirements. |
error 500: | There was some issue restoring the account. Send for help |
requests.delete('http://127.0.0.1:6543/api/v1/suspend?api_key=12345&activation=behehe&password=admin')
>>> {
"message": "Account activated, please log in",
"username": "admin"
}
requests.delete('http://127.0.0.1:6543/api/v1/suspend?api_key=12345&activation=behehe&password=12')
>>> {
"error": "Come on, pick a real password please"
}
POST /api/v1/admin/invite
Allows a user to create an invitation to another user in the system.
query param: | api_key required - the api key for your account to make the call with |
---|---|
query param: | email required - the email address of the new user to invite |
query param: | callback - wrap JSON response in an optional callback |
success 200: | If successful a “200 OK” will be returned, with json body of message: done |
---|---|
error 406: | No email address submitted in the request so we can’t invite anyone |
requests.post('http://127.0.0.1:6543/api/v1/admin/invite?api_key=12345...&email=testing@dummy.com')
>>> {
"message": """done""",
}
requests.post('http://127.0.0.1:6543/api/v1/admin/invite?api_key=12345...')
>>> {
"error": "Please submit an email address",
}
requests.post('http://127.0.0.1:6543/api/v1/admin/invite?api_key=12345...&email=testing@dummy.com')
>>> {
"error": "This user has already been invited to the system.",
"email": "testing@dummy.com"
}
GET /api/v1/stats/bookmarks
Returns basic statistics about number of bookmarks in the database
success 200: | If successful a “200 OK” will be returned, with json body of count, and unique_count |
---|
requests.get('http://127.0.0.1:6543/api/v1/stats/bookmarks')
>>> {
"count": 115047,
"unique_count": 108060
}
These are calls meant to help the admin with the system. Their documented for the project’s need.
GET /api/v1/a/accounts/invites
Return a list of the users and the number of invites they have.
query param: | api_key required - the api key for your account to make the call with |
---|---|
query param: | callback - wrap JSON response in an optional callback |
success 200: | If successful a “200 OK” will be returned |
---|
requests.get('http://127.0.0.1:6543/api/v1/a/accounts/invites?api_key=12345...')
>>>{
"users": [
[
"admin",
11
],
[
"user2",
0
]
]
}
POST /api/v1/a/accounts/invites/:username/:count
Set the invite_ct for the specified user to the specified count
query param: | api_key required - the api key for your account to make the call with |
---|---|
query param: | callback - wrap JSON response in an optional callback |
success 200: | If successful a “200 OK” will be returned. |
---|
requests.get('http://127.0.0.1:6543/api/v1/a/accounts/invites/admin/10?api_key=12345...')
>>>{
"count": 1,
"users": [
{
"activated": false,
"api_key": "12345",
"email": "testing@someting.com",
"id": 2,
"invite_ct": 0,
"invited_by": "admin",
"is_admin": false,
"last_login": "",
"name": null,
"password": null,
"signup": "2010-04-07 17:50:18",
"username": "admin"
}
]
}
GET /api/v1/a/accounts/inactive
Return the account info for users that are not set to active. Useful to see new signups that haven’t activated or users with password/reset issues. New users will have their email address as their username since they’ve not set one yet.
query param: | api_key required - the api key for your account to make the call with |
---|---|
query param: | callback - wrap JSON response in an optional callback |
success 200: | If successful a “200 OK” will be returned. |
---|
requests.get('http://127.0.0.1:6543/api/v1/a/accounts/invites?api_key=12345...')
>>>{
"count": 1,
"users": [
{
"activated": false,
"api_key": "12345",
"email": "newuser@something.com",
"id": 2,
"invite_ct": 0,
"invited_by": "admin",
"is_admin": false,
"last_login": "",
"name": null,
"password": null,
"signup": "2011-04-07 17:50:18",
"username": "newuser@something.com"
}
]
}
GET /api/v1/admin/readable/todo
Return a list of urls that need to have content fetched for their readable views. This is used from external tools that will fetch the content and feed back into the api for readable parsing.
query param: api_key required - the api key for your account to make the call with query param: callback - wrap JSON response in an optional callback
requests.get('http://127.0.0.1:6543/api/v1/a/readable/todo?api_key=12345...')
>>> {
message: ""
payload: {
urls: [
...
]
}
success: true
}
@todo Provide statics of the status code of readable attempts
@todo Provide some readable details, number of outstanding bookmarks to read, number with readable content, etc.
@todo Mark a user as disabled. Will not allow them to login, save bookmarks, use the api
GET /api/v1/a/users/list
Return a list of the users in the system.
query param: | api_key required - the api key for your account to make the call with |
---|---|
query param: | callback - wrap JSON response in an optional callback |
success 200: | If successful a “200 OK” will be returned |
---|
requests.get('http://127.0.0.1:6543/api/v1/a/users/list?api_key=12345...')
>>>{
"count": 10,
"users": [
[
"admin",
...
],
[
"user2",
...
]
]
}
POST /api/v1/a/users/add
Admin override and add a new user to the system.
query param: | api_key required - the api key for your account to make the call with |
---|---|
query param: | username required - the username of the new user |
query param: | email required - the email address of the new user |
query param: | callback - wrap JSON response in an optional callback |
success 200: | If successful a “200 OK” will be returned |
---|
requests.post('http://127.0.0.1:6543/api/v1/a/users/list?api_key=12345...', {
'email': 'test@dummy.com',
'username': 'test',
})
>>>{
"username": "admin",
"email": "test@dummy.com",
"id": 11,
"random_pass": "blah123",
...
}
DELETE /api/v1/a/users/delete/:username
Admin endpoint to remove a user from the system.
Currently meant for bad new user accounts that removes activation and user account. Does not reach into bmarks/tags.
query param: | api_key required - the api key for your account to make the call with |
---|---|
query param: | callback - wrap JSON response in an optional callback |
success 200: | If successful a “200 OK” will be returned |
---|
requests.post('http://127.0.0.1:6543/api/v1/a/users/delete/admin?api_key=12345...')
>>>{
"success": true,
"message": "Removed user: admin"
}
GET /api/v1/admin/stats/bmarks
Return the most recent counts of bookmarks, tags, and unique bookmarks
query param: api_key required - the api key for your account to make the call with query param: count - the number in the result you wish to return query param: page - the page number to get results for based off of the count specified query param: callback - wrap JSON response in an optional callback
requests.get('http://127.0.0.1:6543/api/v1/admin/stats/bmarks?api_key=12345...')
>>> ...
GET /a/social/twitter_refresh/:username
Refresh twitter fetch for specific user
query param: api_key required - the api key for your account to make the call with query param: callback - wrap JSON response in an optional callback
requests.get('http://127.0.0.1:6543/api/v1/a/social/twitter_refresh/admin?api_key=12345...')
>>> {
"message": "running bot to fetch user's tweets"
"success": true,
}
GET /a/social/twitter_refresh/all
Refresh twitter fetch for all the users
query param: api_key required - the api key for your account to make the call with query param: callback - wrap JSON response in an optional callback
requests.get('http://127.0.0.1:6543/api/v1/a/social/twitter_refresh/all?api_key=12345...')
>>> {
"message": "running bot to fetch user's tweets"
"success": true,
}
Since we started out attempting to match the Delicious api, we support some of those features. Not all of them make sense, so not all are implemented. Currently, the browser extensions communicate to the server via the Delicious api calls. Eventually, we’ll probably move those over to the official JSON api as I much prefer JSON and hate dealing with the XML calls that Delicious implemented.
All of our api calls are POST since we allow for some large content payloads.
All of our delicious.com api calls that make changes to the database, require an api_key parameter to be passed with the request. This is a slight deviation from the Delicious API since we do not currently support login.
For things on the todo list see the Trello board:
Location: | @mitechie‘s house. Ping him for details. |
---|---|
Time: | 11am |
The main task is to work on test coverage of Bookie. We’ll be using coverage.py to find areas missing tests and work on getting better coverage for them.
A couple of people have expressed interest in working on breadability and some sites it’s not processing that well.
Lunch will be provided and if you’re interesting in working on something else please let me know. The day will end when people get tired of sprinting.
Location: | PyOhio see @mitechie for where on Friday and Saturday info is on the |
---|---|
Site: | http://pyohio.org/Sprints/ |
Time: | Refer to the PyOhio site for details. |
We’ll be working on any of the Bookie part of apps. This includes breadability, bookie_parser, bookie_firefox, bookie_android. So if you want to hack on a Pyramid app, celery processing, parsing algorigthms, Tornado apps on Heroku, or CoffeeScript we’ve got something you can work on.
Specific task ideas include:
Location: | PyCon Sprints! |
---|---|
Time: | All the time! |
Get 0.4 out the door, this means FF extension completed and do a release
Start 0.5 release, possible items include:
- Signup system with throttled registrations/waiting list + api/ui for it
- In place editing
- Easy reader UI for !toread bmarks
- Look at adding smarter tag suggestions (js page parser + smarter server side)
- Celery/out of process worker system for things
- Rework the url parsing worker for the celery backend, requests, async
- get yeti and browser functional tests running SST?
- better mobile/responsive UI bits
- Stats stats and more stats API/UI
Location: My Place Time: 10am-4pm
The main task is to work on a full Firefox extension based off the add-on SDK. Current base is located:
A lot’s changed. We could use help with:
Location: PyOhio see @mitechie for where on Friday and Saturday info is on the site: http://pyohio.org/Sprints/ Time: Friday will be in the evening and Saturday with the PyOhio peeps after the conference
We’re going to have the first ever Bookie sprint on Friday the 22nd of April. Some potential goals:
The doors open up at 11am and we’ll have some lunch delivered around 12:30pm. I’ll chase everyone away somewhere around 4pm.
We’re big fans of Open Source development. Bookie is AGPL licensed and we encourage you to fork it from Github and try it out. Have a pet feature you’d love to work with? Then let us know. Jump into our IRC channel #bookie on freenode to chat with the other developers.
At this time we support importing the Delicious and Google Bookmark exports. They’re just html files. The importer detects which format you’re got and will process them all in one swoop. It should run pretty quickly, though if you have over 5,000 bookmarks you might notice it taking a little bit to get through them.
When importing, make sure you enter the API key that is set in your installation’s .ini file. That’s the security measure that prevents others from importing into your Bookie installation. That key should be changed from the default.
Most of the details are available over in the extension docs. Make sure that after you install it, that you go into the options to configure it to talk to your specific Bookie installation.
You can use a bookmarklet to save bookmarks from any other browser, including mobile browsers. Log into your account and you can get a copy/paste-able bookmarklet from your account page.
When you load bookmarks into the system they are put into a fulltext index that makes things very searchable. The description field, extended description, and the tags are put into this index.
If you have enabled the readable parsed version of your bookmarks then these are also available for search, however it’s a separate checkbox as it might make searches slightly slower.
These indexes are updated as you add and delete bookmarks and should make finding things much easier thank just looking for tags.
Bookie now supports storing a cleaned version of the content of the page you just bookmarked. You can either enable it via the Chrome extension options or you can run the server side script provided in scripts/readability/existing.py to collect the page content. It’s not perfect, but in testing it provides a decent trimmed down version of the pages. This content is then indexed and made searchable. In the future we hope to use this to provide fast mobile viewing of your bookmarks and possibly even the ability to build ‘books’ of content that can be packaged together and turned into e-book material.
This is all very much inspired by Instapaper and Readability.
If you find pages of content that don’t work well please let us know and we can see if the code used to do the parsing can be tweaked to do a better job with your content.
Every time a bookmark url is clicked on it’s tracked and counted. This allows us to provide a view of your bookmarks by popularity. We’re hoping this provides a very useful interface when we start working on our mobile views. In the future we might also be able to provide some analytics much like bit.ly does for shortened urls.
Bookie currently supports Sqlite, MySQL, and Postgresql as a storage backend. This includes all of the fulltext indexing and searching. Obviously, how each database performs these is a little bit different so you might find better luck with some backends over others.
We’re working on making the website mobile friendly using responsive design techniques.
Location: | @mitechie‘s house. Ping him for details. |
---|---|
Time: | 11am |
The main task is to work on test coverage of Bookie. We’ll be using coverage.py to find areas missing tests and work on getting better coverage for them.
A couple of people have expressed interest in working on breadability and some sites it’s not processing that well.
Lunch will be provided and if you’re interesting in working on something else please let me know. The day will end when people get tired of sprinting.
Location: | PyOhio see @mitechie for where on Friday and Saturday info is on the |
---|---|
Site: | http://pyohio.org/Sprints/ |
Time: | Refer to the PyOhio site for details. |
We’ll be working on any of the Bookie part of apps. This includes breadability, bookie_parser, bookie_firefox, bookie_android. So if you want to hack on a Pyramid app, celery processing, parsing algorigthms, Tornado apps on Heroku, or CoffeeScript we’ve got something you can work on.
Specific task ideas include:
Location: | PyCon Sprints! |
---|---|
Time: | All the time! |
Get 0.4 out the door, this means FF extension completed and do a release
Start 0.5 release, possible items include:
- Signup system with throttled registrations/waiting list + api/ui for it
- In place editing
- Easy reader UI for !toread bmarks
- Look at adding smarter tag suggestions (js page parser + smarter server side)
- Celery/out of process worker system for things
- Rework the url parsing worker for the celery backend, requests, async
- get yeti and browser functional tests running SST?
- better mobile/responsive UI bits
- Stats stats and more stats API/UI
Location: My Place Time: 10am-4pm
The main task is to work on a full Firefox extension based off the add-on SDK. Current base is located:
A lot’s changed. We could use help with:
Location: PyOhio see @mitechie for where on Friday and Saturday info is on the site: http://pyohio.org/Sprints/ Time: Friday will be in the evening and Saturday with the PyOhio peeps after the conference
We’re going to have the first ever Bookie sprint on Friday the 22nd of April. Some potential goals:
The doors open up at 11am and we’ll have some lunch delivered around 12:30pm. I’ll chase everyone away somewhere around 4pm.