Initial commit

This commit is contained in:
Nicolas Wavrant 2017-04-29 10:36:57 +02:00
commit 12ac4e1c0e
13 changed files with 370 additions and 0 deletions

6
README.md Normal file
View File

@ -0,0 +1,6 @@
django-feed-manager is an application for Django which manages different RSS feeds and their lifetime.
This application has been developed to gather results of recurrent and automatized actions by multiple servers and allow different users to be updated using a standard format (which then allow automatic treatment of the notifications).
Feeds and feed's items can be created using HTTP Post requests. A cli python script is given in this repository to do that for you.

0
cli/__init__.py Normal file
View File

117
cli/reporter.py Normal file
View File

@ -0,0 +1,117 @@
#!/bin/env python
#-*- coding: utf-8 -*-
import argparse
import datetime
import json
import os
import requests
import sys
import configparser
CONFIG_PATH = '/etc/cloud_reporter.conf'
def generatePassword(length=20):
import random
import string
random.seed(os.urandom(4096))
chars = string.ascii_letters + string.digits + '"«»()@+-/*=%`°≠×÷−±@)[><—$,;.:…’'
return ''.join(random.choice(chars) for i in range(length))
def getConfigParser():
parser = configparser.ConfigParser()
parser.read(CONFIG_PATH)
return parser
def getCredential():
section_name = 'credential'
parser = getConfigParser()
return {
'username': parser.get(section_name, 'username'),
'password': parser.get(section_name, 'password'),
}
def subscribe():
import socket
data = {
'username': socket.gethostname(),
'password': generatePassword(),
}
response = requests.post('%s/createAccount/' % parser.get('server', 'base_url') , data=data)
if response.status_code == requests.codes.ok:
feed_id = json.loads(response.text)['feed_id']
parser = getConfigParser()
parser.add_section('feed')
parser.set('feed', 'id', feed_id)
parser.set('feed', 'username', data['username'])
parser.set('feed', 'password', data['password'])
with open(CONFIG_PATH, 'w') as config_file:
parser.write(config_file)
else:
sys.exit('Bad status returned by PubSub server')
def __getActionReport():
from collections import deque
content_list = deque()
with open('/var/log/ansible', 'r') as log:
log_line_list = log.readlines()
log_line_list.reverse()
for line in log_line_list:
content_list.appendleft(line)
if line.startswith('HEAD is now at'):
break
content_list.appendleft('\n\n-------\n\n')
with open('/var/log/aptitude', 'r') as log:
log_line_list = log.readlines()
log_line_list.reverse()
for line in log_line_list:
content_list.appendleft(line)
if line.startswith('Aptitude') and line.endswith(': log report\n'):
break
return '<pre>%s</pre>' % (''.join(content_list),)
def post():
parser = getConfigParser()
data = {
'username': parser.get('feed', 'username'),
'password': parser.get('feed', 'password'),
'link': 'http://exemple.com',
'description': __getActionReport(),
'pubDate': datetime.datetime.now().isoformat(),
}
data['title'] = 'Status Report for %s, %s' % (parser.get('feed', 'username'), data['pubDate'][:10])
response = requests.post(
'%s/%s/postItem/' % (parser.get('server', 'base_url'), parser.get('feed', 'id')),
data=data
)
print(response.status_code)
def createBaseConfig(**kw):
parser = ConfigParser.ConfigParser()
parser.add_section('server')
parser.set('server', 'base_url', 'https://me.wavrant.xyz/feed')
with open(CONFIG_PATH, 'w') as config_file:
parser.write(config_file)
def parseArguments():
parser = argparse.ArgumentParser()
return parser.parse_args()
def main():
if not os.path.exists(CONFIG_PATH):
createBaseConfig()
command_list = {
'subscribe': subscribe,
'post': post,
}
command_list[sys.argv[1]]()
if __name__ == '__main__':
main()

0
feedmanager/__init__.py Normal file
View File

6
feedmanager/admin.py Normal file
View File

@ -0,0 +1,6 @@
from django.contrib import admin
from .models import Feed, Item
admin.site.register(Feed)
admin.site.register(Item)

5
feedmanager/apps.py Normal file
View File

@ -0,0 +1,5 @@
from django.apps import AppConfig
class FeedConfig(AppConfig):
name = 'feed'

View File

@ -0,0 +1,42 @@
# -*- coding: utf-8 -*-
# Generated by Django 1.11 on 2017-04-29 08:28
from __future__ import unicode_literals
from django.conf import settings
from django.db import migrations, models
import django.db.models.deletion
class Migration(migrations.Migration):
initial = True
dependencies = [
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
]
operations = [
migrations.CreateModel(
name='Feed',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=100)),
('link', models.URLField()),
('description', models.TextField()),
('language', models.CharField(max_length=5)),
('maximum_length', models.IntegerField()),
('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
],
),
migrations.CreateModel(
name='Item',
fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('title', models.CharField(max_length=100)),
('link', models.URLField()),
('description', models.TextField()),
('pubDate', models.DateTimeField()),
('feed', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='feedmanager.Feed')),
],
),
]

View File

27
feedmanager/models.py Normal file
View File

@ -0,0 +1,27 @@
from django.contrib.auth.models import User
from django.db import models
class Feed(models.Model):
title = models.CharField(max_length=100)
link = models.URLField()
description = models.TextField()
language = models.CharField(max_length=5)
maximum_length = models.IntegerField()
user = models.OneToOneField(User, on_delete=models.CASCADE)
def __str__(self):
return self.title
def addItem(self, **kw):
Item.objects.create(feed=self, **kw)
class Item(models.Model):
title = models.CharField(max_length=100)
link = models.URLField()
description = models.TextField()
pubDate = models.DateTimeField()
feed = models.ForeignKey('Feed', on_delete=models.CASCADE)
def __str__(self):
return "%s[%s]" % (self.feed, self.title)

51
feedmanager/tests.py Normal file
View File

@ -0,0 +1,51 @@
from django.contrib.auth.models import User
from django.test import TestCase
from django.utils import timezone
from .models import Item
class PeriodTestCase(TestCase):
def test_createAccountAndPostOneItem(self):
data = {
'username': 'user',
'password': 'pass',
}
# Create an account
res = self.client.post(
'/feed/createAccount/',
{
'username': data['username'],
'password': data['password']
}
)
self.assertEqual(res.status_code, 200)
feed_id = res.json()['feed_id']
# Validate user
user_set = User.objects.filter(username=data['username'])
self.assertEqual(len(user_set), 1)
user = user_set.first()
user.is_active = True
user.save()
# Post a feed item
res = self.client.post(
'/feed/%d/postItem/' % feed_id,
{
'username': data['username'],
'password': data['password'],
'title': 'Item Title',
'link': 'Item Link',
'description': 'Item Description',
'pubDate': timezone.now().isoformat()[:10],
}
)
self.assertEqual(res.status_code, 200)
self.assertEqual(
len(Item.objects.all()), 1)

9
feedmanager/urls.py Normal file
View File

@ -0,0 +1,9 @@
from django.conf.urls import url
from . import views
urlpatterns = [
url(r'(?P<feed_id>[0-9]+)/$', views.printFeed, name='printFeed'),
url(r'(?P<feed_id>[0-9]+)/postItem/$', views.postItem, name='postItem'),
url(r'createAccount/$', views.createAccount, name='createAccount'),
]

73
feedmanager/views.py Normal file
View File

@ -0,0 +1,73 @@
import PyRSS2Gen as RSS2
from django.contrib.auth import authenticate
from django.contrib.auth.models import User
from django.http import JsonResponse, HttpResponse
from django.views.decorators.csrf import csrf_exempt
from django.shortcuts import get_object_or_404, render
from .models import Feed
FEED_ELEMENT_LIST = ['title', 'link', 'description', 'language']
ITEM_ELEMENT_LIST = ['title', 'link', 'description', 'pubDate']
def __getRSSItemList(item_list):
rss_item_list = []
for item in item_list:
rss_item_list.append(
RSS2.RSSItem(
**{element: getattr(item, element, '')
for element in ITEM_ELEMENT_LIST}
)
)
return rss_item_list
def printFeed(request, feed_id):
feed = get_object_or_404(Feed, pk=feed_id)
item_list = feed.item_set.order_by('pubDate')
return HttpResponse(
RSS2.RSS2(
items=__getRSSItemList(item_list),
**{element: getattr(feed, element, '')
for element in FEED_ELEMENT_LIST}
).to_xml()
)
@csrf_exempt
def createAccount(request):
if 'username' not in request.POST \
or 'password' not in request.POST:
return HttpResponse(status=500)
user = User.objects.create_user(request.POST['username'], password=request.POST['password'], is_active=False)
user.save()
feed = Feed.objects.create(
title = "Computer status for %s" % request.POST['username'],
link = "",
description = "",
language = "en",
maximum_length = 50,
user=user,
)
feed.save()
return JsonResponse({'feed_id': feed.id})
@csrf_exempt
def postItem(request, feed_id):
feed = get_object_or_404(Feed, pk=feed_id)
item_attribute_dict = {}
for item in ITEM_ELEMENT_LIST:
try:
item_attribute_dict[item] = request.POST[item]
except KeyError:
pass
try:
user = authenticate(username=request.POST['username'], password=request.POST['password'])
if user is None:
return HttpResponse(status=401)
except KeyError:
return HttpResponse(status=500)
feed.addItem(**item_attribute_dict)
return HttpResponse(status=200)

34
setup.py Normal file
View File

@ -0,0 +1,34 @@
import os
from setuptools import setup
# Utility function to read the README file.
# Used for the long_description. It's nice, because now 1) we have a top level
# README file and 2) it's easier to type in the README file than to put a raw
# string in below ...
def read(fname):
return open(os.path.join(os.path.dirname(__file__), fname)).read()
setup(
name = "django-feed-manager",
version = "0.1",
author = "Nicolas Wavrant",
author_email = "nicolas.wavrant@gmail.com",
description = ("A feed manager for Django, aiming to report automated tasks results in a standard format."),
license = "BSD",
keywords = "django feed rss",
url = "https://github.com/Sebatyne/django-feed-manager",
long_description = read('README.md'),
install_requires = [
'requests',
],
entry_points = {
'console_scripts': [
'reporter=cli.reporter:main',
],
},
classifiers = [
"Development Status :: 4 - Beta",
"Topic :: Utilities",
"License :: OSI Approved :: BSD License",
],
)