Skip to content

Commit a63ad7a

Browse files
committed
Initial commit
0 parents  commit a63ad7a

File tree

4 files changed

+248
-0
lines changed

4 files changed

+248
-0
lines changed

README.rst

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
=======
2+
Mongurl
3+
=======
4+
-----------------------------------------
5+
URL shortener using Bubble.py and MongoDB
6+
-----------------------------------------
7+
8+
9+
10+
Dependencies
11+
============
12+
13+
1. MongoDB (www.mongodb.org)
14+
2. pymongo (easy_install pymongo)
15+
3. Bottle.py (http://github.com/defnull/bottle/raw/master/bottle.py)
16+
17+
18+
To Use
19+
======
20+
21+
Run ``./mongurl`` then go to http://localhost:8080. You can also use the REST
22+
interface if you prefer.
23+
24+
Directly from the shell::
25+
26+
curl -X POST http://localhost:8080 -d http://www.mongodb.org
27+
28+
As a bash funtion::
29+
30+
posturl() {
31+
ID=$(curl -X POST http://localhost:8080 -s -d "$1")
32+
echo http://localhost:8080/$ID
33+
}
34+
posturl www.mongodb.org
35+
36+
37+
License
38+
=======
39+
40+
MIT
41+
42+
Copyright (c) 2009 Mathias Stearn <mathias at 10gen dot com>
43+
44+
Permission is hereby granted, free of charge, to any person
45+
obtaining a copy of this software and associated documentation
46+
files (the "Software"), to deal in the Software without
47+
restriction, including without limitation the rights to use,
48+
copy, modify, merge, publish, distribute, sublicense, and/or sell
49+
copies of the Software, and to permit persons to whom the
50+
Software is furnished to do so, subject to the following
51+
conditions:
52+
53+
The above copyright notice and this permission notice shall be
54+
included in all copies or substantial portions of the Software.
55+
56+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
57+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
58+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
59+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
60+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
61+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
62+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
63+
OTHER DEALINGS IN THE SOFTWARE.

mainpage.tpl

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
2+
<html>
3+
<head>
4+
<title>Mongurl - A URL shortner using MongoDB</title>
5+
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" >
6+
<style>
7+
.link_id {
8+
font-family: monospace;
9+
font-style: bold;
10+
}
11+
</style>
12+
</head>
13+
<body>
14+
<h1>Mongurl</h1>
15+
16+
<form action='/' method='POST'>
17+
<label for="url"> Add </label>
18+
<input type="text" name="url" />
19+
</form>
20+
21+
<ol>
22+
%for url in urls:
23+
<li>
24+
<a href="/{{url['_id']}}/info" class="link_id">{{url['_id']}}</a> -
25+
<img width="10" height="10" src="{{urljoin(url['url'], '/favicon.ico')}}" />
26+
<a href="/{{url['_id']}}">{{url['url']}}</a>
27+
({{url['visits']}} visits)
28+
</li>
29+
%end
30+
</ol>
31+
</body>
32+
</html>
33+
34+
%# vim: set ft=html:

mongurl.py

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
#!/usr/bin/python
2+
import pymongo
3+
from bottle import *
4+
import sys
5+
import random
6+
import urllib
7+
import urlparse
8+
from datetime import datetime
9+
import re
10+
11+
db = pymongo.Connection().mongurl
12+
13+
db.urls.ensure_index([('visits',1), ('created',1)])
14+
15+
@route('/')
16+
def index():
17+
urls = db.urls.find().sort([('visits',-1), ('created',-1)]).limit(100)
18+
return template('mainpage', urls=urls, urljoin=urlparse.urljoin)
19+
20+
def genid():
21+
chars = ('abcdefghijklmnopqrstuvwxyz'
22+
'ABCDEFGHIJKLMNOPQRSTUVWXYZ')
23+
return ''.join(random.sample(chars, 5))
24+
25+
@route('/', method="POST")
26+
def post_url():
27+
url = request.body.getvalue()
28+
from_browser=False
29+
if url.startswith('url='):
30+
from_browser = True
31+
url = urllib.unquote(url[4:])
32+
url = url.strip()
33+
34+
if not re.match(r'^\w+://', url):
35+
url = 'http://' + url
36+
37+
while True:
38+
try:
39+
_id = genid()
40+
db.urls.insert(dict(
41+
_id = _id,
42+
url = url,
43+
created = datetime.now(),
44+
visits = 0,
45+
last_visited = None,
46+
), safe=True)
47+
except:
48+
continue
49+
break
50+
51+
db.real_urls.insert(dict(
52+
_id = url,
53+
created = datetime.now(),
54+
visits = 0,
55+
last_visited = None,
56+
tags = []
57+
)) # will silently fail if real_url already in system
58+
59+
db.real_urls.update({'_id':url},{'$push':{'shorts': _id}})
60+
61+
if from_browser:
62+
redirect('/', 303)
63+
else:
64+
return _id
65+
66+
def get_url_data(id):
67+
url = db.urls.find_one({'_id':id})
68+
if not url:
69+
abort(404, 'Non-existant id')
70+
return url
71+
72+
@route('/:id')
73+
def forwarder(id):
74+
url = get_url_data(id)
75+
db.urls.update({'_id':id},
76+
{ '$inc': {'visits': 1}
77+
, '$set': {'last_visited': datetime.now()}
78+
})
79+
db.real_urls.update({'_id':url['url']},
80+
{ '$inc': {'visits': 1}
81+
, '$set': {'last_visited': datetime.now()}
82+
})
83+
redirect(url['url'], 301)
84+
85+
@route('/:id/url')
86+
def get_url(id):
87+
return get_url_data(id)['url']
88+
89+
@route('/:id/info')
90+
def get_info(id):
91+
url = get_url_data(id)
92+
url = dict((str(k), v) for k,v in url.iteritems())
93+
real_url = db.real_urls.find_one({'_id':url['url']})
94+
return template('urlpage', real_url=real_url, urljoin=urlparse.urljoin, **url)
95+
96+
if __name__ == '__main__':
97+
do_reload = '--reload' in sys.argv
98+
debug(do_reload)
99+
run(reloader=do_reload)

urlpage.tpl

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
2+
<html>
3+
<head>
4+
<title>Mongurl - A URL shortner using MongoDB</title>
5+
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" >
6+
<style>
7+
.link_id {
8+
font-family: monospace;
9+
font-style: bold;
10+
}
11+
</style>
12+
</head>
13+
<body>
14+
<h1>Mongurl</h1>
15+
16+
<h2>{{_id}}</h2>
17+
18+
<div>
19+
<img src="{{urljoin(url, '/favicon.ico')}}" />
20+
</div>
21+
22+
<p>
23+
URL: <a href="/{{_id}}">{{url}}</a><br />
24+
Clicks: {{visits}}<br />
25+
Created: {{created.ctime()}}<br />
26+
Last Clicked: {{last_visited}}<br />
27+
28+
<p>
29+
For all links with this url:<br />
30+
First linked to: {{real_url['created'].ctime()}}<br />
31+
Clicks: {{real_url['visits']}}<br />
32+
Last Clicked: {{real_url['last_visited']}}<br />
33+
34+
%if len(real_url['shorts']) > 1:
35+
<p>
36+
Other mongurls:
37+
<ul>
38+
%for id in real_url['shorts']:
39+
%if id != _id:
40+
<li>
41+
<a href="/{{id}}/info" class="link_id">{{id}}</a>
42+
</li>
43+
%end
44+
%end
45+
</ul>
46+
%end
47+
48+
49+
</body>
50+
</html>
51+
52+
%# vim: set ft=html:

0 commit comments

Comments
 (0)