Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(140)

Unified Diff: third_party/gsutil/third_party/protorpc/demos/tunes_db/server/tunes_db.py

Issue 1377933002: [catapult] - Copy Telemetry's gsutilz over to third_party. (Closed) Base URL: https://github.com/catapult-project/catapult.git@master
Patch Set: Rename to gsutil. Created 5 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: third_party/gsutil/third_party/protorpc/demos/tunes_db/server/tunes_db.py
diff --git a/third_party/gsutil/third_party/protorpc/demos/tunes_db/server/tunes_db.py b/third_party/gsutil/third_party/protorpc/demos/tunes_db/server/tunes_db.py
new file mode 100755
index 0000000000000000000000000000000000000000..0d62cb7ce9a7bdbc69bc629b946168854b6713de
--- /dev/null
+++ b/third_party/gsutil/third_party/protorpc/demos/tunes_db/server/tunes_db.py
@@ -0,0 +1,539 @@
+#!/usr/bin/env python
+#
+# Copyright 2010 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+"""Tunes DB service implementation.
+
+This module contains all the protocol buffer and service definitions
+necessary for the Tunes DB service.
+"""
+
+import base64
+import sys
+
+from google.appengine.ext import db
+
+from protorpc import descriptor
+from protorpc import message_types
+from protorpc import messages
+from protorpc import protobuf
+from protorpc import remote
+
+import model
+
+
+class Artist(messages.Message):
+ """Musician or music group responsible for music production.
+
+ Fields:
+ artist_id: Unique opaque identifier for artist.
+ name: User friendly name of artist.
+ album_count: Number of albums produced by artist.
+ """
+
+ artist_id = messages.StringField(1, required=True)
+ name = messages.StringField(2, required=True)
+
+ album_count = messages.IntegerField(3)
+
+
+class Album(messages.Message):
+ """Album produced by a musician or music group.
+
+ Fields:
+ album_id: Unique opaque identifier for artist.
+ artist_id: Artist id of musician or music group that produced album.
+ name: Name of album.
+ released: Year when album was released.
+ """
+
+ album_id = messages.StringField(1, required=True)
+ artist_id = messages.StringField(2, required=True)
+ name = messages.StringField(3, required=True)
+ released = messages.IntegerField(4)
+
+
+class AddArtistRequest(messages.Message):
+ """Request to add a new Artist to library.
+
+ Fields:
+ name: User friendly name of artist.
+ """
+
+ name = messages.StringField(1, required=True)
+
+
+class AddArtistResponse(messages.Message):
+ """Response sent after creation of new artist in library.
+
+ Fields:
+ artist_id: Unique opaque ID of new artist.
+ """
+
+ artist_id = messages.StringField(1, required=True)
+
+
+class UpdateArtistRequest(messages.Message):
+ """Update an existing artist.
+
+ Fields:
+ artist: Complete information about artist to update.
+ """
+
+ artist = messages.MessageField(Artist, 1, required=True)
+
+
+class UpdateArtistResponse(messages.Message):
+ """Artist update response.
+
+ Fields:
+ artist_updated: Artist was found and updated.
+ """
+
+ artist_updated = messages.BooleanField(1, required=True)
+
+
+class DeleteArtistRequest(messages.Message):
+ """Delete artist from library.
+
+ Fields:
+ artist_id: Unique opaque ID of artist to delete.
+ """
+
+ artist_id = messages.StringField(1, required=True)
+
+
+class DeleteArtistResponse(messages.Message):
+ """Artist deletion response.
+
+ Fields:
+ artist_deleted: Artist was found and deleted.
+ """
+
+ artist_deleted = messages.BooleanField(1, default=True)
+
+
+class FetchArtistRequest(messages.Message):
+ """Fetch an artist from the library.
+
+ Fields:
+ artist_id: Unique opaque ID of artist to fetch.
+ """
+
+ artist_id = messages.StringField(1, required=True)
+
+
+class FetchArtistResponse(messages.Message):
+ """Fetched artist from library.
+
+ Fields:
+ artist: Artist found in library.
+ """
+
+ artist = messages.MessageField(Artist, 1)
+
+
+class SearchArtistsRequest(messages.Message):
+ """Artist search request.
+
+ Fields:
+ continuation: Continuation from the response of a previous call to
+ search_artists remote method.
+ fetch_size: Maximum number of records to retrieve.
+ name_prefix: Name prefix of artists to search. If none provided and
+ no continuation provided, search will be of all artists in library.
+ If continuation is provided, name_prefix should be empty, if not, value
+ is ignored.
+ """
+
+ continuation = messages.StringField(1)
+ fetch_size = messages.IntegerField(2, default=10)
+ name_prefix = messages.StringField(3, default=u'')
+
+
+class SearchArtistsResponse(messages.Message):
+ """Response from searching artists.
+
+ Fields:
+ artists: Artists found from search up to fetch_size.
+ continuation: Opaque string that can be used with a new search request
+ that will continue finding new artists where this response left off.
+ Will not be set if there were no results from the search or fewer
+ artists were returned in the response than requested, indicating the end
+ of the query.
+ """
+
+ artists = messages.MessageField(Artist, 1, repeated=True)
+ continuation = messages.StringField(2)
+
+
+class AddAlbumRequest(messages.Message):
+ """Request to add a new album to library.
+
+ Fields:
+ name: User friendly name of album.
+ artist_id: Artist id of artist that produced record.
+ released: Year album was released.
+ """
+
+ name = messages.StringField(1, required=True)
+ artist_id = messages.StringField(2, required=True)
+ released = messages.IntegerField(3)
+
+
+class AddAlbumResponse(messages.Message):
+ """Response sent after creation of new album in library.
+
+ Fields:
+ album_id: Unique opaque ID of new album.
+ """
+
+ album_id = messages.StringField(1, required=True)
+
+
+class UpdateAlbumRequest(messages.Message):
+ """Update an existing album.
+
+ Fields:
+ album: Complete information about album to update.
+ """
+
+ album = messages.MessageField(Album, 1, required=True)
+
+
+class UpdateAlbumResponse(messages.Message):
+ """Album update response.
+
+ Fields:
+ album_updated: Album was found and updated.
+ """
+
+ album_updated = messages.BooleanField(1, required=True)
+
+
+class DeleteAlbumRequest(messages.Message):
+ """Delete album from library.
+
+ Fields:
+ album_id: Unique opaque ID of album to delete.
+ """
+
+ album_id = messages.StringField(1, required=True)
+
+
+class DeleteAlbumResponse(messages.Message):
+ """Album deletion response.
+
+ Fields:
+ album_deleted: Album was found and deleted.
+ """
+
+ album_deleted = messages.BooleanField(1, default=True)
+
+
+class FetchAlbumRequest(messages.Message):
+ """Fetch an album from the library.
+
+ Fields:
+ album_id: Unique opaque ID of album to fetch.
+ """
+
+ album_id = messages.StringField(1, required=True)
+
+
+class FetchAlbumResponse(messages.Message):
+ """Fetched album from library.
+
+ Fields:
+ album: Album found in library.
+ """
+
+ album = messages.MessageField(Album, 1)
+
+
+class SearchAlbumsRequest(messages.Message):
+ """Album search request.
+
+ Fields:
+ continuation: Continuation from the response of a previous call to
+ search_albums remote method.
+ fetch_size: Maximum number of records to retrieve.
+ name_prefix: Name prefix of albms to search. If none provided and
+ no continuation provided, search will be of all albums in library.
+ If continuation is provided, name_prefix should be empty, if not, value
+ is ignored.
+ artist_id: Restrict search to albums of single artist.
+ """
+
+ continuation = messages.StringField(1)
+ fetch_size = messages.IntegerField(2, default=10)
+ name_prefix = messages.StringField(3, default=u'')
+ artist_id = messages.StringField(4)
+
+
+class SearchAlbumsResponse(messages.Message):
+ """Response from searching artists.
+
+ Fields:
+ albums: Albums found from search up to fetch_size.
+ continuation: Opaque string that can be used with a new search request
+ that will continue finding new albums where this response left off.
+ Will not be set if there were no results from the search or fewer
+ albums were returned in the response than requested, indicating the end
+ of the query.
+ """
+
+ albums = messages.MessageField(Album, 1, repeated=True)
+ continuation = messages.StringField(2)
+
+
+class MusicLibraryService(remote.Service):
+ """Music library service."""
+
+ __file_set = None
+
+ def __artist_from_model(self, artist_model):
+ """Helper that copies an Artist model to an Artist message.
+
+ Args:
+ artist_model: model.ArtistInfo instance to convert in to an Artist
+ message.
+
+ Returns:
+ New Artist message with contents of artist_model copied in to it.
+ """
+ return Artist(artist_id=unicode(artist_model.key()),
+ name=artist_model.name,
+ album_count=artist_model.album_count)
+
+ def __album_from_model(self, album_model):
+ """Helper that copies an Album model to an Album message.
+
+ Args:
+ album_model: model.AlbumInfo instance to convert in to an Album
+ message.
+
+ Returns:
+ New Album message with contents of album_model copied in to it.
+ """
+ artist_id = model.AlbumInfo.artist.get_value_for_datastore(album_model)
+
+ return Album(album_id=unicode(album_model.key()),
+ artist_id=unicode(artist_id),
+ name=album_model.name,
+ released=album_model.released or None)
+
+ @classmethod
+ def __search_info(cls,
+ request,
+ info_class,
+ model_to_message,
+ customize_query=None):
+ """Search over an Info subclass.
+
+ Since all search request classes are very similar, it's possible to
+ generalize how to do searches over them.
+
+ Args:
+ request: Search request received from client.
+ info_class: The model.Info subclass to search.
+ model_to_method: Function (model) -> message that transforms an instance
+ of info_class in to the appropriate messages.Message subclass.
+ customize_query: Function (request, query) -> None that adds additional
+ filters to Datastore query based on specifics of that search message.
+
+ Returns:
+ Tuple (results, continuation):
+ results: A list of messages satisfying the parameters of the request.
+ None if there are no results.
+ continuation: Continuation string for response if there are more
+ results available. None if there are no more results available.
+ """
+ # TODO(rafek): fetch_size from this request should take priority
+ # over what is stored in continuation.
+ if request.continuation:
+ encoded_search, continuation = request.continuation.split(':', 1)
+ decoded_search = base64.urlsafe_b64decode(encoded_search.encode('utf-8'))
+ request = protobuf.decode_message(type(request), decoded_search)
+ else:
+ continuation = None
+ encoded_search = unicode(base64.urlsafe_b64encode(
+ protobuf.encode_message(request)))
+
+ name_prefix = request.name_prefix
+
+ query = info_class.search(name_prefix)
+ query.order('name')
+ if customize_query:
+ customize_query(request, query)
+
+ if continuation:
+ # TODO(rafek): Pure query cursors are not safe for model with
+ # query restrictions. Would technically need to be encrypted.
+ query.with_cursor(continuation)
+
+ fetch_size = request.fetch_size
+
+ model_instance = query.fetch(fetch_size)
+ results = None
+ continuation = None
+ if model_instance:
+ results = [model_to_message(i) for i in model_instance]
+ if len(model_instance) == fetch_size:
+ cursor = query.cursor()
+ continuation = u'%s:%s' % (encoded_search, query.cursor())
+
+ return results, continuation
+
+
+ @remote.method(AddArtistRequest, AddArtistResponse)
+ def add_artist(self, request):
+ """Add artist to library."""
+ artist_name = request.name
+ def do_add():
+ artist = model.ArtistInfo(name=artist_name)
+ artist.put()
+ return artist
+ artist = db.run_in_transaction(do_add)
+
+ return AddArtistResponse(artist_id = unicode(artist.key()))
+
+ @remote.method(UpdateArtistRequest, UpdateArtistResponse)
+ def update_artist(self, request):
+ """Update artist from library."""
+ def do_deletion():
+ artist = model.ArtistInfo.get(request.artist.artist_id)
+ if artist:
+ artist.name = request.artist.name
+ artist.put()
+ return True
+ else:
+ return False
+ return UpdateArtistResponse(
+ artist_updated=db.run_in_transaction(do_deletion))
+
+ @remote.method(DeleteArtistRequest, DeleteArtistResponse)
+ def delete_artist(self, request):
+ """Delete artist from library."""
+ def do_deletion():
+ artist = model.ArtistInfo.get(request.artist_id)
+ if artist:
+ db.delete(model.Info.all(keys_only=True).ancestor(artist))
+ return True
+ else:
+ return False
+ return DeleteArtistResponse(
+ artist_deleted = db.run_in_transaction(do_deletion))
+
+ @remote.method(FetchArtistRequest, FetchArtistResponse)
+ def fetch_artist(self, request):
+ """Fetch artist from library."""
+ artist_model = model.ArtistInfo.get(request.artist_id)
+ if isinstance(artist_model, model.ArtistInfo):
+ artist = self.__artist_from_model(artist_model)
+ else:
+ artist = None
+ return FetchArtistResponse(artist=artist)
+
+
+ @remote.method(SearchArtistsRequest, SearchArtistsResponse)
+ def search_artists(self, request):
+ """Search library for artists."""
+ results, continuation = self.__search_info(request,
+ model.ArtistInfo,
+ self.__artist_from_model)
+ return SearchArtistsResponse(artists=results or [],
+ continuation=continuation or None)
+
+ @remote.method(AddAlbumRequest, AddAlbumResponse)
+ def add_album(self, request):
+ """Add album to library."""
+ def create_album():
+ if not request.artist_id:
+ raise ValueError('Request does not have artist-id.')
+ artist = model.ArtistInfo.get(request.artist_id)
+ if not artist:
+ raise ValueError('No artist found for %s.' % request.artist_id)
+ artist.album_count += 1
+ artist.put()
+
+ album = model.AlbumInfo(name=request.name,
+ released=request.released,
+ artist=artist,
+ parent=artist)
+ album.put()
+
+ return album
+ album = db.run_in_transaction(create_album)
+
+ return AddAlbumResponse(album_id=unicode(album.key()))
+
+ @remote.method(UpdateAlbumRequest, UpdateAlbumResponse)
+ def update_album(self, request):
+ """Update album from library."""
+ def do_deletion():
+ album = model.AlbumInfo.get(request.album.album_id)
+ if album:
+ album.name = request.album.name
+ album.released = request.album.released
+ album.put()
+ return True
+ else:
+ return False
+ return UpdateAlbumResponse(album_updated=db.run_in_transaction(do_deletion))
+
+ @remote.method(DeleteAlbumRequest, DeleteAlbumResponse)
+ def delete_album(self, request):
+ """Delete album from library."""
+ def do_deletion():
+ album = model.AlbumInfo.get(request.album_id)
+
+ artist = album.artist
+ artist.album_count -= 1
+ artist.put()
+
+ if album:
+ db.delete(model.Info.all(keys_only=True).ancestor(album))
+ return True
+ else:
+ return False
+
+ return DeleteAlbumResponse(album_deleted=db.run_in_transaction(do_deletion))
+
+ @remote.method(FetchAlbumRequest, FetchAlbumResponse)
+ def fetch_album(self, request):
+ """Fetch album from library."""
+ album_model = model.AlbumInfo.get(request.album_id)
+ if isinstance(album_model, model.AlbumInfo):
+ album = self.__album_from_model(album_model)
+ else:
+ album = None
+ return FetchAlbumResponse(album=album)
+
+ @remote.method(SearchAlbumsRequest, SearchAlbumsResponse)
+ def search_albums(self, request):
+ """Search library for albums."""
+ def customize_query(request, query):
+ if request.artist_id:
+ query.filter('artist', db.Key(request.artist_id))
+
+ response = SearchAlbumsResponse()
+ results, continuation = self.__search_info(request,
+ model.AlbumInfo,
+ self.__album_from_model,
+ customize_query)
+ return SearchAlbumsResponse(albums=results or [],
+ continuation=continuation or None)

Powered by Google App Engine
This is Rietveld 408576698