From 95b336084603cfb195939950d3a75a84d300a532 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Stefan=20K=C3=B6gl?= Date: Sun, 26 Oct 2014 18:32:55 +0100 Subject: [PATCH] [Migration] remove remaining CouchDB related code --- .travis.yml | 1 - INSTALL.md | 102 +-------- couchdb/general/_design/generic/filters/is_type.js | 9 - .../general/_design/generic/filters/is_types.js | 9 - .../general/_design/generic/filters/notdeleted.js | 4 - .../_design/episode_actions/views/by_device/map.js | 41 ---- .../episode_actions/views/by_podcast/map.js | 41 ---- .../episode_actions/views/by_podcast_device/map.js | 42 ---- .../_design/episode_actions/views/by_user/map.js | 40 ---- .../episode_states/filters/episode_states.js | 4 - .../episode_states/filters/has_play_events.js | 24 -- .../episode_states/views/by_podcast_episode/map.js | 7 - .../episode_states/views/by_ref_urls/map.js | 7 - .../episode_states/views/by_user_episode/map.js | 7 - .../episode_states/views/by_user_podcast/map.js | 41 ---- .../_design/history/views/by_device/map.js | 69 ------ .../userdata/_design/history/views/by_user/map.js | 64 ------ .../_design/listeners/views/by_episode/map.js | 16 -- .../_design/listeners/views/by_episode/reduce.js | 1 - .../_design/listeners/views/by_podcast/map.js | 16 -- .../_design/listeners/views/by_podcast/reduce.js | 1 - .../listeners/views/by_podcast_episode/map.js | 16 -- .../listeners/views/by_podcast_episode/reduce.js | 1 - .../_design/listeners/views/by_user/map.js | 16 -- .../_design/listeners/views/by_user/reduce.js | 1 - .../_design/listeners/views/by_user_podcast/map.js | 15 -- .../listeners/views/by_user_podcast/reduce.js | 1 - .../listeners/views/times_played_by_user/map.js | 19 -- .../listeners/views/times_played_by_user/reduce.js | 1 - .../_design/podcast_states/validate_doc_update.js | 53 ----- .../_design/podcast_states/views/by_device/map.js | 24 -- .../_design/podcast_states/views/by_podcast/map.js | 7 - .../_design/podcast_states/views/by_user/map.js | 7 - .../_design/subscribers/views/by_podcast/map.js | 27 --- .../_design/subscribers/views/by_podcast/reduce.js | 1 - .../_design/subscriptions/views/by_device/map.js | 34 --- .../_design/subscriptions/views/by_podcast/map.js | 34 --- .../subscriptions/views/by_podcast/reduce.js | 1 - .../_design/subscriptions/views/by_user/map.js | 44 ---- .../_design/subscriptions/views/by_user/reduce.js | 1 - .../_design/usertags/views/by_podcast/map.js | 10 - .../_design/usertags/views/by_podcast/reduce.js | 1 - .../userdata/_design/usertags/views/by_user/map.js | 10 - .../_design/usertags/views/podcasts/map.js | 11 - .../_design/usertags/views/podcasts/reduce.js | 1 - .../_design/clients/views/by_ua_string/map.js | 14 -- .../_design/clients/views/by_ua_string/reduce.js | 1 - couchdb/users/_design/users/validate_doc_update.js | 59 ----- .../_design/users/views/by_google_email/map.js | 10 - couchdb/users/_design/users/views/deleted/map.js | 10 - .../users/_design/users/views/deleted/reduce.js | 1 - doc/dev/couchdb-docs.rst | 246 --------------------- doc/dev/couchdb-views.rst | 151 ------------- doc/dev/index.rst | 2 - doc/dev/python3.rst | 2 - mygpo/administration/tests.py | 2 - mygpo/api/advanced/__init__.py | 3 +- mygpo/api/advanced/settings.py | 7 +- mygpo/api/simple.py | 2 - mygpo/api/tests.py | 11 +- mygpo/core/tasks.py | 3 - mygpo/db/__init__.py | 8 - mygpo/db/couchdb/__init__.py | 109 --------- mygpo/db/couchdb/episode_state.py | 156 ------------- mygpo/db/couchdb/models.py | 0 mygpo/db/couchdb/utils.py | 41 ---- mygpo/decorators.py | 66 ------ mygpo/settings.py | 19 -- mygpo/users/models.py | 125 ----------- mygpo/users/ratings.py | 32 --- mygpo/users/settings.py | 14 -- mygpo/users/tests.py | 6 - mygpo/web/templatetags/podcasts.py | 3 +- mygpo/web/utils.py | 28 +-- mygpo/web/views/episode.py | 3 +- requirements.txt | 2 - res/couchdb-docs/replicate-categories.json | 14 -- 77 files changed, 16 insertions(+), 2016 deletions(-) delete mode 100644 couchdb/general/_design/generic/filters/is_type.js delete mode 100644 couchdb/general/_design/generic/filters/is_types.js delete mode 100644 couchdb/general/_design/generic/filters/notdeleted.js delete mode 100644 couchdb/userdata/_design/episode_actions/views/by_device/map.js delete mode 100644 couchdb/userdata/_design/episode_actions/views/by_podcast/map.js delete mode 100644 couchdb/userdata/_design/episode_actions/views/by_podcast_device/map.js delete mode 100644 couchdb/userdata/_design/episode_actions/views/by_user/map.js delete mode 100644 couchdb/userdata/_design/episode_states/filters/episode_states.js delete mode 100644 couchdb/userdata/_design/episode_states/filters/has_play_events.js delete mode 100644 couchdb/userdata/_design/episode_states/views/by_podcast_episode/map.js delete mode 100644 couchdb/userdata/_design/episode_states/views/by_ref_urls/map.js delete mode 100644 couchdb/userdata/_design/episode_states/views/by_user_episode/map.js delete mode 100644 couchdb/userdata/_design/episode_states/views/by_user_podcast/map.js delete mode 100644 couchdb/userdata/_design/history/views/by_device/map.js delete mode 100644 couchdb/userdata/_design/history/views/by_user/map.js delete mode 100644 couchdb/userdata/_design/listeners/views/by_episode/map.js delete mode 100644 couchdb/userdata/_design/listeners/views/by_episode/reduce.js delete mode 100644 couchdb/userdata/_design/listeners/views/by_podcast/map.js delete mode 100644 couchdb/userdata/_design/listeners/views/by_podcast/reduce.js delete mode 100644 couchdb/userdata/_design/listeners/views/by_podcast_episode/map.js delete mode 100644 couchdb/userdata/_design/listeners/views/by_podcast_episode/reduce.js delete mode 100644 couchdb/userdata/_design/listeners/views/by_user/map.js delete mode 100644 couchdb/userdata/_design/listeners/views/by_user/reduce.js delete mode 100644 couchdb/userdata/_design/listeners/views/by_user_podcast/map.js delete mode 100644 couchdb/userdata/_design/listeners/views/by_user_podcast/reduce.js delete mode 100644 couchdb/userdata/_design/listeners/views/times_played_by_user/map.js delete mode 100644 couchdb/userdata/_design/listeners/views/times_played_by_user/reduce.js delete mode 100644 couchdb/userdata/_design/podcast_states/validate_doc_update.js delete mode 100644 couchdb/userdata/_design/podcast_states/views/by_device/map.js delete mode 100644 couchdb/userdata/_design/podcast_states/views/by_podcast/map.js delete mode 100644 couchdb/userdata/_design/podcast_states/views/by_user/map.js delete mode 100644 couchdb/userdata/_design/subscribers/views/by_podcast/map.js delete mode 100644 couchdb/userdata/_design/subscribers/views/by_podcast/reduce.js delete mode 100644 couchdb/userdata/_design/subscriptions/views/by_device/map.js delete mode 100644 couchdb/userdata/_design/subscriptions/views/by_podcast/map.js delete mode 100644 couchdb/userdata/_design/subscriptions/views/by_podcast/reduce.js delete mode 100644 couchdb/userdata/_design/subscriptions/views/by_user/map.js delete mode 100644 couchdb/userdata/_design/subscriptions/views/by_user/reduce.js delete mode 100644 couchdb/userdata/_design/usertags/views/by_podcast/map.js delete mode 100644 couchdb/userdata/_design/usertags/views/by_podcast/reduce.js delete mode 100644 couchdb/userdata/_design/usertags/views/by_user/map.js delete mode 100644 couchdb/userdata/_design/usertags/views/podcasts/map.js delete mode 100644 couchdb/userdata/_design/usertags/views/podcasts/reduce.js delete mode 100644 couchdb/users/_design/clients/views/by_ua_string/map.js delete mode 100644 couchdb/users/_design/clients/views/by_ua_string/reduce.js delete mode 100644 couchdb/users/_design/users/validate_doc_update.js delete mode 100644 couchdb/users/_design/users/views/by_google_email/map.js delete mode 100644 couchdb/users/_design/users/views/deleted/map.js delete mode 100644 couchdb/users/_design/users/views/deleted/reduce.js delete mode 100644 doc/dev/couchdb-docs.rst delete mode 100644 doc/dev/couchdb-views.rst delete mode 100644 mygpo/db/__init__.py delete mode 100644 mygpo/db/couchdb/__init__.py delete mode 100644 mygpo/db/couchdb/episode_state.py delete mode 100644 mygpo/db/couchdb/models.py delete mode 100644 mygpo/db/couchdb/utils.py delete mode 100644 mygpo/users/ratings.py delete mode 100644 res/couchdb-docs/replicate-categories.json diff --git a/.travis.yml b/.travis.yml index 83700df0..e18b8757 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,6 @@ python: - "2.7" services: - - couchdb - redis-server - elasticsearch diff --git a/INSTALL.md b/INSTALL.md index 134f2c96..f79d64e8 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -9,7 +9,7 @@ When no version number is indicated, it is advisable to install the current development version from the repository. * Python (>= 2.6) or PyPy (tested with 2.0 beta1) -* [CouchDB](https://couchdb.apache.org/) (>= 1.2) +* PostgreSQL Basic setup @@ -21,9 +21,6 @@ Ubuntu 12.04 x86 install, 'should work' with other versions/archs as well). If you are on a Debian/Ubuntu system, do: sudo apt-get install erlang git python-pip python-dev libevent-dev - sudo apt-get build-dep couchdb - - [ install couchdb 1.2.0 from source ] For creating logo thumbnails, install libraries for the various image formats. They are used by the pillow library. @@ -43,8 +40,7 @@ some other fancy stuff): That's it for the setup. Now to initialize the DB: cd mygpo - python manage.py sync_couchdb - python manage.py sync-design-docs + python manage.py migrate ..and here we go: @@ -78,23 +74,6 @@ that your machine is connected to. Apply common sense and ideally use only on trusted networks. -Installing database dumps (if you have any, that is) ----------------------------------------------------- - -As easy as.. - - cd lib - hg clone https://code.google.com/p/couchdb-python/ - cd couchdb-python - zcat /path/to/your.dump.gz | python couchdb/tools/load.py \ - http://localhost:5984/mygpo - -..when you have created a dump like this: - - python manage.py dump-sample --user=[username] | gzip > my.dump.gz - -(where [username] is a specific user for which the data will be dumped) - Updating derived data --------------------- @@ -129,83 +108,6 @@ To set a user as publisher for a given feed URL, use: python manage.py make-publisher [...] -Resetting the database ("start from scratch") ---------------------------------------------- - -Delete the database: - - curl -X DELETE http://127.0.0.1:5984/mygpo - curl -X DELETE http://127.0.0.1:5984/mygpo_sessions - -Recreate the design documents: - - cd mygpo - python manage.py sync_couchdb - python manage.py sync-design-docs - - -Additional details on couchDB ------------------------------ - -A source distribution of CouchDB can be obtained from - - http://couchdb.apache.org/downloads.html - -Build and installation instructions can be found at - - http://wiki.apache.org/couchdb/Installation - -Here are some rough instructions on how to build CouchDB 1.2.0 from source -and setting it up with a local user account: - - (assuming you have downloaded and extracted the CouchDB 1.2.0 source) - sudo apt-get build-dep couchdb - export COUCHDB_HOME=~/pkg/apache-couchdb-1.2.0/ - ./configure --prefix=$COUCHDB_HOME - make -j4 && make install - sudo useradd -d $COUCHDB_HOME/var/lib/couchdb couchdb - sudo chown -R couchdb: $COUCHDB_HOME - sudo chown -R root:couchdb $COUCHDB_HOME/etc/couchdb - sudo chmod 664 $COUCHDB_HOME/etc/couchdb/*.ini - sudo chmod 775 $COUCHDB_HOME/etc/couchdb/*.d - - -If you want to avoid installing a CouchDB server yourself, you can use a free -CouchDB hosting service, for example from - - http://www.iriscouch.com/service - -Please note, however, that hosted CouchDB services generally do not provide -security or authentication mechanisms, so this might only be useful for -development servers. - -If you don't use a local database, you need to update the COUCHDB_DATABASES -setting (see the "Settings" section below). - - - -Initializing an empty Database ------------------------------- - -To create the database, execute the following on the commandline - - curl -X PUT http://127.0.0.1:5984/mygpo - -To initialize the views, execute from the mygpo directory - - python manage.py sync_couchdb - - -Importing a Dump ----------------- - -To import a CouchDB dump, execute the following from the commandline - - gunzip -c | couchdb-load http://127.0.0.1:5984/mygpo - - - - Settings -------- diff --git a/couchdb/general/_design/generic/filters/is_type.js b/couchdb/general/_design/generic/filters/is_type.js deleted file mode 100644 index ca65bced..00000000 --- a/couchdb/general/_design/generic/filters/is_type.js +++ /dev/null @@ -1,9 +0,0 @@ -function(doc, req) -{ - if(doc._deleted) - { - return false; - } - - return (doc.doc_type && doc.doc_type == req.query.doc_type); -} diff --git a/couchdb/general/_design/generic/filters/is_types.js b/couchdb/general/_design/generic/filters/is_types.js deleted file mode 100644 index 1c95eceb..00000000 --- a/couchdb/general/_design/generic/filters/is_types.js +++ /dev/null @@ -1,9 +0,0 @@ -function(doc, req) -{ - if(doc._deleted) - { - return false; - } - - return (doc.doc_type && (req.query.doc_types.indexOf(doc.doc_type) > -1)); -} diff --git a/couchdb/general/_design/generic/filters/notdeleted.js b/couchdb/general/_design/generic/filters/notdeleted.js deleted file mode 100644 index 1bde7011..00000000 --- a/couchdb/general/_design/generic/filters/notdeleted.js +++ /dev/null @@ -1,4 +0,0 @@ -function(doc, req) -{ - return !doc._deleted; -} diff --git a/couchdb/userdata/_design/episode_actions/views/by_device/map.js b/couchdb/userdata/_design/episode_actions/views/by_device/map.js deleted file mode 100644 index 5907248c..00000000 --- a/couchdb/userdata/_design/episode_actions/views/by_device/map.js +++ /dev/null @@ -1,41 +0,0 @@ -function(doc) -{ - if(doc.doc_type == "EpisodeUserState") - { - for(var n in doc.actions) - { - var action = doc.actions[n]; - - var action_obj = { - podcast: doc.podcast_ref_url, - episode: doc.ref_url, - action: action.action, - timestamp: action.timestamp.slice(0, action.timestamp.length-1), - } - - if(action.device != null) - { - action_obj["device_id"] = action.device; - } - if(action.started != null) - { - action_obj["started"] = action.started; - } - if(action.playmark != null) - { - action_obj["position"] = action.playmark; - } - if(action.total != null) - { - action_obj["total"] = action.total; - } - - emit([ - doc.user, - action.device, - action.upload_timestamp, - ], action_obj - ); - } - } -} diff --git a/couchdb/userdata/_design/episode_actions/views/by_podcast/map.js b/couchdb/userdata/_design/episode_actions/views/by_podcast/map.js deleted file mode 100644 index 65c12a17..00000000 --- a/couchdb/userdata/_design/episode_actions/views/by_podcast/map.js +++ /dev/null @@ -1,41 +0,0 @@ -function(doc) -{ - if(doc.doc_type == "EpisodeUserState") - { - for(var n in doc.actions) - { - var action = doc.actions[n]; - - var action_obj = { - podcast: doc.podcast_ref_url, - episode: doc.ref_url, - action: action.action, - timestamp: action.timestamp.slice(0, action.timestamp.length-1), - } - - if(action.device != null) - { - action_obj["device_id"] = action.device; - } - if(action.started != null) - { - action_obj["started"] = action.started; - } - if(action.playmark != null) - { - action_obj["position"] = action.playmark; - } - if(action.total != null) - { - action_obj["total"] = action.total; - } - - emit([ - doc.user, - doc.podcast, - action.upload_timestamp - ], action_obj - ); - } - } -} diff --git a/couchdb/userdata/_design/episode_actions/views/by_podcast_device/map.js b/couchdb/userdata/_design/episode_actions/views/by_podcast_device/map.js deleted file mode 100644 index bb68009c..00000000 --- a/couchdb/userdata/_design/episode_actions/views/by_podcast_device/map.js +++ /dev/null @@ -1,42 +0,0 @@ -function(doc) -{ - if(doc.doc_type == "EpisodeUserState") - { - for(var n in doc.actions) - { - var action = doc.actions[n]; - - var action_obj = { - podcast: doc.podcast_ref_url, - episode: doc.ref_url, - action: action.action, - timestamp: action.timestamp.slice(0, action.timestamp.length-1), - } - - if(action.device != null) - { - action_obj["device_id"] = action.device; - } - if(action.started != null) - { - action_obj["started"] = action.started; - } - if(action.playmark != null) - { - action_obj["position"] = action.playmark; - } - if(action.total != null) - { - action_obj["total"] = action.total; - } - - emit([ - doc.user, - doc.podcast, - action.device, - action.upload_timestamp, - ], action_obj - ); - } - } -} diff --git a/couchdb/userdata/_design/episode_actions/views/by_user/map.js b/couchdb/userdata/_design/episode_actions/views/by_user/map.js deleted file mode 100644 index 3ceae529..00000000 --- a/couchdb/userdata/_design/episode_actions/views/by_user/map.js +++ /dev/null @@ -1,40 +0,0 @@ -function(doc) -{ - if(doc.doc_type == "EpisodeUserState") - { - for(var n in doc.actions) - { - var action = doc.actions[n]; - - var action_obj = { - podcast: doc.podcast_ref_url, - episode: doc.ref_url, - action: action.action, - timestamp: action.timestamp.slice(0, action.timestamp.length-1), - } - - if(action.device != null) - { - action_obj["device_id"] = action.device; - } - if(action.started != null) - { - action_obj["started"] = action.started; - } - if(action.playmark != null) - { - action_obj["position"] = action.playmark; - } - if(action.total != null) - { - action_obj["total"] = action.total; - } - - emit([ - doc.user, - action.upload_timestamp - ], action_obj - ); - } - } -} diff --git a/couchdb/userdata/_design/episode_states/filters/episode_states.js b/couchdb/userdata/_design/episode_states/filters/episode_states.js deleted file mode 100644 index a04f088b..00000000 --- a/couchdb/userdata/_design/episode_states/filters/episode_states.js +++ /dev/null @@ -1,4 +0,0 @@ -function(doc, req) -{ - return (doc.doc_type == "EpisodeUserState"); -} diff --git a/couchdb/userdata/_design/episode_states/filters/has_play_events.js b/couchdb/userdata/_design/episode_states/filters/has_play_events.js deleted file mode 100644 index 991a90f6..00000000 --- a/couchdb/userdata/_design/episode_states/filters/has_play_events.js +++ /dev/null @@ -1,24 +0,0 @@ -function(doc, req) -{ - if(doc.doc_type != "EpisodeUserState") - { - return false; - } - - if(doc._deleted == true) - { - return false; - } - - if(!doc.actions) - { - return false; - } - - function isPlayEvent(action) - { - return action.action == "play"; - } - - return doc.actions.some(isPlayEvent); -} diff --git a/couchdb/userdata/_design/episode_states/views/by_podcast_episode/map.js b/couchdb/userdata/_design/episode_states/views/by_podcast_episode/map.js deleted file mode 100644 index 8b59d81a..00000000 --- a/couchdb/userdata/_design/episode_states/views/by_podcast_episode/map.js +++ /dev/null @@ -1,7 +0,0 @@ -function(doc) -{ - if(doc.doc_type == "EpisodeUserState") - { - emit([doc.podcast, doc.episode, doc.user], null); - } -} diff --git a/couchdb/userdata/_design/episode_states/views/by_ref_urls/map.js b/couchdb/userdata/_design/episode_states/views/by_ref_urls/map.js deleted file mode 100644 index 263bc3c4..00000000 --- a/couchdb/userdata/_design/episode_states/views/by_ref_urls/map.js +++ /dev/null @@ -1,7 +0,0 @@ -function(doc) -{ - if(doc.doc_type == "EpisodeUserState") - { - emit([doc.user, doc.podcast_ref_url, doc.ref_url], null); - } -} diff --git a/couchdb/userdata/_design/episode_states/views/by_user_episode/map.js b/couchdb/userdata/_design/episode_states/views/by_user_episode/map.js deleted file mode 100644 index cc1673a4..00000000 --- a/couchdb/userdata/_design/episode_states/views/by_user_episode/map.js +++ /dev/null @@ -1,7 +0,0 @@ -function(doc) -{ - if(doc.doc_type == "EpisodeUserState") - { - emit([doc.user, doc.episode], null); - } -} diff --git a/couchdb/userdata/_design/episode_states/views/by_user_podcast/map.js b/couchdb/userdata/_design/episode_states/views/by_user_podcast/map.js deleted file mode 100644 index 4d451074..00000000 --- a/couchdb/userdata/_design/episode_states/views/by_user_podcast/map.js +++ /dev/null @@ -1,41 +0,0 @@ -function(doc) -{ - if(doc.doc_type == "EpisodeUserState") - { - if(doc.actions.length < 1) - { - return; - } - - var action = doc.actions[doc.actions.length-1]; - - var action_obj = { - podcast_url: doc.podcast_ref_url, - episode_url: doc.ref_url, - podcast_id: doc.podcast, - episode_id: doc.episode, - action: action.action, - timestamp: action.timestamp.slice(0, action.timestamp.length-1), - } - - if(action.device != null) - { - action_obj["device_id"] = action.device; - } - if(action.started != null) - { - action_obj["started"] = action.started; - } - if(action.playmark != null) - { - action_obj["position"] = action.playmark; - } - if(action.total != null) - { - action_obj["total"] = action.total; - } - - - emit([doc.user, doc.podcast, doc.episode], action_obj); - } -} diff --git a/couchdb/userdata/_design/history/views/by_device/map.js b/couchdb/userdata/_design/history/views/by_device/map.js deleted file mode 100644 index d4d50712..00000000 --- a/couchdb/userdata/_design/history/views/by_device/map.js +++ /dev/null @@ -1,69 +0,0 @@ -function(doc) -{ - function processEpisodeAction(action) - { - if(action.device == null) - { - return; - } - - action_obj = { - type: "Episode", - podcast_url: doc.podcast_ref_url, - episode_url: doc.ref_url, - podcast_id: doc.podcast, - episode_id: doc.episode, - action: action.action, - timestamp: action.timestamp.slice(0, action.timestamp.length-1), - } - - if(action.device != null) - { - action_obj["device_id"] = action.device; - } - if(action.started != null) - { - action_obj["started"] = action.started; - } - if(action.playmark != null) - { - action_obj["position"] = action.playmark; - } - if(action.total != null) - { - action_obj["total"] = action.total; - } - - emit([doc.user, action.device, action_obj.timestamp], action_obj); - } - - function processSubscriptionAction(action) - { - action_obj = { - type: "Subscription", - podcast_url: doc.ref_url, - podcast_id: doc.podcast, - action: action.action, - timestamp: action.timestamp.slice(0, action.timestamp.length-1), - device_id: action.device, - } - - emit([doc.user, action.device, action_obj.timestamp], action_obj); - } - - - if(doc.doc_type == "EpisodeUserState") - { - for(var n in doc.actions) - { - processEpisodeAction(doc.actions[n]); - } - } - if(doc.doc_type == "PodcastUserState") - { - for(var n in doc.actions) - { - processSubscriptionAction(doc.actions[n]); - } - } -} diff --git a/couchdb/userdata/_design/history/views/by_user/map.js b/couchdb/userdata/_design/history/views/by_user/map.js deleted file mode 100644 index 8111b3e6..00000000 --- a/couchdb/userdata/_design/history/views/by_user/map.js +++ /dev/null @@ -1,64 +0,0 @@ -function(doc) -{ - function processEpisodeAction(action) - { - action_obj = { - type: "Episode", - podcast_url: doc.podcast_ref_url, - episode_url: doc.ref_url, - podcast_id: doc.podcast, - episode_id: doc.episode, - action: action.action, - timestamp: action.timestamp.slice(0, action.timestamp.length-1), - } - - if(action.device != null) - { - action_obj["device_id"] = action.device; - } - if(action.started != null) - { - action_obj["started"] = action.started; - } - if(action.playmark != null) - { - action_obj["position"] = action.playmark; - } - if(action.total != null) - { - action_obj["total"] = action.total; - } - - emit([doc.user, action_obj.timestamp], action_obj); - } - - function processSubscriptionAction(action) - { - action_obj = { - type: "Subscription", - podcast_url: doc.ref_url, - podcast_id: doc.podcast, - action: action.action, - timestamp: action.timestamp.slice(0, action.timestamp.length-1), - device_id: action.device, - } - - emit([doc.user, action_obj.timestamp], action_obj); - } - - - if(doc.doc_type == "EpisodeUserState") - { - for(var n in doc.actions) - { - processEpisodeAction(doc.actions[n]); - } - } - if(doc.doc_type == "PodcastUserState") - { - for(var n in doc.actions) - { - processSubscriptionAction(doc.actions[n]); - } - } -} diff --git a/couchdb/userdata/_design/listeners/views/by_episode/map.js b/couchdb/userdata/_design/listeners/views/by_episode/map.js deleted file mode 100644 index f9adc78d..00000000 --- a/couchdb/userdata/_design/listeners/views/by_episode/map.js +++ /dev/null @@ -1,16 +0,0 @@ -function(doc) -{ - if(doc.doc_type == "EpisodeUserState") - { - for(var n=doc.actions.length-1; n>=0; n--) - { - var action = doc.actions[n]; - if(action.action == "play") - { - var day = action.timestamp.slice(0, 10); - emit([doc.episode, day], doc.user); - return; - } - } - } -} diff --git a/couchdb/userdata/_design/listeners/views/by_episode/reduce.js b/couchdb/userdata/_design/listeners/views/by_episode/reduce.js deleted file mode 100644 index c866cd72..00000000 --- a/couchdb/userdata/_design/listeners/views/by_episode/reduce.js +++ /dev/null @@ -1 +0,0 @@ -_count diff --git a/couchdb/userdata/_design/listeners/views/by_podcast/map.js b/couchdb/userdata/_design/listeners/views/by_podcast/map.js deleted file mode 100644 index 2798c54d..00000000 --- a/couchdb/userdata/_design/listeners/views/by_podcast/map.js +++ /dev/null @@ -1,16 +0,0 @@ -function(doc) -{ - if(doc.doc_type == "EpisodeUserState") - { - for(var n=doc.actions.length-1; n>=0; n--) - { - var action = doc.actions[n]; - if(action.action == "play") - { - var day = action.timestamp.slice(0, 10); - emit([doc.podcast, day], doc.user); - return; - } - } - } -} diff --git a/couchdb/userdata/_design/listeners/views/by_podcast/reduce.js b/couchdb/userdata/_design/listeners/views/by_podcast/reduce.js deleted file mode 100644 index c866cd72..00000000 --- a/couchdb/userdata/_design/listeners/views/by_podcast/reduce.js +++ /dev/null @@ -1 +0,0 @@ -_count diff --git a/couchdb/userdata/_design/listeners/views/by_podcast_episode/map.js b/couchdb/userdata/_design/listeners/views/by_podcast_episode/map.js deleted file mode 100644 index 9cb1b0bc..00000000 --- a/couchdb/userdata/_design/listeners/views/by_podcast_episode/map.js +++ /dev/null @@ -1,16 +0,0 @@ -function(doc) -{ - if(doc.doc_type == "EpisodeUserState") - { - for(var n=doc.actions.length-1; n>=0; n--) - { - var action = doc.actions[n]; - if(action.action == "play") - { - var day = action.timestamp.slice(0, 10); - emit([doc.podcast, doc.episode, day], doc.user); - return; - } - } - } -} diff --git a/couchdb/userdata/_design/listeners/views/by_podcast_episode/reduce.js b/couchdb/userdata/_design/listeners/views/by_podcast_episode/reduce.js deleted file mode 100644 index c866cd72..00000000 --- a/couchdb/userdata/_design/listeners/views/by_podcast_episode/reduce.js +++ /dev/null @@ -1 +0,0 @@ -_count diff --git a/couchdb/userdata/_design/listeners/views/by_user/map.js b/couchdb/userdata/_design/listeners/views/by_user/map.js deleted file mode 100644 index 876030bf..00000000 --- a/couchdb/userdata/_design/listeners/views/by_user/map.js +++ /dev/null @@ -1,16 +0,0 @@ -function(doc) -{ - if(doc.doc_type == "EpisodeUserState") - { - for(var n=doc.actions.length-1; n>=0; n--) - { - var action = doc.actions[n]; - if(action.action == "play") - { - var day = action.timestamp.slice(0, 10); - emit([doc.user, day], doc.episode); - return; - } - } - } -} diff --git a/couchdb/userdata/_design/listeners/views/by_user/reduce.js b/couchdb/userdata/_design/listeners/views/by_user/reduce.js deleted file mode 100644 index c866cd72..00000000 --- a/couchdb/userdata/_design/listeners/views/by_user/reduce.js +++ /dev/null @@ -1 +0,0 @@ -_count diff --git a/couchdb/userdata/_design/listeners/views/by_user_podcast/map.js b/couchdb/userdata/_design/listeners/views/by_user_podcast/map.js deleted file mode 100644 index 739b3231..00000000 --- a/couchdb/userdata/_design/listeners/views/by_user_podcast/map.js +++ /dev/null @@ -1,15 +0,0 @@ -function(doc) -{ - if(doc.doc_type == "EpisodeUserState") - { - for(var n=doc.actions.length-1; n>=0; n--) - { - var action = doc.actions[n]; - if(action.action == "play") - { - emit([doc.user, doc.podcast], doc.episode); - return; - } - } - } -} diff --git a/couchdb/userdata/_design/listeners/views/by_user_podcast/reduce.js b/couchdb/userdata/_design/listeners/views/by_user_podcast/reduce.js deleted file mode 100644 index c866cd72..00000000 --- a/couchdb/userdata/_design/listeners/views/by_user_podcast/reduce.js +++ /dev/null @@ -1 +0,0 @@ -_count diff --git a/couchdb/userdata/_design/listeners/views/times_played_by_user/map.js b/couchdb/userdata/_design/listeners/views/times_played_by_user/map.js deleted file mode 100644 index 3b3d01d7..00000000 --- a/couchdb/userdata/_design/listeners/views/times_played_by_user/map.js +++ /dev/null @@ -1,19 +0,0 @@ -function(doc) -{ - if(doc.doc_type == "EpisodeUserState") - { - for(var n=0; n -1) - { - throw({forbidden: "Can not subscribe twice on device " + action.device + " in podcast state " + newDoc._id}); - } - - subscribed_devices.push(action.device); - } - else if (action.action == "unsubscribe") - { - if(index == -1) - { - throw({forbidden: "Can not unsubscribe on device " + action.device + " on which the podcast is not subscribed in podcast state " + newDoc._id}); - } - - subscribed_devices.splice(index, 1); - } - - } - } -} diff --git a/couchdb/userdata/_design/podcast_states/views/by_device/map.js b/couchdb/userdata/_design/podcast_states/views/by_device/map.js deleted file mode 100644 index 4b4cf267..00000000 --- a/couchdb/userdata/_design/podcast_states/views/by_device/map.js +++ /dev/null @@ -1,24 +0,0 @@ -function(doc) -{ - if(doc.doc_type == "PodcastUserState") - { - var affected_devices = []; - - for(var n in doc.actions) - { - var action = doc.actions[n]; - if (affected_devices.indexOf(action.device) == -1) - { - affected_devices.push(action.device); - } - } - - for(var n in affected_devices) - { - var device = affected_devices[n]; - - emit([device, doc.podcast], null); - } - } -} - diff --git a/couchdb/userdata/_design/podcast_states/views/by_podcast/map.js b/couchdb/userdata/_design/podcast_states/views/by_podcast/map.js deleted file mode 100644 index a0fbc20a..00000000 --- a/couchdb/userdata/_design/podcast_states/views/by_podcast/map.js +++ /dev/null @@ -1,7 +0,0 @@ -function(doc) -{ - if(doc.doc_type == "PodcastUserState") - { - emit([doc.podcast, doc.user], null); - } -} diff --git a/couchdb/userdata/_design/podcast_states/views/by_user/map.js b/couchdb/userdata/_design/podcast_states/views/by_user/map.js deleted file mode 100644 index 974fb35f..00000000 --- a/couchdb/userdata/_design/podcast_states/views/by_user/map.js +++ /dev/null @@ -1,7 +0,0 @@ -function(doc) -{ - if(doc.doc_type == "PodcastUserState") - { - emit([doc.user, doc.podcast], null); - } -} diff --git a/couchdb/userdata/_design/subscribers/views/by_podcast/map.js b/couchdb/userdata/_design/subscribers/views/by_podcast/map.js deleted file mode 100644 index 84e969ed..00000000 --- a/couchdb/userdata/_design/subscribers/views/by_podcast/map.js +++ /dev/null @@ -1,27 +0,0 @@ -function(doc) -{ - if(doc.doc_type == "PodcastUserState") - { - var subscribed_devices = []; - - for(var n in doc.actions) - { - var action = doc.actions[n]; - - if(action.action == "subscribe") - { - subscribed_devices.push(action.device); - } - else if(action.action == "unsubscribe") - { - var index = subscribed_devices.indexOf(action.device); - subscribed_devices.splice(index, 1); - } - } - - if (subscribed_devices.length > 0) - { - emit([doc.podcast, doc.user], null); - } - } -} diff --git a/couchdb/userdata/_design/subscribers/views/by_podcast/reduce.js b/couchdb/userdata/_design/subscribers/views/by_podcast/reduce.js deleted file mode 100644 index c866cd72..00000000 --- a/couchdb/userdata/_design/subscribers/views/by_podcast/reduce.js +++ /dev/null @@ -1 +0,0 @@ -_count diff --git a/couchdb/userdata/_design/subscriptions/views/by_device/map.js b/couchdb/userdata/_design/subscriptions/views/by_device/map.js deleted file mode 100644 index 29b4ba7c..00000000 --- a/couchdb/userdata/_design/subscriptions/views/by_device/map.js +++ /dev/null @@ -1,34 +0,0 @@ -function(doc) -{ - if(doc.doc_type == "PodcastUserState") - { - var subscribed_devices = []; - - for(var n in doc.actions) - { - var action = doc.actions[n]; - - if(action.action == "subscribe") - { - subscribed_devices.push(action.device); - } - else if(action.action == "unsubscribe") - { - var index = subscribed_devices.indexOf(action.device); - subscribed_devices.splice(index, 1); - } - } - - for(var n in subscribed_devices) - { - var device = subscribed_devices[n]; - - if(doc.disabled_devices && (doc.disabled_devices.indexOf(device) > -1)) - { - continue; - } - - emit([device, doc.podcast], null); - } - } -} diff --git a/couchdb/userdata/_design/subscriptions/views/by_podcast/map.js b/couchdb/userdata/_design/subscriptions/views/by_podcast/map.js deleted file mode 100644 index 79f086c6..00000000 --- a/couchdb/userdata/_design/subscriptions/views/by_podcast/map.js +++ /dev/null @@ -1,34 +0,0 @@ -function(doc) -{ - if(doc.doc_type == "PodcastUserState") - { - var subscribed_devices = []; - - for(var n in doc.actions) - { - var action = doc.actions[n]; - - if(action.action == "subscribe") - { - subscribed_devices.push(action.device); - } - else if(action.action == "unsubscribe") - { - var index = subscribed_devices.indexOf(action.device); - subscribed_devices.splice(index, 1); - } - } - - for(var n in subscribed_devices) - { - var device = subscribed_devices[n]; - - if(doc.disabled_devices && (doc.disabled_devices.indexOf(device) > -1)) - { - continue; - } - - emit([doc.podcast, doc.user, device], null); - } - } -} diff --git a/couchdb/userdata/_design/subscriptions/views/by_podcast/reduce.js b/couchdb/userdata/_design/subscriptions/views/by_podcast/reduce.js deleted file mode 100644 index c866cd72..00000000 --- a/couchdb/userdata/_design/subscriptions/views/by_podcast/reduce.js +++ /dev/null @@ -1 +0,0 @@ -_count diff --git a/couchdb/userdata/_design/subscriptions/views/by_user/map.js b/couchdb/userdata/_design/subscriptions/views/by_user/map.js deleted file mode 100644 index d7bfdf87..00000000 --- a/couchdb/userdata/_design/subscriptions/views/by_user/map.js +++ /dev/null @@ -1,44 +0,0 @@ -function(doc) -{ - if(doc.doc_type == "PodcastUserState") - { - var subscribed_devices = []; - - for(var n in doc.actions) - { - var action = doc.actions[n]; - - if(action.action == "subscribe") - { - subscribed_devices.push(action.device); - } - else if(action.action == "unsubscribe") - { - var index = subscribed_devices.indexOf(action.device); - subscribed_devices.splice(index, 1); - } - } - - for(var n in subscribed_devices) - { - var device = subscribed_devices[n]; - - if(doc.disabled_devices && (doc.disabled_devices.indexOf(device) > -1)) - { - continue; - } - - - if(doc.settings == null || doc.settings.public_subscription == null) - { - var is_public = true; - } - else - { - var is_public = doc.settings.public_subscription; - } - - emit([doc.user, is_public, doc.podcast, device], null); - } - } -} diff --git a/couchdb/userdata/_design/subscriptions/views/by_user/reduce.js b/couchdb/userdata/_design/subscriptions/views/by_user/reduce.js deleted file mode 100644 index c866cd72..00000000 --- a/couchdb/userdata/_design/subscriptions/views/by_user/reduce.js +++ /dev/null @@ -1 +0,0 @@ -_count diff --git a/couchdb/userdata/_design/usertags/views/by_podcast/map.js b/couchdb/userdata/_design/usertags/views/by_podcast/map.js deleted file mode 100644 index abbbecc0..00000000 --- a/couchdb/userdata/_design/usertags/views/by_podcast/map.js +++ /dev/null @@ -1,10 +0,0 @@ -function(doc) -{ - if(doc.doc_type == "PodcastUserState") - { - for(n in doc.tags) - { - emit([doc.podcast, doc.tags[n]], 0.5); - } - } -} diff --git a/couchdb/userdata/_design/usertags/views/by_podcast/reduce.js b/couchdb/userdata/_design/usertags/views/by_podcast/reduce.js deleted file mode 100644 index a1050627..00000000 --- a/couchdb/userdata/_design/usertags/views/by_podcast/reduce.js +++ /dev/null @@ -1 +0,0 @@ -_sum diff --git a/couchdb/userdata/_design/usertags/views/by_user/map.js b/couchdb/userdata/_design/usertags/views/by_user/map.js deleted file mode 100644 index 9bd095e0..00000000 --- a/couchdb/userdata/_design/usertags/views/by_user/map.js +++ /dev/null @@ -1,10 +0,0 @@ -function (doc) -{ - if (doc.doc_type == "PodcastUserState") - { - for(n in doc.tags) - { - emit([doc.user, doc.podcast], doc.tags[n]); - } - } -} diff --git a/couchdb/userdata/_design/usertags/views/podcasts/map.js b/couchdb/userdata/_design/usertags/views/podcasts/map.js deleted file mode 100644 index 18911a8b..00000000 --- a/couchdb/userdata/_design/usertags/views/podcasts/map.js +++ /dev/null @@ -1,11 +0,0 @@ -function(doc) -{ - if(doc.doc_type == "PodcastUserState") - { - for(n in doc.tags) - { - emit([doc.tags[n], doc.podcast], 0.5); - } - } -} - diff --git a/couchdb/userdata/_design/usertags/views/podcasts/reduce.js b/couchdb/userdata/_design/usertags/views/podcasts/reduce.js deleted file mode 100644 index a1050627..00000000 --- a/couchdb/userdata/_design/usertags/views/podcasts/reduce.js +++ /dev/null @@ -1 +0,0 @@ -_sum diff --git a/couchdb/users/_design/clients/views/by_ua_string/map.js b/couchdb/users/_design/clients/views/by_ua_string/map.js deleted file mode 100644 index 753daa13..00000000 --- a/couchdb/users/_design/clients/views/by_ua_string/map.js +++ /dev/null @@ -1,14 +0,0 @@ -function(doc) -{ - if(doc.doc_type == "User") - { - for(var n in doc.devices) - { - var device = doc.devices[n]; - if(device.user_agent) - { - emit(device.user_agent, device.id); - } - } - } -} diff --git a/couchdb/users/_design/clients/views/by_ua_string/reduce.js b/couchdb/users/_design/clients/views/by_ua_string/reduce.js deleted file mode 100644 index c866cd72..00000000 --- a/couchdb/users/_design/clients/views/by_ua_string/reduce.js +++ /dev/null @@ -1 +0,0 @@ -_count diff --git a/couchdb/users/_design/users/validate_doc_update.js b/couchdb/users/_design/users/validate_doc_update.js deleted file mode 100644 index a7c7443b..00000000 --- a/couchdb/users/_design/users/validate_doc_update.js +++ /dev/null @@ -1,59 +0,0 @@ -function(newDoc, oldDoc, userCtx) -{ - - if(newDoc.doc_type == "User") - { - var device_uids = []; - var device_ids = []; - - for(var n in newDoc.devices) - { - var device = newDoc.devices[n]; - - if(device_ids.indexOf(device.id) > -1) - { - throw({forbidden: "Duplicate Device-Id " + device.id + - " for user " + newDoc._id}); - } - - device_ids.push(device.id); - - if(device_uids.indexOf(device.uid) > -1) - { - throw({forbidden: "Duplicate Device-UID " + device.uid + - " for user " + newDoc._id}); - } - - if(!device.uid.match("^[\\w_.-]+$")) - { - throw({forbidden: "Invalid Device-UID " + device.uid + - " for user " + newDoc._id}); - } - - device_uids.push(device.uid); - } - - for(var n in newDoc.sync_groups) - { - var group = newDoc.sync_groups[n]; - - if(group.length < 2) - { - throw({forbidden: "Sync-Group " + n + " must contain at " + - "least two devices for user " + newDoc._id}); - } - - for(var i in group) - { - var device_id = group[i]; - - if(device_ids.indexOf(device_id) < 0) - { - throw({forbidden: "Invalid Device-Id " + device_id + - " in Sync-Group " + n + " for user " + - newDoc._id}); - } - } - } - } -} diff --git a/couchdb/users/_design/users/views/by_google_email/map.js b/couchdb/users/_design/users/views/by_google_email/map.js deleted file mode 100644 index f469fc60..00000000 --- a/couchdb/users/_design/users/views/by_google_email/map.js +++ /dev/null @@ -1,10 +0,0 @@ -function(doc) -{ - if(doc.doc_type == "User") - { - if(doc.google_email) - { - emit(doc.google_email, null); - } - } -} diff --git a/couchdb/users/_design/users/views/deleted/map.js b/couchdb/users/_design/users/views/deleted/map.js deleted file mode 100644 index 779b087d..00000000 --- a/couchdb/users/_design/users/views/deleted/map.js +++ /dev/null @@ -1,10 +0,0 @@ -function(doc) -{ - if(doc.doc_type == "User") - { - if(doc.deleted == true) - { - emit(null, null); - } - } -} diff --git a/couchdb/users/_design/users/views/deleted/reduce.js b/couchdb/users/_design/users/views/deleted/reduce.js deleted file mode 100644 index c866cd72..00000000 --- a/couchdb/users/_design/users/views/deleted/reduce.js +++ /dev/null @@ -1 +0,0 @@ -_count diff --git a/doc/dev/couchdb-docs.rst b/doc/dev/couchdb-docs.rst deleted file mode 100644 index 58aa57b8..00000000 --- a/doc/dev/couchdb-docs.rst +++ /dev/null @@ -1,246 +0,0 @@ - -CouchDB Documents -================= - -This page describes the documents that are used in the CouchDB based backend of -the gpodder.net webservice. - -User ----- - -A registered (but probably not yet activated) user and all his devices. - -Example Document :: - - { - username: "stefan", - email: "stefan@gpodder.net",, - password: "asdf$asdfasdf$asdfsdfa", - is_active: False, - registration_key: "ad18719239817389ad", - - devices: [ - { - "_id": "872348923987492834", - "user": "stefan", - "id": "n900", - "type": "mobile", - "label": "N900" - }, - { ... } - ], - - synchronized: [ - ["872348923987492834", "3498503485093495"], - ["9384053405", "90548903458"] - ] - } - - - -Podcast -------- - -If a Podcast does not belong to a group, it is a document on its own. The -"subscriber" field contains historical subscriber counts. The "tags" field -contains all tags that are fetched in some automated way (ie not entered by a -user). - -A Podcast contains recent subscriber data in the "subscribers" property. To -keep the Podcast objects small, subscriber data is periodically moved to the -podcast's PodcastSubscriberData object. - - -Example Document :: - - { - _id: "118913287192379173298" - name: "Cool Podcast", - feed_url: "http://podcast.com/mp3.xml", - description: "..." - subscribers: [{timestamp: "2010-09-01", subscriber_count: 120}, ... }, - related_podcasts: ["34059384598304850", "23480128409230482"], - tags: { - feed: [["technology", 50], ["news", 20]], - delicious: [["technews", 10]] - }, - publishers: ["stefan"], - merged_ids: [ ...], - language: "en", - episodes: { - 19384038450938450983: { Episode }, - 20928340283040234820. { Episode }, - }, - content_types: ["image", "audio"] - } - - -PodcastGroup ------------- - -Represents a group of podcast and contains all podcasts assigned to it. -Once a Podcast is assigned to a group, its Podcast document in deleted -and moved into the PodcastGroup. - -Example Document :: - - { - _id: "9238409283402830480", - oldid: 10, - name: "Cool Podcast", - podcasts: [ - { - id: "118913287192379173298", - name: "Cool Podcast", - group: "9238409283402830480", # the _id of the group for - # referring to it when the the - # podcast is referred to separately - group_member_name: "MP3", # required if Podcast is member of a group - feed_url: "http://podcast.com/mp3.xml", - description: "..." - subscribers: [[2010-09-01, 120], [2010-09-01, 130]], - related_podcasts: ["34059384598304850", "23480128409230482"], - tags: ["technology", "news"], - publishers: ["stefan"], - episodes: Episode-Dict - }, - { ... } - ], - } - - - -PodcastSubscriberData ---------------------- - -Contains the older subscriber data for a Podcast. This can be used to retrieve -changes in the number of subscribers of a podcast. - -Example Document :: - - { - podcast: "118913287192379173298", - subscribers: [ - {timestamp: "2010-09-01", subscriber_count: 120}, - {timestamp: "2010-10-01", subscriber_count: 125}, - {timestamp: "2010-11-01", subscriber_count: 210}, - ], - } - - -Episode -------- - -An episode, in concept, belongs to a Podcast (not a PodcastGroup) but is a -standalone document. It has optional support for multiple media files (eg Audio -and PDF Show Notes). The fields listeners and recent_listeners are -regularly updated with aggregated data to substitute queries based on time. - -Example Document :: - - { - _id: "19384038450938450983", - title: "Cool Episode of Cool Podcast", - podcast: "118913287192379173298", - media_urls: ["http://podcast.com/episode1.mp3", "http://podcast.com/episode1.pdf"], - released: "2010-09-20 12:00", - tags: ["opensource", "technology"], - merged_ids: ["908345083045", "29380423849"], - media_types: ["audio"], - listeners: [["2010-09-20", 20], ["2010-09-21", 15]], - recent_listeners: 75, - language: "en" - } - - -PodcastUserState ----------------- - -Contains everything that a User has done with a Podcast. The podcast is -referenced by the "ref_url" which is the URL that was received via the API. It -can be used in API responses so that the user receives exactly the same URL he -has sent (the Podcast document could contain several URLs). - -Example Document :: - - { - _id: "3893745983453948589345", - user: "345983045035480345809", - user_oldid: 4253, - podcast: "118913287192379173298", - ref_url: "http://example.com/podcast.xml", - actions: - [ - { - action: "subscribed", - timestamp: "2010-09-10", - device: "872348923987492834" - }, - { - action: "unsubscribed", - timestamp: "2010-09-15", - device: "872348923987492834" - } - ], - tags: ["technews"] - settings: { ... } - } - - -EpisodeUserState ----------------- - -A EpisodeUserState contains everything that a User has done with an Episode. -The episode is referenced by the "ref_url" and "podcast_ref_url" which contain -the URLs for the episode and the podcast, exactly as they have been received -via the API. These values can also be used in API responses to refer to exactly -the same URL as in the request (both Episode and Podcast documents can contain -several URLs). - -Example Document :: - - { - episode: "3893745983453948589345", - podcast: "118913287192379173298", - ref_url: "http://podcast.com/episode-1.mp3", - podcast_ref_url: "http://podcast.com/mp3.xml", - actions: - [ - { - action: "download", - file: "http://podcast.com/episode-1.mp3", - timestamp: "2010-09-10", - device: "872348923987492834" - }, - { - action: "play", - file: "http://podcast.com/episode-1.mp3", - timestamp: "2010-09-15", - device: "872348923987492834" - } - ], - chapters: [ - { - from: "02:10", - to: "04:20", - advertisement: false, - label: "interview", - } - ], - settings: { ... } - } - - -Advertisement -------------- - -Contains an advertisement for a Podcast - -Example Document :: - - { - user: "stefan", - podcast: "118913287192379173298", - start: "2010-09-01", - end: "2010-09-15" - } diff --git a/doc/dev/couchdb-views.rst b/doc/dev/couchdb-views.rst deleted file mode 100644 index 1091feb4..00000000 --- a/doc/dev/couchdb-views.rst +++ /dev/null @@ -1,151 +0,0 @@ - -CouchDB Views -============= - -This page describes the views that will be used in the CouchDB based backend of -the gpodder.net webservice. - -The views are separated into groups, based on the databases they are indexed -on. - -General -------- - -This group of views is available on the general database, called ``mygpo`` by -default. - - -Clients -^^^^^^^ - -Doc-Types: User - -**Views** - -* `clients/by_ua_string `_ - - -Users -^^^^^ - -Doc-Types: User - -**Views** - -* `users/by_google_email `_ -* `users/deleted `_ - - -Userdata --------- - -This group of views is available in the *userdata* database, called -``mygpo_userdata`` by default. - - -Episode Actions -^^^^^^^^^^^^^^^ - -Doc-Types: EpisodeUserState - -**Views** - -* `episode_actions/by_device `_ -* `episode_actions/by_podcast_device `_ -* `episode_actions/by_podcast `_ -* `episode_actions/by_user `_ - - -Episode States -^^^^^^^^^^^^^^ - -Doc-Types: EpisodeUserState - -**Views** - -* `episode_states/by_podcast_episode `_ -* `episode_states/by_ref_urls `_ -* `episode_states/by_user_episode `_ -* `episode_states/by_user_podcast `_ - - -Favorites -^^^^^^^^^ - -Doc-Types: EpisodeUserState - -**Views** - -* `episodes/favorites_by_user `_ - - -History -^^^^^^^ - -Doc-Types: EpisodeUserState, PodcastUserState - -**Views** - -* `history/by_device `_ -* `history/by_user `_ - - -Listeners -^^^^^^^^^ - -Doc-Types: EpisodeUserState - -**Views** - -* `listeners/by_episode `_ -* `listeners/by_podcast_episode `_ -* `listeners/by_podcast `_ -* `listeners/by_user `_ -* `listeners/by_user_podcast `_ -* `listeners/times_played_by_user `_ - - -Podcast States -^^^^^^^^^^^^^^ - -Doc-Types: PodcastUserState - -**Views** - -* `podcast_states/by_device `_ -* `podcast_states/by_podcast `_ -* `podcast_states/by_user `_ - - -Subscribers -^^^^^^^^^^^ - -Doc-Types: PodcastUserState - -**Views** - -* `subscribers/by_podcast `_ - - -Subscriptions -^^^^^^^^^^^^^ - -Doc-Types: PodcastUserState - -**Views** - -* `subscriptions/by_device `_ -* `subscriptions/by_podcast `_ -* `subscriptions/by_user `_ - - -User-Tags -^^^^^^^^^ - -Doc-Types: PodcastUserState - -**Views** - -* `usertags/by_podcast `_ -* `usertags/by_user `_ -* `usertags/podcasts `_ diff --git a/doc/dev/index.rst b/doc/dev/index.rst index f6146664..2bc3b7ac 100644 --- a/doc/dev/index.rst +++ b/doc/dev/index.rst @@ -26,7 +26,5 @@ Contents .. toctree:: :maxdepth: 1 - couchdb-docs - couchdb-views python3 postgres-setup diff --git a/doc/dev/python3.rst b/doc/dev/python3.rst index 73d32213..17d8f903 100644 --- a/doc/dev/python3.rst +++ b/doc/dev/python3.rst @@ -15,8 +15,6 @@ OK Not OK ------ -* restkit # https://github.com/benoitc/restkit/tree/py3_2 -* couchdbkit * Babel # used only in one place, could maybe be removed as a (hard) dependency Unknown diff --git a/mygpo/administration/tests.py b/mygpo/administration/tests.py index 91fc91cb..a8124dc7 100644 --- a/mygpo/administration/tests.py +++ b/mygpo/administration/tests.py @@ -54,9 +54,7 @@ class SimpleTest(TestCase): device2 = Client.objects.create(user=user, uid='dev2', id=uuid.uuid1()) subscribe(p1, user, device1) - time.sleep(1) unsubscribe(p1, user, device1) - time.sleep(1) subscribe(p1, user, device1) subscribe(p2, user, device2) diff --git a/mygpo/api/advanced/__init__.py b/mygpo/api/advanced/__init__.py index 2a3527b5..a33ec755 100644 --- a/mygpo/api/advanced/__init__.py +++ b/mygpo/api/advanced/__init__.py @@ -43,8 +43,7 @@ from mygpo.utils import format_time, parse_bool, get_timestamp, \ from mygpo.decorators import allowed_methods, cors_origin from mygpo.history.models import EpisodeHistoryEntry from mygpo.core.tasks import auto_flattr_episode -from mygpo.users.models import (EpisodeAction, Client, - InvalidEpisodeActionAttributes, ) +from mygpo.users.models import Client, InvalidEpisodeActionAttributes from mygpo.users.settings import FLATTR_AUTO from mygpo.favorites.models import FavoriteEpisode from mygpo.core.json import JSONDecodeError diff --git a/mygpo/api/advanced/settings.py b/mygpo/api/advanced/settings.py index 5a7210bd..c340c556 100644 --- a/mygpo/api/advanced/settings.py +++ b/mygpo/api/advanced/settings.py @@ -26,8 +26,6 @@ from mygpo.utils import parse_request_body from mygpo.api.basic_auth import require_valid_user, check_username from mygpo.api.httpresponse import JsonResponse from mygpo.users.models import Client -from mygpo.db.couchdb import get_userdata_database -from mygpo.db.couchdb.episode_state import episode_state_for_user_episode @csrf_exempt @@ -38,8 +36,6 @@ from mygpo.db.couchdb.episode_state import episode_state_for_user_episode @cors_origin() def main(request, username, scope): - udb = get_userdata_database() - def user_settings(user): return user, user, None @@ -64,8 +60,7 @@ def main(request, username, scope): except Episode.DoesNotExist: raise Http404 - episode_state = episode_state_for_user_episode(user, episode) - return episode_state, episode_state, udb + return None, None, None models = dict( account = lambda: user_settings(request.user), diff --git a/mygpo/api/simple.py b/mygpo/api/simple.py index e22205c3..51d7f1b3 100644 --- a/mygpo/api/simple.py +++ b/mygpo/api/simple.py @@ -19,8 +19,6 @@ import string from itertools import islice from functools import wraps -from couchdbkit.exceptions import ResourceNotFound - from django.shortcuts import render from django.core.cache import cache from django.http import HttpResponse, HttpResponseBadRequest diff --git a/mygpo/api/tests.py b/mygpo/api/tests.py index 8fc93578..544d6b9e 100644 --- a/mygpo/api/tests.py +++ b/mygpo/api/tests.py @@ -180,21 +180,20 @@ class DirectoryTest(TestCase): 'title': 'My Episode', }, ) + self.client = Client() def test_episode_info(self): """ Test that the expected number of queries is executed """ url = reverse('api-episode-info') + '?' + urlencode( (('podcast', self.podcast.url), ('url', self.episode.url))) - with self.assertNumQueries(4): - resp = anon_request(url) + resp = self.client.get(url) self.assertEqual(resp.status_code, 200) def load_tests(loader, tests, ignore): - tl = unittest.TestLoader() - tests.addTest(tl.loadTestsFromTestCase(AdvancedAPITests)) - tests.addTest(tl.loadTestsFromTestCase(SubscriptionAPITests)) - tests.addTest(tl.loadTestsFromTestCase(DirectoryTest)) + tests.addTest(loader.loadTestsFromTestCase(AdvancedAPITests)) + tests.addTest(loader.loadTestsFromTestCase(SubscriptionAPITests)) + tests.addTest(loader.loadTestsFromTestCase(DirectoryTest)) return tests diff --git a/mygpo/core/tasks.py b/mygpo/core/tasks.py index 4d5909bd..82115dd5 100644 --- a/mygpo/core/tasks.py +++ b/mygpo/core/tasks.py @@ -6,9 +6,6 @@ from django.conf import settings from mygpo.podcasts.models import Podcast, Episode from mygpo.celery import celery from mygpo.history.models import HistoryEntry -from mygpo.data.feeddownloader import PodcastUpdater -from mygpo.utils import get_timestamp -from mygpo.users.models import EpisodeAction from mygpo.flattr import Flattr from mygpo.history.models import EpisodeHistoryEntry diff --git a/mygpo/db/__init__.py b/mygpo/db/__init__.py deleted file mode 100644 index 66c441c7..00000000 --- a/mygpo/db/__init__.py +++ /dev/null @@ -1,8 +0,0 @@ - - -class DatabaseBackendException(Exception): - """ Generic database backend exception """ - - -class QueryParameterMissing(DatabaseBackendException): - """ A mandatory parameter to a query is missing """ diff --git a/mygpo/db/couchdb/__init__.py b/mygpo/db/couchdb/__init__.py deleted file mode 100644 index 3a9bffb8..00000000 --- a/mygpo/db/couchdb/__init__.py +++ /dev/null @@ -1,109 +0,0 @@ -from operator import itemgetter -from collections import namedtuple - -from couchdbkit.ext.django import loading -from couchdbkit import MultipleResultsFound -from couchdbkit import * - -import logging -logger = logging.getLogger(__name__) - - -def get_userdata_database(): - return loading.get_db('userdata') - - -class BulkException(Exception): - - def __init__(self, errors): - self.errors = errors - - -BulkError = namedtuple('BulkError', 'doc error reason') - - -def __default_reload(db, obj): - doc = db[obj._id] - return obj.__class__.wrap(doc) - - -__get_obj = itemgetter(0) - -def bulk_save_retry(obj_funs, db, reload_f=__default_reload): - """ Saves multiple documents and retries failed ones - - Objects to be saved are passed as (obj, mod_f), where obj is the CouchDB - and mod_f is the modification function that should be applied to it. - - If saving a document fails, it is again fetched from the database, the - modification function is applied again and saving is retried. """ - - errors = [] - - while True: - - # apply modification function (and keep funs) - obj_funs = [(f(o), f) for (o, f) in obj_funs] - - # filter those with obj None - obj_funs = filter(lambda of: __get_obj(of) is not None, obj_funs) - - # extract objects - objs = map(__get_obj, obj_funs) - - if not objs: - return - - try: - db.save_docs(objs) - return - - except BulkSaveError as ex: - - new_obj_funs = [] - for res, (obj, f) in zip(ex.results, obj_funs): - if res.get('error', False) == 'conflict': - - # reload conflicted object - obj = reload_f(db, obj) - new_obj_funs.append( (obj, f) ) - - elif res.get('error', False): - # don't retry other errors - err = BulkError(obj, res['error'], res.get('reason', None)) - errors.append(err) - - obj_funs = new_obj_funs - - if errors: - logger.warn('Errors at bulk-save: %s', errors) - raise BulkException(errors) - - -def get_single_result(db, view, **query_args): - """ return a single CouchDB view result - - Logs an error if multiple results are returned, and uses the first result. - This can happen as CouchDB can not guarantee uniqueness of attributes other - than _id. If no result are fetched, None is returned. """ - - r = db.view(view, **query_args) - - if not r: - return None - - try: - result = r.one() - - except MultipleResultsFound as ex: - logger.info('Multiple results found in %s with params %s', - view, query_args) - # use the first result as fallback - result = r.first() - - # we can only set the db if the result has been - # wrapped (depending on query_args) - if hasattr(result, 'set_db'): - result.set_db(db) - - return result diff --git a/mygpo/db/couchdb/episode_state.py b/mygpo/db/couchdb/episode_state.py deleted file mode 100644 index 99fd6b0f..00000000 --- a/mygpo/db/couchdb/episode_state.py +++ /dev/null @@ -1,156 +0,0 @@ -from hashlib import sha1 - -from django.core.cache import cache - -from mygpo.podcasts.models import Podcast, Episode -from mygpo.users.models import EpisodeUserState -from mygpo.db import QueryParameterMissing -from mygpo.db.couchdb import get_userdata_database, get_single_result -from mygpo.decorators import repeat_on_conflict - - -def episode_state_for_user_episode(user, episode): - - if not user: - raise QueryParameterMissing('user') - - if not episode: - raise QueryParameterMissing('episode') - - if hasattr(episode, '_id'): - episode_id = episode._id - else: - episode_id = episode.get_id() - key = 'episode-state-userid-%s-episodeid-%s' % (sha1(user.profile.uuid.hex).hexdigest(), - sha1(episode_id).hexdigest()) - -# Disabled as cache invalidation does not work properly -# state = cache.get(key) -# if state: -# return state - - udb = get_userdata_database() - state = get_single_result(udb, 'episode_states/by_user_episode', - key = [user.profile.uuid.hex, episode_id], - include_docs = True, - limit = 1, - schema = EpisodeUserState, - ) - - if state: - cache.set(key, state) - return state - - else: - if isinstance(episode.podcast, unicode): - podcast = Podcast.objects.get_by_any_id(episode.podcast) - else: - podcast = episode.podcast - - state = EpisodeUserState() - state.episode = episode_id - state.podcast = podcast.get_id() - state.user = user.profile.uuid.hex - state.ref_url = episode.url - state.podcast_ref_url = podcast.url - # don't cache here, because the state is saved by the calling function - - return state - - -def episode_state_for_ref_urls(user, podcast_url, episode_url): - - if not user: - raise QueryParameterMissing('user') - - if not podcast_url: - raise QueryParameterMissing('podcast_url') - - if not episode_url: - raise QueryParameterMissing('episode_url') - - - cache_key = 'episode-state-%s-%s-%s' % (user.profile.uuid.hex, - sha1(podcast_url).hexdigest(), - sha1(episode_url).hexdigest()) - - state = cache.get(cache_key) - if state: - return state - - udb = get_userdata_database() - state = get_single_result(udb, 'episode_states/by_ref_urls', - key = [user.profile.uuid.hex, podcast_url, episode_url], - limit = 1, - include_docs=True, - schema = EpisodeUserState, - ) - - if state: - state.ref_url = episode_url - state.podcast_ref_url = podcast_url - cache.set(cache_key, state, 60*60) - return state - - else: - podcast = Podcast.objects.get_or_create_for_url(podcast_url) - episode = Episode.objects.get_or_create_for_url(podcast, episode_url) - return episode_state_for_user_episode(user, episode) - - - -def get_episode_actions(user_id, since=None, until={}, podcast_id=None, - device_id=None, limit=1000): - """ Returns Episode Actions for the given criteria - - There is an upper limit on how many actions will be returned; until is the - timestamp of the last episode action. - """ - - if not user_id: - raise QueryParameterMissing('user_id') - - if since >= until: - return [], until - - if not podcast_id and not device_id: - view = 'episode_actions/by_user' - startkey = [user_id, since] - endkey = [user_id, until] - - elif podcast_id and not device_id: - view = 'episode_actions/by_podcast' - startkey = [user_id, podcast_id, since] - endkey = [user_id, podcast_id, until] - - elif device_id and not podcast_id: - view = 'episode_actions/by_device' - startkey = [user_id, device_id, since] - endkey = [user_id, device_id, until] - - else: - view = 'episode_actions/by_podcast_device' - startkey = [user_id, podcast_id, device_id, since] - endkey = [user_id, podcast_id, device_id, until] - - udb = get_userdata_database() - res = udb.view(view, - startkey = startkey, - endkey = endkey, - limit = limit, - ) - - results = list(res) - actions = map(lambda r: r['value'], results) - if actions: - # the upload_timestamp is always the last part of the key - until = results[-1]['key'][-1] - - return actions, until - - -@repeat_on_conflict(['state']) -def add_episode_actions(state, actions): - udb = get_userdata_database() - state.add_actions(actions) - udb.save_doc(state) diff --git a/mygpo/db/couchdb/models.py b/mygpo/db/couchdb/models.py deleted file mode 100644 index e69de29b..00000000 diff --git a/mygpo/db/couchdb/utils.py b/mygpo/db/couchdb/utils.py deleted file mode 100644 index 71eb6223..00000000 --- a/mygpo/db/couchdb/utils.py +++ /dev/null @@ -1,41 +0,0 @@ -# -# This file is part of my.gpodder.org. -# -# my.gpodder.org is free software: you can redistribute it and/or modify it -# under the terms of the GNU Affero General Public License as published by -# the Free Software Foundation, either version 3 of the License, or (at your -# option) any later version. -# -# my.gpodder.org is distributed in the hope that it will be useful, but -# WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -# or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public -# License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with my.gpodder.org. If not, see . -# - -import os.path - -from couchdbkit.loaders import FileSystemDocsLoader -from couchdbkit.ext.django import loading - -from django.conf import settings - -import logging -logger = logging.getLogger(__name__) - - -def sync_design_docs(): - """ synchronize the design docs for all databases """ - - base_dir = settings.BASE_DIR - - for part, label in settings.COUCHDB_DDOC_MAPPING.items(): - path = os.path.join(base_dir, '..', 'couchdb', part, '_design') - - logger.info('syncing ddocs for "%s" from "%s"', label, path) - - db = loading.get_db(label) - loader = FileSystemDocsLoader(path) - loader.sync(db, verbose=True) diff --git a/mygpo/decorators.py b/mygpo/decorators.py index f6bbdd1c..99bee437 100644 --- a/mygpo/decorators.py +++ b/mygpo/decorators.py @@ -18,11 +18,7 @@ # from functools import wraps -import inspect -from couchdbkit import ResourceConflict - -from django.http import Http404 from django.shortcuts import render, get_object_or_404 from django.http import HttpResponseForbidden, HttpResponseNotAllowed from django.contrib.auth import get_user_model @@ -86,68 +82,6 @@ def allowed_methods(methods): return decorator -class repeat_on_conflict(object): - """ Repeats an update operation in case of a ResourceConflict - - In case of a CouchDB ResourceConflict, reloads the parameter with the given - name and repeats the function call until it succeeds. When calling the - function, the parameter that should be reloaded must be given as a - keyword-argument """ - - ARGSPEC = '__repeat_argspec__' - - def __init__(self, obj_names=[], reload_f=None): - self.obj_names = obj_names - self.reload_f = reload_f or self.default_reload - - def default_reload(self, obj): - # if the object knows its DB, use this one - if obj._db: - doc = obj._db.get(obj._id) - return obj.__class__.wrap(doc) - # otherwise the class' default DB is used - return obj.__class__.get(obj._id) - - def build_locals(self, f, args, kwargs): - argspec = getattr(f, self.ARGSPEC) - if len(args) > len(argspec.args): - varargs = args[len(args):] - args = args[:len(args)] - else: - varargs = [] - locals = dict(zip(argspec.args, args)) - if argspec.varargs is not None: - locals.update({argspec.varargs: varargs}) - if argspec.keywords is not None: - locals.update({argspec.keywords: kwargs}) - locals.update(kwargs) - return locals - - def __call__(self, f): - - if not hasattr(f, self.ARGSPEC): - argspec = inspect.getargspec(f) - setattr(f, self.ARGSPEC, argspec) - - @wraps(f) - def wrapper(*args, **kwargs): - all_args = before = self.build_locals(f, args, kwargs) - - # repeat until operation succeeds - # TODO: adding an upper bound might make sense - while True: - try: - return f(**all_args) - - except ResourceConflict as e: - logger.info('retrying %s, reloading %s', f, self.obj_names) - for obj_name in self.obj_names: - obj = all_args[obj_name] - all_args[obj_name] = self.reload_f(obj) - - return wrapper - - def query_if_required(): """ If required, queries some resource before calling the function diff --git a/mygpo/settings.py b/mygpo/settings.py index 866a5925..bff3bf6c 100644 --- a/mygpo/settings.py +++ b/mygpo/settings.py @@ -37,24 +37,6 @@ DATABASES = { default='postgres://mygpo:mygpo@localhost/mygpo'), } -COUCHDB_DATABASES = { - 'mygpo.users': - {'URL': 'http://127.0.0.1:5984/mygpo_users'}, - - 'mygpo.userdata': - {'URL': 'http://127.0.0.1:5984/mygpo_userdata'}, -} - -# Maps design documents to databases. The keys correspond to the directories in -# mygpo/couch/, the values are the app labels which are mapped to the actual -# databases in COUCHDB_DATABASES. This indirect mapping is used because -# COUCHDB_DATABASES is likely to be overwritten in settings_prod.py while -# COUCHDB_DDOC_MAPPING is most probably not overwritten. -COUCHDB_DDOC_MAPPING = { - 'userdata': 'userdata', - 'users': 'users', -} - # Local time zone for this installation. Choices can be found here: # http://en.wikipedia.org/wiki/List_of_tz_zones_by_name # although not all choices may be available on all operating systems. @@ -131,7 +113,6 @@ INSTALLED_APPS = ( 'mygpo.pubsub', 'mygpo.podcastlists', 'mygpo.votes', - 'mygpo.db.couchdb', ) try: diff --git a/mygpo/users/models.py b/mygpo/users/models.py index 0760515c..70defe01 100644 --- a/mygpo/users/models.py +++ b/mygpo/users/models.py @@ -3,12 +3,8 @@ from __future__ import unicode_literals import re import uuid import collections -from datetime import datetime import dateutil.parser -from itertools import imap -import string -from couchdbkit.ext.django.schema import * from uuidfield import UUIDField from django.core.validators import RegexValidator @@ -22,7 +18,6 @@ from mygpo.core.models import (TwitterModel, UUIDModel, SettingsModel, GenericManager, DeleteableModel, ) from mygpo.podcasts.models import Podcast, Episode from mygpo.utils import random_token -from mygpo.users.settings import FAV_FLAG, SettingsMixin import logging logger = logging.getLogger(__name__) @@ -174,126 +169,6 @@ class UserProfile(TwitterModel, SettingsModel): setattr(self, token_name, random_token()) -class EpisodeAction(DocumentSchema): - """ - One specific action to an episode. Must - always be part of a EpisodeUserState - """ - - action = StringProperty(required=True) - - # walltime of the event (assigned by the uploading client, defaults to now) - timestamp = DateTimeProperty(required=True, default=datetime.utcnow) - - # upload time of the event - upload_timestamp = IntegerProperty(required=True) - - device_oldid = IntegerProperty(required=False) - device = StringProperty() - started = IntegerProperty() - playmark = IntegerProperty() - total = IntegerProperty() - - def __eq__(self, other): - if not isinstance(other, EpisodeAction): - return False - vals = ('action', 'timestamp', 'device', 'started', 'playmark', - 'total') - return all([getattr(self, v, None) == getattr(other, v, None) for v in vals]) - - - def to_history_entry(self): - entry = HistoryEntry() - entry.action = self.action - entry.timestamp = self.timestamp - entry.device_id = self.device - entry.started = self.started - entry.position = self.playmark - entry.total = self.total - return entry - - - - def validate_time_values(self): - """ Validates allowed combinations of time-values """ - - PLAY_ACTION_KEYS = ('playmark', 'started', 'total') - - # Key found, but must not be supplied (no play action!) - if self.action != 'play': - for key in PLAY_ACTION_KEYS: - if getattr(self, key, None) is not None: - raise InvalidEpisodeActionAttributes('%s only allowed in play actions' % key) - - # Sanity check: If started or total are given, require playmark - if ((self.started is not None) or (self.total is not None)) and \ - self.playmark is None: - raise InvalidEpisodeActionAttributes('started and total require position') - - # Sanity check: total and playmark can only appear together - if ((self.total is not None) or (self.started is not None)) and \ - ((self.total is None) or (self.started is None)): - raise InvalidEpisodeActionAttributes('total and started can only appear together') - - - def __repr__(self): - return '%s-Action on %s at %s (in %s)' % \ - (self.action, self.device, self.timestamp, self._id) - - - def __hash__(self): - return hash(frozenset([self.action, self.timestamp, self.device, - self.started, self.playmark, self.total])) - - -class EpisodeUserState(Document, SettingsMixin): - """ - Contains everything a user has done with an Episode - """ - - episode = StringProperty(required=True) - actions = SchemaListProperty(EpisodeAction) - user_oldid = IntegerProperty() - user = StringProperty(required=True) - ref_url = StringProperty(required=True) - podcast_ref_url = StringProperty(required=True) - merged_ids = StringListProperty() - chapters = ListProperty() - podcast = StringProperty(required=True) - - - - def add_actions(self, actions): - map(EpisodeAction.validate_time_values, actions) - self.actions = list(self.actions) + actions - self.actions = list(set(self.actions)) - self.actions = sorted(self.actions, key=lambda x: x.timestamp) - - - def is_favorite(self): - return self.get_wksetting(FAV_FLAG) - - - def set_favorite(self, set_to=True): - self.settings[FAV_FLAG.name] = set_to - - - def get_history_entries(self): - return imap(EpisodeAction.to_history_entry, self.actions) - - - def __repr__(self): - return 'Episode-State %s (in %s)' % \ - (self.episode, self._id) - - def __eq__(self, other): - if not isinstance(other, EpisodeUserState): - return False - - return (self.episode == other.episode and - self.user == other.user) - - class SyncGroup(models.Model): """ A group of Clients """ diff --git a/mygpo/users/ratings.py b/mygpo/users/ratings.py deleted file mode 100644 index 5ebfcda7..00000000 --- a/mygpo/users/ratings.py +++ /dev/null @@ -1,32 +0,0 @@ -from datetime import datetime - -from couchdbkit.ext.django.schema import * - - -class Rating(DocumentSchema): - rating = IntegerProperty() - timestamp = DateTimeProperty(default=datetime.utcnow) - user = StringProperty() - - -ALLOWED_RATINGS = (1, -1) - -class RatingMixin(DocumentSchema): - ratings = SchemaListProperty(Rating) - - def rate(self, rating_val, user_id): - - if user_id is None: - raise ValueError('User must not be None') - - if rating_val not in ALLOWED_RATINGS: - raise ValueError('Rating must be in %s' % (ALLOWED_RATINGS, )) - - rating = Rating(rating=rating_val, user=user_id) - self.ratings = filter(lambda r: r.user is not None, self.ratings) - self.ratings = filter(lambda r: r.user != user_id, self.ratings) - self.ratings.append(rating) - - - def get_rating(self): - return sum(r.rating for r in self.ratings) diff --git a/mygpo/users/settings.py b/mygpo/users/settings.py index ae35abd3..70c1dd17 100644 --- a/mygpo/users/settings.py +++ b/mygpo/users/settings.py @@ -1,7 +1,5 @@ from collections import namedtuple -from couchdbkit.ext.django.schema import * - WellKnownSetting = namedtuple('WellKnownSetting', 'name default') @@ -32,15 +30,3 @@ FLATTR_USERNAME = WellKnownSetting('flattr_username', '') # Flag to mark an episode as favorite FAV_FLAG = WellKnownSetting('is_favorite', False) - - - -class SettingsMixin(DocumentSchema): - """ Objects that have an Old-Id from the old MySQL backend """ - - settings = DictProperty() - - - def get_wksetting(self, setting): - """ returns the value of a well-known setting """ - return self.settings.get(setting.name, setting.default) diff --git a/mygpo/users/tests.py b/mygpo/users/tests.py index ddf427e6..d67060b9 100644 --- a/mygpo/users/tests.py +++ b/mygpo/users/tests.py @@ -102,12 +102,6 @@ class UnsubscribeMergeTests(TestCase): pm = PodcastMerger([self.podcast1, self.podcast2], Counter(), []) pm.merge() - # seems that setting delayed_commit = false in the CouchDB config, as - # well as a delay here fix the intermittent failures. - # TODO: further investiation needed - import time - time.sleep(2) - # get podcast for URL of podcast2 and unsubscribe from it p = Podcast.objects.get(urls__url=self.P2_URL) unsubscribe(p, self.user, self.device) diff --git a/mygpo/web/templatetags/podcasts.py b/mygpo/web/templatetags/podcasts.py index b402172c..c2de0fac 100644 --- a/mygpo/web/templatetags/podcasts.py +++ b/mygpo/web/templatetags/podcasts.py @@ -124,8 +124,7 @@ register.tag('podcast_group_link_target', PodcastGroupLinkTargetNode.compile) def podcast_group_link(podcast, title=None): """ Returns the link strings for Podcast and PodcastGroup objects - automatically distringuishes between relational Podcast/PodcastGroup - objects and CouchDB-based Podcast/PodcastGroup objects """ + automatically distinguishes between relational Podcast/PodcastGroup """ from mygpo.podcasts.models import PodcastGroup diff --git a/mygpo/web/utils.py b/mygpo/web/utils.py index 9d0f1fa7..e939df75 100644 --- a/mygpo/web/utils.py +++ b/mygpo/web/utils.py @@ -137,10 +137,7 @@ def maintenance(request, *args, **kwargs): def get_podcast_link_target(podcast, view_name='podcast', add_args=[]): - """ Returns the link-target for a Podcast, preferring slugs over Ids - - automatically distringuishes between relational Podcast objects and - CouchDB-based Podcasts """ + """ Returns the link-target for a Podcast, preferring slugs over Ids """ # we prefer slugs if podcast.slug: @@ -156,30 +153,15 @@ def get_podcast_link_target(podcast, view_name='podcast', add_args=[]): def get_podcast_group_link_target(group, view_name, add_args=[]): - """ Returns the link-target for a Podcast group, preferring slugs over Ids - - automatically distringuishes between relational Podcast objects and - CouchDB-based Podcasts """ - - # we prefer slugs - if group.slug: - args = [group.slug] - view_name = '%s-slug-id' % view_name - - # as a fallback we use CouchDB-IDs - else: - args = [group._id] - view_name = '%s-slug-id' % view_name - + """ the link-target for a Podcast group, preferring slugs over Ids """ + args = [group.slug] + view_name = '%s-slug-id' % view_name return reverse(view_name, args=args + add_args) def get_episode_link_target(episode, podcast, view_name='episode', add_args=[]): - """ Returns the link-target for an Episode, preferring slugs over Ids - - automatically distringuishes between relational Episode objects and - CouchDB-based Episodes """ + """ Returns the link-target for an Episode, preferring slugs over Ids """ # prefer slugs if episode.slug: diff --git a/mygpo/web/views/episode.py b/mygpo/web/views/episode.py index 027373dd..221eece3 100644 --- a/mygpo/web/views/episode.py +++ b/mygpo/web/views/episode.py @@ -33,7 +33,6 @@ from django.utils.translation import ugettext as _ from mygpo.podcasts.models import Podcast, Episode from mygpo.api.constants import EPISODE_ACTION_TYPES from mygpo.core.tasks import flattr_thing -from mygpo.users.models import HistoryEntry, EpisodeAction from mygpo.utils import parse_time, get_timestamp from mygpo.users.settings import FLATTR_TOKEN from mygpo.web.heatmap import EpisodeHeatmap @@ -225,7 +224,7 @@ def flattr_episode(request, episode): return HttpResponseRedirect(get_episode_link_target(episode, podcast)) -# To make all view accessible via either CouchDB-ID for Slugs +# To make all view accessible via either IDs or Slugs # a decorator queries the episode and passes the Id on to the # regular views diff --git a/requirements.txt b/requirements.txt index 039b8194..d6442424 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,7 +2,6 @@ Babel==1.3 Pillow==2.5.3 Django==1.7.1 celery==3.1.13 --e git+https://github.com/stefankoegl/couchdbkit.git@7593bca76f76097394840a96031ce7a297b18b34#egg=couchdbkit-dev coverage==3.7.1 dj-database-url==0.3.0 django-redis-sessions==0.4.0 @@ -18,6 +17,5 @@ pyes==0.99.5 python-dateutil==2.2 python-memcached==1.53 redis==2.10.3 -restkit==4.2.2 ujson==1.33 django-celery==3.1.10 diff --git a/res/couchdb-docs/replicate-categories.json b/res/couchdb-docs/replicate-categories.json deleted file mode 100644 index f57133b4..00000000 --- a/res/couchdb-docs/replicate-categories.json +++ /dev/null @@ -1,14 +0,0 @@ -{ - "_id": "mygpo_categories", - "source": "mygpo", - "target": "mygpo_categories", - "create_target": true, - "user_ctx": { - "name": "username", - "roles": ["admin"] - }, - "filter": "generic/is_type", - "query_params": { - "doc_type": "Category" - } -} -- 2.11.4.GIT