Browse Source

v0.1.1

master
Doug Le Tough 2 years ago
commit
2b3e5d4d45
26 changed files with 2710 additions and 0 deletions
  1. +4
    -0
      .gitignore
  2. +404
    -0
      README.md
  3. +0
    -0
      api/__init__.py
  4. +103
    -0
      api/app.py
  5. +18
    -0
      config.local.ini
  6. BIN
      data/communes_2018.zip
  7. BIN
      data/departements_2018.zip
  8. BIN
      data/regions_20180101.zip
  9. +38
    -0
      dbclient/__init__.py
  10. +221
    -0
      demo/Protest/__init__.py
  11. +134
    -0
      demo/Protester/__init__.py
  12. +141
    -0
      demo/Subscription/__init__.py
  13. +0
    -0
      demo/__init__.py
  14. +15
    -0
      gunicorn.config.py
  15. +21
    -0
      jsonencoder/__init__.py
  16. +108
    -0
      logger/__init__.py
  17. +73
    -0
      protestometer_demo.py
  18. +0
    -0
      ressources/__init__.py
  19. +95
    -0
      ressources/communes.py
  20. +246
    -0
      ressources/counters.py
  21. +88
    -0
      ressources/departements.py
  22. +259
    -0
      ressources/protesters.py
  23. +208
    -0
      ressources/protests.py
  24. +80
    -0
      ressources/regions.py
  25. +133
    -0
      ressources/subscriptions.py
  26. +321
    -0
      sql/gis_config.sql

+ 4
- 0
.gitignore View File

@ -0,0 +1,4 @@
*venv*
*un~
*.pyc
*__pycache__*

+ 404
- 0
README.md View File

@ -0,0 +1,404 @@
# Protest'O'Meter
## A web API allowing protesters to count themselves.
# Install
## Prerequities
* Postgres
* GIS extension for Postgres
* Python3
## Installation
Once you have fullfilled the above requirements:
### Install required python modules
```
$ cd ./protestometer_api
$ # OPTIONNAL
$ virtualenv .venv
$ source .venv/bin/activate
$ # /OPTIONNAL
$ pip3 install falcon
$ pip3 install gunicorn
$ pip3 install pyscopg2
$ pip3 install pytz
```
### Clone the protestometer_api git repository:
```
$ git clone http://git.redatomik.org:/python/falcon/protestometer_api
```
### Run protestometer_api with gunicorn
You may set a reverse proxy as a frontend for production use.
In the protestometer_api, run the following command
```
gunicorn -c gunicorn.config.py api.app
```
### Play with protestometer_api
Protester_api comes with a demo script that tests all api end-points.
This script is known to be somewhat buggy. Use with care.
```
python3 protester_demo.py
```
# API endpoints (W.I.P)
## Protests
```
Method: GET
Route: /protests
Mandatory: None
Optional: None
Return: <JSON>
method: <string>
obj_name: <string>
obj_type: <string>
result: <string> ['error'|'success']
response: <dict>
<int> protest_id: <dict>
nom: <string>
description: <string>
start_date: <string>
expire_date: <string>
[...]
```
```
Method: GET
Route: /protests/<ID>/<OWNER>
Mandatory: None
Optional: None
Return: <JSON>
method: <string>
obj_name: <string>
obj_type: <string>
result: <string> ['error'|'success']
response: <dict>
id: <int>
nom: <string>
description: <string>
start_date: <string>
expire_date: <string>
```
```
Method: PUT
Route: /protests
Mandatory: <string> nom, <string> description, <string> start_date [YYYYmmddHHMM], <string> expire_date [YYYYmmddHHMM]
Optional: None
Return: <JSON>
method: <string>
obj_name: <string>
obj_type: <string>
result: <string> ['error'|'success']
response: <dict>
id: <int>
nom: <string>
description: <string>
start_date: <string>
expire_date: <string>
```
```
Method: PATCH
Route: /protests/<ID>/<OWNER>
Mandatory: <int> owner (owner id MUST be given in URL and in data parameters)
Optional: <string> nom, <string> description, <string> start_date [YYYYmmddHHMM], <string> expire_date [YYYYmmddHHMM]
Return: <JSON>
method: <string>
obj_name: <string>
obj_type: <string>
result: <string> ['error'|'success']
response: <dict>
id: <int>
nom: <string>
description: <string>
start_date: <string>
expire_date: <string>
```
```
Method: DELETE
Route: /protests/<ID>/<OWNER>
Mandatory: None
Optional: None
Return: <JSON>
method: <string>
obj_name: <string>
obj_type: <string>
result: <string> ['error'|'success']
response: <dict>
id: <int>
nom: <string>
description: <string>
start_date: <string>
expire_date: <string>
```
## Protesters
```
Method: PUT
Route: /protesters
Mandatory: <uuid> protester_uid, <float> lon, <float> lat
Optional: None
Return: <JSON>
method: <string>
obj_name: <string>
obj_type: <string>
result: <string> ['error'|'success']
response: <dict>
protester_id: <int>
potester_uid: <uuid>
lon: <float>
lat: <float>
last_seen: <datetime>
rgid: <int>
dgid: <int>
cgid: <int>
```
```
Method: GET
Route: /protesters/<UUID>
Mandatory: None
Optional: None
Return: <JSON>
method: <string>
obj_name: <string>
obj_type: <string>
result: <string> ['error'|'success']
response: <dict>
protester_id: <int>
potester_uid: <uuid>
lon: <float>
lat: <float>
last_seen: <datetime>
rgid: <int>
dgid: <int>
cgid: <int>
```
```
Method: PATCH
Route: /protesters/<UUID>
Mandatory: None
Optional: <float> lon, <float> lat
Return: <JSON>
method: <string>
obj_name: <string>
obj_type: <string>
result: <string> ['error'|'success']
response: <dict>
protester_id: <int>
potester_uid: <uuid>
lon: <float>
lat: <float>
last_seen: <datetime>
rgid: <int>
dgid: <int>
cgid: <int>
```
```
Method: DELETE
Route: /protesters/<UUID>
Mandatory: None
Optional: None
Return: <JSON>
method: <string>
obj_name: <string>
obj_type: <string>
result: <string> ['error'|'success']
response: <dict>
protester_id: <int>
potester_uid: <uuid>
lon: <float>
lat: <float>
last_seen: <datetime>
rgid: <int>
dgid: <int>
cgid: <int>
```
### subscriptions
```
Method: GET
Route: /subscriptions
Mandatory: <int> protester_id
Optional: None
Return: <JSON>
method: <string>
obj_name: <string>
obj_type: <string>
result: <string> ['error'|'success']
response: <dict>
<int> protest_id: <dict>
protester_id: <int>
subscription_date: <string>
name: <string>
descritpion: <string>
```
```
Method: PUT
Route: /subscriptions/<protest_id>/<protester_id>
Mandatory: None
Optional: None
Return: <JSON>
method: <string>
obj_name: <string>
obj_type: <string>
result: <string> ['error'|'success']
response: <dict>
protest_id: <int>
protester_id: <int>
subscription_date: <string>
```
```
Method: DELETE
Route: /subscriptions/<protest_id>/<protester_id>
Mandatory: None
Optional: None
Return: <JSON>
method: <string>
obj_name: <string>
obj_type: <string>
result: <string> ['error'|'success']
response: <dict>
protest_id: <int>
protester_id: <int>
subscription_date: <string>
```
## Regions
```
Method: GET
Route: /regions
Mandatory: None
Optional: None
Return: <JSON>
action: <string>
type: <string>
result: <string> ['error'|'success']
response: <dict>
<int> rgid: <dict>
code_insee: <int>
nom: <string>
lon: <float>
lat: <float>
[...]
```
```
Method: GET
Route: /regions/<rgid>
Mandatory: None
Optional: None
Return: <JSON>
action: <string>
type: <string>
result: <string> ['error'|'success']
response: <dict>
rgid: <int>
code_insee: <int>
nom: <string>
lon: <float>
lat: <float>
[...]
```
## Departements
```
Method: GET
Route: /departements
Mandatory: None
Optional: <int> rgid
Return: <JSON>
action: <string>
type: <string>
result: <string> ['error'|'success']
response: <dict>
<int> dgid: <dict>
rgid: <int>
code_insee: <int>
nom: <string>
lon: <float>
lat: <float>
[...]
```
```
Method: GET
Route: /departements/<dgid>
Mandatory: None
Optional: None
Return: <JSON>
action: <string>
type: <string>
result: <string> ['error'|'success']
response: <dict>
dgig: <int>
rgid: <int>
code_insee: <int>
nom: <string>
lon: <float>
lat: <float>
[...]
```
## Communes
```
Method: GET
Route: /communes
Mandatory: None
Optional: <int> rgid, <int> dgid
Return: <JSON>
action: <string>
type: <string>
result: <string> ['error'|'success']
response: <dict>
<int> cgid: <dict>
rgid: <int>
dgid: <int>
code_insee: <int>
nom: <string>
lon: <float>
lat: <float>
[...]
```
```
Method: GET
Route: /communes/<cgid>
Mandatory: None
Optional: None
Return: <JSON>
action: <string>
type: <string>
result: <string> ['error'|'success']
response: <dict>
cgid: <int>
rgid: <int>
dgid: <int>
code_insee: <int>
nom: <string>
lon: <float>
lat: <float>
[...]
```
## Counters
```
Method: GET
Route: /communes/<type> [regions|departements|communes]
Mandatory: None
Optional: <int> rgid, <int> dgid, <int> cgid
Return: <JSON>
action: <string>
type: <string>
result: <string> ['error'|'success']
response: <dict>
<int> gid: <dict>
rgid|dgid|cgid: <int>
nom: <string>
cpt: <int>
[...]
```

+ 0
- 0
api/__init__.py View File


+ 103
- 0
api/app.py View File

@ -0,0 +1,103 @@
#!/usr/bin/env python3
# -*- coding: utf-8
import os
import json
import falcon
import configparser
from logger import Logger
from dbclient import ReallyThreadedConnectionPool
from ressources.regions import Item as Regions_Item
from ressources.regions import Collection as Regions_Collection
from ressources.departements import Item as Departements_Item
from ressources.departements import Collection as Departements_Collection
from ressources.communes import Item as Communes_Item
from ressources.communes import Collection as Communes_Collection
from ressources.protests import Item as Protests_Item
from ressources.protests import Collection as Protests_Collection
from ressources.protesters import Item as Protesters_Item
from ressources.protesters import Collection as Protesters_Collection
from ressources.subscriptions import Item as Subscriptions_Item
from ressources.subscriptions import Collection as Subscriptions_Collection
from ressources.counters import Collection as Counters_Collection
########################################################################
# Configuration loading
########################################################################
LOCAL_CONFIG_FILENAME = 'config.local.ini'
if not os.path.exists(LOCAL_CONFIG_FILENAME):
print("\033[91mError: Config file not found: %s\033[0m" % LOCAL_CONFIG_FILENAME)
exit(1)
config = configparser.ConfigParser()
config.read(LOCAL_CONFIG_FILENAME)
########################################################################
# Timezone configuration
########################################################################
tz = config['global']['timezone']
########################################################################
# Logger configuration
########################################################################
logger = Logger(config['logging']['level'])
########################################################################
# Application
########################################################################
api = application = falcon.API()
api.req_options.auto_parse_form_urlencoded = True
########################################################################
# Database connexion
########################################################################
try:
db_host = config['database']['host']
db_port = config['database']['port']
db_name = config['database']['name']
db_user = config['database']['user']
db_password = config['database']['password']
db_min_pool_size = config['database']['min_pool_size']
db_max_pool_size = config['database']['max_pool_size']
connection_pool = ReallyThreadedConnectionPool(logger,
tz,
int(db_min_pool_size),
int(db_max_pool_size),
database=db_name,
password=db_password,
host=db_host,
user=db_user)
except Exception as e:
logger.critical("No connection to database: %s" % str(e))
########################################################################
# Ressources
########################################################################
regions_collection = Regions_Collection(connection_pool, logger)
regions_item = Regions_Item(connection_pool, logger)
departements_collection = Departements_Collection(connection_pool, logger)
departements_item = Departements_Item(connection_pool, logger)
communes_collection = Communes_Collection(connection_pool, logger)
communes_item = Communes_Item(connection_pool, logger)
protests_collection = Protests_Collection(connection_pool, logger)
protests_item = Protests_Item(connection_pool, logger)
protesters_collection = Protesters_Collection(connection_pool, logger, config['global']['srid'])
protesters_item = Protesters_Item(connection_pool, logger, config['global']['srid'])
counters_collection = Counters_Collection(connection_pool, logger)
subscriptions_collection = Subscriptions_Collection(connection_pool, logger)
subscriptions_item = Subscriptions_Item(connection_pool, logger)
########################################################################
# Application routes
########################################################################
api.add_route('/regions', regions_collection)
api.add_route('/regions/{rgid}', regions_item)
api.add_route('/departements', departements_collection)
api.add_route('/departements/{dgid}', departements_item)
api.add_route('/communes', communes_collection)
api.add_route('/communes/{cgid}', communes_item)
api.add_route('/protests', protests_collection)
api.add_route('/protests/{protest_id}/{owner_id}', protests_item)
api.add_route('/protesters', protesters_collection)
api.add_route('/protesters/{protester_uid}', protesters_item)
api.add_route('/subscriptions', subscriptions_collection)
api.add_route('/subscriptions/{protest_id}/{protester_id}', subscriptions_item)
api.add_route('/counters/{counter_type}/{protest_id}/{rgid}/{dgid}/{cgid}', counters_collection)

+ 18
- 0
config.local.ini View File

@ -0,0 +1,18 @@
[global]
timezone = Europe/Paris
srid = 4326
[logging]
level = debug
[database]
host = localhost
port = 5432
name = gis
user = gis
password = gis
min_pool_size = 1
max_pool_size = 50
[api]
scheme = http
host = localhost
interface = 127.0.0.1
port = 1337

BIN
data/communes_2018.zip View File


BIN
data/departements_2018.zip View File


BIN
data/regions_20180101.zip View File


+ 38
- 0
dbclient/__init__.py View File

@ -0,0 +1,38 @@
#!/usr/bin/env python3
# -*- coding: utf-8
from pytz import timezone
from threading import Semaphore
from psycopg2.pool import ThreadedConnectionPool
import uuid
class ReallyThreadedConnectionPool(ThreadedConnectionPool):
def __init__(self, logger, tz, minconn, maxconn, *args, **kwargs):
self.logger = logger
self._semaphore = Semaphore(maxconn)
self.tz = timezone(tz)
self.uuid = uuid.uuid1()
super(ReallyThreadedConnectionPool, self).__init__(minconn, maxconn, *args, **kwargs)
def getconn(self, *args, **kwargs):
self._semaphore.acquire()
return super(ReallyThreadedConnectionPool, self).getconn(*args, **kwargs)
def putconn(self, *args, **kwargs):
super(ReallyThreadedConnectionPool, self).putconn(*args, **kwargs)
self._semaphore.release()
def get_db_connection(self):
try:
conn = self.getconn()
cursor = conn.cursor()
return (conn, cursor)
except Exception as e:
self.logger.critical("Unable to get connection from pool: %s [%d]" % (str(e), len(self._used)))
def release_db_connection(self, conn, cursor):
try:
cursor.close()
self.putconn(conn)
except Exception as e:
self.logger.warn("Unable to release connection: %s [%d]" % (str(e), len(self._used)))

+ 221
- 0
demo/Protest/__init__.py View File

@ -0,0 +1,221 @@
#!/usr/bin/env python3
# -*- coding: utf-8
import random
import requests
import string
import json
import psycopg2
import inspect
from logger import Logger
def random_string(maxlen):
letters = string.ascii_lowercase
return ''.join(random.choice(letters) for i in range(1+int(random.random()*maxlen)))
def random_paragraph(maxlen, maxwords):
paragraph = ''
for x in range(int(random.random()*maxwords)):
paragraph += '%s ' % random_string(int(random.random()*maxlen))
if x == maxwords/4:
paragraph += '\n'
return paragraph
def random_2digits(dmax):
dmax = int(1+random.random()*dmax)
if dmax < 10:
return '0%s' % dmax
return dmax
def random_date():
year = str(2018+int(random.random()*20))
month = str(random_2digits(12))
day = str(random_2digits(28))
hour = str(random_2digits(23))
minute = str(random_2digits(59))
ttime = '%s%s%sT%s%sZ' % (year, month, day, hour, minute)
return ttime
class Protests(object):
def __init__(self, api_host, connection_pool, logger):
self.API_HOST = api_host
self.logger = logger
self.connection_pool = connection_pool
self.PROTESTS = self.list_protests()
def get_protester(self):
try:
connection, cursor = self.connection_pool.get_db_connection()
req = 'select id from protesters limit 1000;'
cursor.execute(req)
result = cursor.fetchall()
self.connection_pool.release_db_connection(connection, cursor)
if result:
result = [uid[0] for uid in result]
return result.pop()
raise Exception('WTFError: No protesters in base')
except psycopg2.Error as e:
self.connection_pool.release_db_connection(connection, cursor)
self.logger.error('[%s]: %s' % (inspect.currentframe().f_code.co_name, e))
except Exception as e:
self.logger.error('[%s]: %s' % (inspect.currentframe().f_code.co_name, e))
def list_protests(self):
try:
connection, cursor = self.connection_pool.get_db_connection()
req = 'select id, owner from protests limit 10;'
cursor.execute(req)
result = cursor.fetchall()
self.connection_pool.release_db_connection(connection, cursor)
if result:
return result
raise Exception('WTFError: No protests in base')
except psycopg2.Error as e:
self.connection_pool.release_db_connection(connection, cursor)
self.logger.error('[%s]: %s' % (inspect.currentframe().f_code.co_name, e))
return []
except Exception as e:
self.logger.error('[%s]: %s' % (inspect.currentframe().f_code.co_name, e))
return []
def add_protest(self):
try:
url = '%s/%s' % (self.API_HOST, 'protests')
owner = self.get_protester()
name = random_string(20)
description = random_paragraph(20, 100)
start_date = random_date()
expire_date = random_date()
if start_date > expire_date:
start_date, expire_date = expire_date, start_date
args = {'owner': owner,
'name': name,
'description': description,
'start_date': start_date,
'expire_date': expire_date
}
r = requests.put(url, data=args)
result = r.json()['result']
response = r.json()['response']
if result == 'success':
msg = '\033[92mAdded protest:\033[0m %s' % (response['id'])
self.logger.info(msg)
else:
msg = '\033[91mFailed to add protest:\033[0m %s [%s]' % (response, (owner, start_date, expire_date))
self.logger.error(msg)
except json.JSONDecodeError as e:
self.logger.debug('add [r]: %s' % r)
self.logger.debug('add [json]: %s' % r.json())
self.logger.debug('add [result]: %s' % result)
self.logger.debug('add [response]: %s' % response)
exit(1)
def get_protest(self):
try:
if not len(self.PROTESTS):
self.PROTESTS = self.list_protests()
if len(self.PROTESTS):
protest_id, owner = self.PROTESTS.pop()
else:
raise Exception('WTFError: get_protest(): %d' % len(self.PROTESTS))
url = '%s/%s/%s/%s' % (self.API_HOST, 'protests', protest_id, owner)
r = requests.get(url)
result = r.json()['result']
response = r.json()['response']
if result == 'success':
msg = '\033[92mGot protest:\033[0m %s' % (protest_id)
self.logger.info(msg)
else:
msg = '\033[91mFailed to get protest:\033[0m %s [%s]' % (response, protest_id)
self.logger.error(msg)
except json.JSONDecodeError as e:
self.logger.debug('get [r]: %s' % r)
self.logger.debug('get [json]: %s' % r.json())
self.logger.debug('get [result]: %s' % result)
self.logger.debug('get [response]: %s' % response)
exit(1)
def get_protests(self):
try:
url = '%s/%s' % (self.API_HOST, 'protests')
r = requests.get(url)
result = r.json()['result']
response = r.json()['response']
if result == 'success':
if response:
msg = '\033[92mGot protests list:\033[0m len=%d' % len(response)
return self.logger.info(msg)
msg = '\033[92mGot empty protests list:\033[0m %s' % response
self.logger.warn(msg)
exit(1)
else:
msg = '\033[91mFailed to get protests list:\033[0m %s' % response
self.logger.error(msg)
except json.JSONDecodeError as e:
self.logger.debug('gets [r]: %s' % r)
self.logger.debug('gets [json]: %s' % r.json())
self.logger.debug('gets [result]: %s' % result)
self.logger.debug('gets [response]: %s' % response)
exit(1)
def delete_protest(self):
try:
if not len(self.PROTESTS):
self.PROTESTS = self.list_protests()
if len(self.PROTESTS):
protest_id, owner = self.PROTESTS.pop()
else:
raise Exception('WTFError: delete_protest(): %d' % len(self.PROTESTS))
url = '%s/%s/%s/%s' % (self.API_HOST, 'protests', protest_id, owner)
r = requests.delete(url)
result = r.json()['result']
response = r.json()['response']
if result == 'success':
msg = '\033[92mDeleted protest:\033[0m %s' % (protest_id)
self.logger.info(msg)
else:
msg = '\033[91mFailed to delete protest:\033[0m %s [%s]' % (response, (protest_id, owner))
self.logger.error(msg)
except json.JSONDecodeError as e:
self.logger.debug('delete [r]: %s' % r)
self.logger.debug('delete [json]: %s' % r.json())
self.logger.debug('delete [result]: %s' % result)
self.logger.debug('delete [response]: %s' % response)
exit(1)
def patch_protest(self):
try:
if not len(self.PROTESTS):
self.PROTESTS = self.list_protests()
if len(self.PROTESTS):
protest_id, owner = self.PROTESTS.pop()
else:
raise Exception('WTFError: patch_protest(): %d' % len(self.PROTESTS))
url = '%s/%s/%s/%s' % (self.API_HOST, 'protests', protest_id, owner)
name = random_string(20)
description = random_paragraph(20, 100)
start_date = random_date()
expire_date = random_date()
if start_date > expire_date:
start_date, expire_date = expire_date, start_date
args = {'owner': owner,
'name': name,
'description': description,
'start_date': start_date,
'expire_date': expire_date
}
r = requests.patch(url, data=args)
result = r.json()['result']
response = r.json()['response']
if result == 'success':
msg = '\033[92mPatched protest:\033[0m %s' % (protest_id)
self.logger.info(msg)
else:
msg = '\033[91mFailed to patch protest:\033[0m %s [%s]' % (response, (protest_id, start_date, expire_date))
self.logger.error(msg)
except json.JSONDecodeError as e:
self.logger.debug('patch [r]: %s' % r)
self.logger.debug('patch [json]: %s' % r.json())
self.logger.debug('patch [result]: %s' % result)
self.logger.debug('patch [response]: %s' % response)
exit(1)

+ 134
- 0
demo/Protester/__init__.py View File

@ -0,0 +1,134 @@
#!/usr/bin/env python3
# -*- coding: utf-8
import random
import requests
import uuid
import json
import psycopg2
import inspect
from logger import Logger
class Protesters(object):
def __init__(self, api_host, connection_pool, logger):
self.API_HOST = api_host
self.LAT_MAX = 51.0716
self.LAT_MIN = 41.3166
self.LON_MIN = -9.0666
self.LON_MAX = 9.56
self.logger = logger
self.connection_pool = connection_pool
self.PROTESTERS = self.get_protesters()
def get_protesters(self):
try:
connection, cursor = self.connection_pool.get_db_connection()
req = 'select uid from protesters limit 10;'
cursor.execute(req)
result = cursor.fetchall()
self.connection_pool.release_db_connection(connection, cursor)
if result:
result = [uid[0] for uid in result]
return result
raise Exception('WTFError: No protesters in base')
except psycopg2.Error as e:
self.connection_pool.release_db_connection(connection, cursor)
self.logger.error('[%s]: %s' % (inspect.currentframe().f_code.co_name, e))
except Exception as e:
self.logger.error('[%s]: %s' % (inspect.currentframe().f_code.co_name, e))
return []
def add_protester(self):
try:
url = '%s/%s' % (self.API_HOST, 'protesters')
lon = random.uniform(self.LON_MIN, self.LON_MAX)
lat = random.uniform(self.LAT_MIN, self.LAT_MAX)
protester_uid = uuid.uuid1()
args = {'protester_uid': protester_uid,
'lon': lon,
'lat': lat}
r = requests.put(url, data=args)
result = r.json()['result']
response = r.json()['response']
if result == 'success':
msg = '\033[92mAdded protester:\033[0m %s' % (protester_uid)
self.logger.info(msg)
else:
msg = '\033[91mFailed to add protester:\033[0m %s [%s]' % (response, args)
self.logger.error(msg)
except json.JSONDecodeError as e:
self.logger.debug('add [r]: %s' % r)
self.logger.debug('add [json]: %s' % r.json())
self.logger.debug('add [result]: %s' % result)
self.logger.debug('add [response]: %s' % response)
exit(1)
def get_protester(self):
try:
if len(self.PROTESTERS) < 1:
self.PROTESTERS = self.get_protesters()
protester_uid = self.PROTESTERS.pop()
url = '%s/%s/%s' % (self.API_HOST, 'protesters', protester_uid)
r = requests.get(url)
result = r.json()['result']
response = r.json()['response']
if result == 'success':
msg = '\033[92mGot protester:\033[0m %s' % (protester_uid)
self.logger.info(msg)
else:
msg = '\033[91mFailed to get protester:\033[0m %s [%s]' % (response, protester_uid)
self.logger.error(msg)
except json.JSONDecodeError as e:
self.logger.debug('get [r]: %s' % r)
self.logger.debug('get [json]: %s' % r.json())
self.logger.debug('get [result]: %s' % result)
self.logger.debug('get [response]: %s' % response)
exit(1)
def delete_protester(self):
try:
if len(self.PROTESTERS) < 1:
self.PROTESTERS = self.get_protesters()
protester_uid = self.PROTESTERS.pop()
url = '%s/%s/%s' % (self.API_HOST, 'protesters', protester_uid)
r = requests.delete(url)
result = r.json()['result']
response = r.json()['response']
if result == 'success':
msg = '\033[92mDeleted protester:\033[0m %s' % (protester_uid)
self.logger.info(msg)
else:
msg = '\033[91mFailed to delete protester:\033[0m %s [%s]' % (response, protester_uid)
self.logger.error(msg)
except json.JSONDecodeError as e:
self.logger.debug('delete [r]: %s' % r)
self.logger.debug('delete [json]: %s' % r.json())
self.logger.debug('delete [result]: %s' % result)
self.logger.debug('delete [response]: %s' % response)
exit(1)
def patch_protester(self):
try:
if len(self.PROTESTERS) < 1:
self.PROTESTERS = self.get_protesters()
protester_uid = self.PROTESTERS.pop()
url = '%s/%s/%s' % (self.API_HOST, 'protesters', protester_uid)
lon = random.uniform(self.LON_MIN, self.LON_MAX)
lat = random.uniform(self.LAT_MIN, self.LAT_MAX)
args = {'lon': lon,
'lat': lat}
r = requests.patch(url, data=args)
result = r.json()['result']
response = r.json()['response']
if result == 'success':
msg = '\033[92mPatched protester:\033[0m %s' % (protester_uid)
self.logger.info(msg)
else:
msg = '\033[91mFailed to patch protester:\033[0m %s [%s]' % (response, protester_uid)
self.logger.error(msg)
except json.JSONDecodeError as e:
self.logger.debug('patch [r]: %s' % r)
self.logger.debug('patch [json]: %s' % r.json())
self.logger.debug('patch [result]: %s' % result)
self.logger.debug('patch [response]: %s' % response)
exit(1)

+ 141
- 0
demo/Subscription/__init__.py View File

@ -0,0 +1,141 @@
#!/usr/bin/env python3
# -*- coding: utf-8
import requests
import uuid
import json
import psycopg2
import inspect
from logger import Logger
class Subscriptions(object):
def __init__(self, api_host, connection_pool, logger):
self.API_HOST = api_host
self.logger = logger
self.connection_pool = connection_pool
self.SUBSCRIPTIONS = self.list_subscriptions()
def get_protester(self):
try:
connection, cursor = self.connection_pool.get_db_connection()
req = 'select id from protesters limit 1000;'
cursor.execute(req)
result = cursor.fetchall()
self.connection_pool.release_db_connection(connection, cursor)
if result:
result = [uid[0] for uid in result]
return result.pop()
raise Exception('WTFError: No protesters in base')
except psycopg2.Error as e:
self.connection_pool.release_db_connection(connection, cursor)
self.logger.error('[%s]: %s' % (inspect.currentframe().f_code.co_name, e))
except Exception as e:
self.logger.error('[%s]: %s' % (inspect.currentframe().f_code.co_name, e))
def get_protest(self):
try:
connection, cursor = self.connection_pool.get_db_connection()
req = 'select id from protests limit 1000;'
cursor.execute(req)
result = cursor.fetchall()
self.connection_pool.release_db_connection(connection, cursor)
if result:
result = [uid[0] for uid in result]
return result.pop()
raise Exception('WTFError: No protests in base')
except psycopg2.Error as e:
self.connection_pool.release_db_connection(connection, cursor)
self.logger.error('[%s]: %s' % (inspect.currentframe().f_code.co_name, e))
except Exception as e:
self.logger.error('[%s]: %s' % (inspect.currentframe().f_code.co_name, e))
def list_subscriptions(self):
try:
connection, cursor = self.connection_pool.get_db_connection()
req = 'select protest_id, protester_id from subscriptions limit 10;'
cursor.execute(req)
result = cursor.fetchall()
self.connection_pool.release_db_connection(connection, cursor)
if result:
return result
raise Exception('WTFError: No subscriptions in base')
except psycopg2.Error as e:
self.connection_pool.release_db_connection(connection, cursor)
self.logger.error('[%s]: %s' % (inspect.currentframe().f_code.co_name, e))
return []
except Exception as e:
self.logger.error('[%s]: %s' % (inspect.currentframe().f_code.co_name, e))
return []
def add_subscription(self):
try:
protest_id = self.get_protest()
protester_id = self.get_protester()
url = '%s/%s/%s/%s' % (self.API_HOST, 'subscriptions', protest_id, protester_id)
r = requests.put(url)
result = r.json()['result']
response = r.json()['response']
if result == 'success':
msg = '\033[92mAdded subscription:\033[0m %s' % (response)
self.logger.info(msg)
else:
msg = '\033[91mFailed to add subscription:\033[0m %s [%s]' % (response, (protest_id, protester_id))
self.logger.error(msg)
except json.JSONDecodeError as e:
self.logger.debug('add [r]: %s' % r)
self.logger.debug('add [json]: %s' % r.json())
self.logger.debug('add [result]: %s' % result)
self.logger.debug('add [response]: %s' % response)
exit(1)
def get_subscriptions(self):
try:
if not len(self.SUBSCRIPTIONS):
self.SUBSCRIPTIONS = self.list_subscriptions()
if len(self.SUBSCRIPTIONS):
protest_id, protester_id = self.SUBSCRIPTIONS.pop()
else:
raise Exception('WTFError: get_subscriptions(): %d' % len(self.SUBSCRIPTIONS))
url = '%s/%s' % (self.API_HOST, 'subscriptions')
args = {'protester_id': protester_id }
r = requests.get(url, args)
result = r.json()['result']
response = r.json()['response']
if result == 'success':
msg = '\033[92mGot subscriptions:\033[0m %d [%d]' % (len(response), protester_id)
self.logger.info(msg)
else:
msg = '\033[91mFailed to get subscriptions:\033[0m %s [%s]' % (response, protester_id)
self.logger.error(msg)
except json.JSONDecodeError as e:
self.logger.debug('get [r]: %s' % r)
self.logger.debug('get [r]: %s' % r.text)
self.logger.debug('get [json]: %s' % r.json())
self.logger.debug('get [result]: %s' % result)
self.logger.debug('get [response]: %s' % response)
exit(1)
def delete_subscription(self):
try:
if not len(self.SUBSCRIPTIONS):
self.SUBSCRIPTIONS = self.list_subscriptions()
if len(self.SUBSCRIPTIONS):
protest_id, protester_id = self.SUBSCRIPTIONS.pop()
else:
raise Exception('WTFError: delete_susbscription(): %d' % len(self.SUBSCRIPTIONS))
url = '%s/%s/%s/%s' % (self.API_HOST, 'subscriptions', protest_id, protester_id)
r = requests.delete(url)
result = r.json()['result']
response = r.json()['response']
if result == 'success':
msg = '\033[92mDeleted subscription:\033[0m %s' % (protest_id)
self.logger.info(msg)
else:
msg = '\033[91mFailed to delete subscription:\033[0m %s [%s]' % (response, (protest_id, protester_id))
self.logger.error(msg)
except json.JSONDecodeError as e:
self.logger.debug('delete [r]: %s' % r)
self.logger.debug('delete [json]: %s' % r.json())
self.logger.debug('delete [result]: %s' % result)
self.logger.debug('delete [response]: %s' % response)
exit(1)

+ 0
- 0
demo/__init__.py View File


+ 15
- 0
gunicorn.config.py View File

@ -0,0 +1,15 @@
import os
import configparser
import multiprocessing
LOCAL_CONFIG_FILENAME = 'config.local.ini'
if not os.path.exists(LOCAL_CONFIG_FILENAME):
print("\033[91mError: Config file not found: %s\033[0m" % LOCAL_CONFIG_FILENAME)
exit(1)
conf = configparser.ConfigParser()
conf.read(LOCAL_CONFIG_FILENAME)
bind = "%s:%s" % (conf['api']['interface'], conf['api']['port'])
workers = multiprocessing.cpu_count() * 2 + 1
threads = 5
timeout = 120

+ 21
- 0
jsonencoder/__init__.py View File

@ -0,0 +1,21 @@
#!/usr/bin/env python3
# -*- coding: utf-8
import json
class JSONEncoder(json.JSONEncoder):
def default(self, o):
try:
return json.JSONEncoder.default(self, o)
except Exception as e:
return str(o)
def jsonify(method, obj_name, obj_type, result, args):
""" JSONify response """
response = {}
response['method'] = method
response['obj_name'] = obj_name
response['object type'] = obj_type
response['result'] = result
response['response'] = args
response = JSONEncoder().encode(response)
return response

+ 108
- 0
logger/__init__.py View File

@ -0,0 +1,108 @@
#!/usr/bin/env python
# -*- coding: utf-8
import logging
colors = {'debug': '\033[97m',
'info': '\033[92m',
'warn': '\033[93m',
'error': '\033[95m',
'critical': '\033[91m'}
class Logger(object):
base_format = '%(levelname)-9s\033[0m - - [%(asctime)s] %(message)s'
debug_format = '%s%s' % (colors['debug'], base_format)
info_format = '%s%s' % (colors['info'], base_format)
warn_format = '%s%s' % (colors['warn'], base_format)
error_format = '%s%s' % (colors['error'], base_format)
critical_format = '%s%s' % (colors['critical'], base_format)
debug_logger = logging.getLogger('debug_logger')
debug_logger.setLevel(logging.DEBUG)
debug_handler = logging.StreamHandler()
debug_handler.setLevel(logging.DEBUG)
debug_formatter = logging.Formatter(debug_format, datefmt='%d/%b/%Y %H:%M:%S')
debug_handler.setFormatter(debug_formatter)
debug_logger.addHandler(debug_handler)
info_logger = logging.getLogger('info_logger')
info_logger.setLevel(logging.INFO)
info_handler = logging.StreamHandler()
info_handler.setLevel(logging.INFO)
info_formatter = logging.Formatter(info_format, datefmt='%d/%b/%Y %H:%M:%S')
info_handler.setFormatter(info_formatter)
info_logger.addHandler(info_handler)
warn_logger = logging.getLogger('warn_logger')
warn_logger.setLevel(logging.WARN)
warn_handler = logging.StreamHandler()
warn_handler.setLevel(logging.WARN)
warn_formatter = logging.Formatter(warn_format, datefmt='%d/%b/%Y %H:%M:%S')
warn_handler.setFormatter(warn_formatter)
warn_logger.addHandler(warn_handler)
error_logger = logging.getLogger('error_logger')
error_logger.setLevel(logging.ERROR)
error_handler = logging.StreamHandler()
error_handler.setLevel(logging.ERROR)
error_formatter = logging.Formatter(error_format, datefmt='%d/%b/%Y %H:%M:%S')
error_handler.setFormatter(error_formatter)
error_logger.addHandler(error_handler)
critical_logger = logging.getLogger('critical_logger')
critical_logger.setLevel(logging.CRITICAL)
critical_handler = logging.StreamHandler()
critical_handler.setLevel(logging.CRITICAL)
critical_formatter = logging.Formatter(critical_format, datefmt='%d/%b/%Y %H:%M:%S')
critical_handler.setFormatter(critical_formatter)
critical_logger.addHandler(critical_handler)
def __init__(self, level='debug'):
levels = {'debug': self.enable_debug, 'info': self.enable_info, 'warn': self.enable_warn, 'error': self.enable_error, 'critical': self.enable_critical}
levels[level]()
def enable_debug(self):
self.en_debug = True
self.en_info = True
self.en_warn = True
self.en_error = True
self.en_critical = True
def enable_info(self):
self.en_debug = False
self.en_info = True
self.en_warn = True
self.en_error = True
self.en_critical = True
def enable_warn(self):
self.en_debug = False
self.en_info = False
self.en_warn = True
self.en_error = True
self.en_critical = True
def enable_error(self):
self.en_debug = False
self.en_info = False
self.en_warn = False
self.en_error = True
self.en_critical = True
def enable_critical(self):
self.en_debug = False
self.en_info = False
self.en_warn = False
self.en_error = False
self.en_critical = True
def debug(self, msg):
if self.en_debug:
self.debug_logger.debug(msg)
def info(self, msg):
if self.en_info:
self.info_logger.info(msg)
def warn(self, msg):
if self.en_warn:
self.warn_logger.warn(msg)
def error(self, msg):
if self.en_error:
self.error_logger.error(msg)
def critical(self, msg):
if self.en_critical:
self.critical_logger.critical(msg)

+ 73
- 0
protestometer_demo.py View File

@ -0,0 +1,73 @@
#!/usr/bin/env python3
# -*- coding: utf-8
import os
import configparser
from logger import Logger
from dbclient import ReallyThreadedConnectionPool
from demo.Protester import Protesters
from demo.Protest import Protests
from demo.Subscription import Subscriptions
if __name__ == '__main__':
LOCAL_CONFIG_FILENAME = 'config.local.ini'
if not os.path.exists(LOCAL_CONFIG_FILENAME):
print("\033[91mError: Config file not found: %s\033[0m" % LOCAL_CONFIG_FILENAME)
exit(1)
config = configparser.ConfigParser()
config.read(LOCAL_CONFIG_FILENAME)
llogger = Logger(config['logging']['level'])
try:
tz = config['global']['timezone']
db_host = config['database']['host']
db_port = config['database']['port']
db_name = config['database']['name']
db_user = config['database']['user']
db_password = config['database']['password']
db_min_pool_size = config['database']['min_pool_size']
db_max_pool_size = config['database']['max_pool_size']
api_scheme = config['api']['scheme']
api_host = config['api']['host']
api_port = config['api']['port']
api_base_url = '%s://%s:%s' % (api_scheme, api_host, api_port)
connection_pool = ReallyThreadedConnectionPool(llogger,
tz,
int(db_min_pool_size),
int(db_max_pool_size),
database=db_name,
password=db_password,
host=db_host,
user=db_user)
protesters = Protesters(api_base_url, connection_pool, llogger)
protests = Protests(api_base_url, connection_pool, llogger)
subscriptions = Subscriptions(api_base_url, connection_pool, llogger)
num = 0
while num < 5000:
llogger.info("[+] protesters.add_protester() [%d]" % num)
protesters.add_protester()
#~ llogger.info("[+] protests.add_protest() [%d]" % num)
#~ protests.add_protest()
llogger.info("[+] protesters.get_protester() [%d]" % num)
protesters.get_protester()
llogger.info("[+] protesters.delete_protester() [%d]" % num)
protesters.delete_protester()
llogger.info("[+] protesters.patch_protester() [%d]" % num)
protesters.patch_protester()
llogger.info("[+] protests.get_protest() [%d]" % num)
protests.get_protest()
llogger.info("[+] protests.get_protests() [%d]" % num)
protests.get_protests()
llogger.info("[+] protests.patch_protest() [%d]" % num)
protests.patch_protest()
llogger.info("[+] protests.delete_protest() [%d]" % num)
protests.delete_protest()
llogger.info("[+] subscriptions.add_subscription() [%d]" % num)
subscriptions.add_subscription()
llogger.info("[+] subscriptions.get_subscriptions() [%d]" % num)
subscriptions.get_subscriptions()
llogger.info("[+] subscriptions.delete_subscription() [%d]" % num)
subscriptions.delete_subscription()
num += 1
except Exception as e:
llogger.critical("Error: %s" % str((e.__class__.__name__, e)))
exit(1)

+ 0
- 0
ressources/__init__.py View File


+ 95
- 0
ressources/communes.py View File

@ -0,0 +1,95 @@
#!/usr/bin/env python3
# -*- coding: utf-8
import falcon
import inspect
import psycopg2
from jsonencoder import JSONEncoder
class Collection(object):
def __init__(self, connection_pool, logger):
self.connection_pool = connection_pool
self.logger = logger
def on_get(self, req, resp):
resp_args = {}
try:
if 'dgid' in req.params:
dgid = req.get_param_as_int('dgid', required=False)
sql = "select gid, rgid, dgid, insee, nom, lon, lat from communes where dgid=%s;"
connection, cursor = self.connection_pool.get_db_connection()
cursor.execute(sql, (dgid,))
elif 'rgid' in req.params:
rgid = req.get_param_as_int('rgid', required=False)
sql = "select gid, rgid, dgid, insee, nom, lon, lat from communes where rgid=%s;"
connection, cursor = self.connection_pool.get_db_connection()
cursor.execute(sql, (rgid, ))
else:
sql = "select gid, rgid, dgid, insee, nom, lon, lat from communes;"
connection, cursor = self.connection_pool.get_db_connection()
cursor.execute(sql)
communes = cursor.fetchall()
self.connection_pool.release_db_connection(connection, cursor)
for commune in communes:
gid = commune[0]
r_dict = {'rgid': commune[1],
'dgid': commune[2],
'code_insee': commune[3],
'nom': commune[4],
'lon': commune[5],
'lat': commune[6],
}
resp_args[gid] = r_dict
resp.body = JSONEncoder.jsonify('GET', 'communes', 'collection', 'success', resp_args)
resp.status = falcon.HTTP_200
except psycopg2.Error as e:
connection.rollback()
self.connection_pool.release_db_connection(connection, cursor)
self.logger.error('[%s %s()]: %s "%s"' % (self.__class__, inspect.currentframe().f_code.co_name, e.__class__, e))
resp.body = JSONEncoder.jsonify('GET', 'communes', 'collection', 'error', 'Database error')
resp.status = falcon.HTTP_500
except Exception as e:
if 'connection' in locals() and connection.closed != 1:
self.connection_pool.release_db_connection(connection, cursor)
self.logger.warn('[%s %s()]: %s "%s"' % (self.__class__, inspect.currentframe().f_code.co_name, e.__class__, e))
resp.body = JSONEncoder.jsonify('GET', 'communes', 'collection', 'error', str(e))
resp.status = falcon.HTTP_400
class Item(object):
def __init__(self, connection_pool, logger):
self.connection_pool = connection_pool
self.logger = logger
def on_get(self, req, resp, cgid):
resp_args = {}
try:
cgid = int(cgid)
sql = "select gid, rgid, dgid, insee, nom, lon, lat from communes where gid=%s;"
connection, cursor = self.connection_pool.get_db_connection()
cursor.execute(sql, (cgid,))
commune = cursor.fetchone()
self.connection_pool.release_db_connection(connection, cursor)
if commune:
resp_args = {'cgid': commune[0],
'rgid': commune[1],
'dgid': commune[2],
'code_insee': commune[3],
'nom': commune[4],
'lon': commune[5],
'lat': commune[6]}
resp.body = JSONEncoder.jsonify('GET', 'communes', 'item', 'success', resp_args)
resp.status = falcon.HTTP_200
return
raise Exception('No such commune')
except psycopg2.Error as e:
connection.rollback()
self.connection_pool.release_db_connection(connection, cursor)
self.logger.error('[%s %s()]: %s "%s"' % (self.__class__, inspect.currentframe().f_code.co_name, e.__class__, e))
resp.body = JSONEncoder.jsonify('GET', 'communes', 'item', 'error', 'Database error')
resp.status = falcon.HTTP_500
except Exception as e:
if 'connection' in locals() and connection.closed != 1:
self.connection_pool.release_db_connection(connection, cursor)
self.logger.warn('[%s %s()]: %s "%s"' % (self.__class__, inspect.currentframe().f_code.co_name, e.__class__, e))
resp.body = JSONEncoder.jsonify('GET', 'communes', 'item', 'error', str(e))
resp.status = falcon.HTTP_400

+ 246
- 0
ressources/counters.py View File

@ -0,0 +1,246 @@
#!/usr/bin/env python3
# -*- coding: utf-8
import falcon
import inspect
import psycopg2
from jsonencoder import JSONEncoder
class Counter(object):
def __init__(self, connection_pool, logger):
self.connection_pool = connection_pool
self.logger = logger
def create_protest(self, protest_id):
try:
reqs = []
connection, cursor = self.connection_pool.get_db_connection()
reqs.append(('insert into regions_counters (rgid, protest_id, cpt) select gid, %s, %s from regions;', (protest_id, 0)))
reqs.append(('insert into departements_counters (dgid, protest_id, cpt) select gid, %s, %s from departements;', (protest_id, 0)))
reqs.append(('insert into communes_counters (cgid, protest_id, cpt) select gid, %s, %s from communes;', (protest_id, 0)))
for req in reqs:
cursor.execute(req[0], req[1])
connection.commit()
self.connection_pool.release_db_connection(connection, cursor)
return True
except psycopg2.Error as e:
connection.rollback()
self.connection_pool.release_db_connection(connection, cursor)
self.logger.error('[%s %s()]: %s "%s"' % (self.__class__, inspect.currentframe().f_code.co_name, e.__class__, e))
return False
except Exception as e:
if 'connection' in locals():
self.connection_pool.release_db_connection(connection, cursor)
self.logger.error('[%s %s()]: %s "%s"' % (self.__class__, inspect.currentframe().f_code.co_name, e.__class__, e))
return False
def drop_protest(self, protest_id):
try:
reqs = []
connection, cursor = self.connection_pool.get_db_connection()
reqs.append(('delete from regions_counters where protest_id=%s;', (protest_id, )))
reqs.append(('delete from departements_counters where protest_id=%s;', (protest_id,)))
reqs.append(('delete from communes_counters where protest_id=%s;', (protest_id,)))
for req in reqs:
cursor.execute(req[0], req[1])
connection.commit()
self.connection_pool.release_db_connection(connection, cursor)
return True
except psycopg2.Error as e:
connection.rollback()
self.connection_pool.release_db_connection(connection, cursor)
self.logger.error('[%s %s()]: %s "%s"' % (self.__class__, inspect.currentframe().f_code.co_name, e.__class__, e))
return False
except Exception as e:
if 'connection' in locals():
self.connection_pool.release_db_connection(connection, cursor)
self.logger.error('[%s %s()]: %s "%s"' % (self.__class__, inspect.currentframe().f_code.co_name, e.__class__, e))
return False
def increment_gids(self, rgid, dgid, cgid, protest_id):
try:
# Don't bother with None value
if rgid and dgid and cgid:
reqs = []
connection, cursor = self.connection_pool.get_db_connection()
reqs.append(("update regions_counters set cpt=cpt+1 where rgid=%s and protest_id=%s returning cpt;", (rgid, protest_id)))
reqs.append(("update departements_counters set cpt=cpt+1 where dgid=%s and protest_id=%s returning cpt;", (dgid, protest_id)))
reqs.append(("update communes_counters set cpt=cpt+1 where cgid=%s and protest_id=%s returning cpt;", (cgid, protest_id)))
for req in reqs:
cursor.execute(req[0], req[1])
connection.commit()
self.connection_pool.release_db_connection(connection, cursor)
return True
except psycopg2.Error as e:
connection.rollback()
self.connection_pool.release_db_connection(connection, cursor)
self.logger.error('[%s %s()]: %s "%s"' % (self.__class__, inspect.currentframe().f_code.co_name, e.__class__, e))
return False
except Exception as e:
if 'connection' in locals():
self.connection_pool.release_db_connection(connection, cursor)
self.logger.error('[%s %s()]: %s "%s"' % (self.__class__, inspect.currentframe().f_code.co_name, e.__class__, e))
return False
def decrement_gids(self, rgid, dgid, cgid, protest_id):
try:
# Don't bother with None value
if rgid and dgid and cgid:
reqs = []
connection, cursor = self.connection_pool.get_db_connection()
reqs.append(("update regions_counters set cpt=cpt-1 where rgid=%s and protest_id=%s returning cpt;", (rgid, protest_id)))
reqs.append(("update departements_counters set cpt=cpt-1 where dgid=%s and protest_id=%s returning cpt;", (dgid, protest_id)))
reqs.append(("update communes_counters set cpt=cpt-1 where cgid=%s and protest_id=%s returning cpt;", (cgid, protest_id)))
for req in reqs:
cursor.execute(req[0], req[1])
connection.commit()
self.connection_pool.release_db_connection(connection, cursor)
return True
except psycopg2.Error as e:
connection.rollback()
self.connection_pool.release_db_connection(connection, cursor)
self.logger.error('[%s %s()]: %s "%s"' % (self.__class__, inspect.currentframe().f_code.co_name, e.__class__, e))
return False
except Exception as e:
if 'connection' in locals():
self.connection_pool.release_db_connection(connection, cursor)
self.logger.error('[%s %s()]: %s "%s"' % (self.__class__, inspect.currentframe().f_code.co_name, e.__class__, e))
return False
class Collection(object):
def __init__(self, connection_pool, logger):
self.connection_pool = connection_pool
self.logger = logger
def on_get(self, req, resp, counter_type, protest_id, rgid, dgid, cgid):
try:
protest_id = int(protest_id)
rgid = int(rgid)
dgid = int(dgid)
cgid = int(cgid)
counter_types = {'regions': self.regions_count,
'departements': self.departements_count,
'communes': self.communes_count,
}
counter_types[counter_type](req, resp, protest_id, rgid, dgid, cgid)
except Exception as e:
self.logger.warn('[%s %s()]: %s "%s"' % (self.__class__, inspect.currentframe().f_code.co_name, e.__class__, e))
resp.body = JSONEncoder.jsonify('GET', 'counters