<s> from datetime import datetime <EOL> from collections import namedtuple <EOL> from couchdbkit import ResourceNotFound <EOL> from django . core . management . base import LabelCommand <EOL> from dimagi . utils . chunked import chunked <EOL> from corehq . util . couch import send_keys_to_couch , IterDB <EOL> from corehq . util . couchdb_management import couch_config <EOL> Results = namedtuple ( '<STR_LIT>' , [ '<STR_LIT>' , '<STR_LIT>' , '<STR_LIT>' ] ) <EOL> def get_deleted_doc ( db , doc_id , rev ) : <EOL> res = db . get ( doc_id , revs = True , rev = rev ) <EOL> start = res [ '<STR_LIT>' ] [ '<STR_LIT:start>' ] <EOL> ids = res [ '<STR_LIT>' ] [ '<STR_LIT>' ] <EOL> prev_revision = "<STR_LIT>" . format ( start - <NUM_LIT:1> , ids [ <NUM_LIT:1> ] ) <EOL> try : <EOL> doc = db . get ( doc_id , rev = prev_revision ) <EOL> except ResourceNotFound : <EOL> res . pop ( '<STR_LIT>' ) <EOL> res . pop ( '<STR_LIT>' ) <EOL> doc = res <EOL> doc . pop ( '<STR_LIT>' ) <EOL> return doc <EOL> def undelete_docs ( db , doc_ids ) : <EOL> results = Results ( set ( ) , set ( ) , set ( ) ) <EOL> with IterDB ( db ) as iter_db : <EOL> for chunk in chunked ( set ( doc_ids ) , <NUM_LIT:100> ) : <EOL> for res in send_keys_to_couch ( db , keys = set ( chunk ) ) : <EOL> doc_id = res [ '<STR_LIT:key>' ] <EOL> if res . get ( '<STR_LIT:error>' , None ) == '<STR_LIT>' : <EOL> results . not_found . add ( doc_id ) <EOL> elif res . get ( '<STR_LIT:value>' , { } ) . get ( '<STR_LIT>' , False ) : <EOL> iter_db . save ( <EOL> get_deleted_doc ( db , doc_id , res [ '<STR_LIT:value>' ] [ '<STR_LIT>' ] ) <EOL> ) <EOL> results . restored . add ( doc_id ) <EOL> else : <EOL> results . not_deleted . add ( doc_id ) <EOL> return results , iter_db <EOL> class Command ( LabelCommand ) : <EOL> help = ( "<STR_LIT>" <EOL> "<STR_LIT>" <EOL> "<STR_LIT>" ) <EOL> def handle ( self , * args , ** options ) : <EOL> try : <EOL> database , ids_file = args <EOL> except ValueError : <EOL> print "<STR_LIT>" <EOL> return <EOL> slugs = [ '<STR_LIT>' ] + sorted ( filter ( None , couch_config . all_dbs_by_slug ) ) <EOL> if database not in slugs : <EOL> print ( "<STR_LIT>" <EOL> "<STR_LIT>" . format ( database , "<STR_LIT:U+002CU+0020>" . join ( slugs ) ) ) <EOL> return <EOL> slug = None if database == '<STR_LIT>' else database <EOL> db = couch_config . all_dbs_by_slug [ slug ] <EOL> try : <EOL> with open ( ids_file ) as f : <EOL> doc_ids = [ doc_id . strip ( ) for doc_id in f . readlines ( ) ] <EOL> except IOError : <EOL> print "<STR_LIT>" . format ( ids_file ) <EOL> return <EOL> msg = ( "<STR_LIT>" <EOL> . format ( len ( doc_ids ) , db . dbname ) ) <EOL> if raw_input ( msg ) != "<STR_LIT:y>" : <EOL> return <EOL> results , iter_db = undelete_docs ( db , doc_ids ) <EOL> print "<STR_LIT>" . format ( len ( results . restored ) ) <EOL> print "<STR_LIT>" . format ( len ( results . not_found ) ) <EOL> print "<STR_LIT>" . format ( len ( results . not_deleted ) ) <EOL> outfile = "<STR_LIT>" . format ( iter_db . db . dbname , datetime . now ( ) ) <EOL> with open ( outfile , '<STR_LIT:w>' ) as f : <EOL> f . write ( '<STR_LIT:\n>' . join ( get_output_file ( results , iter_db ) ) ) <EOL> print "<STR_LIT>" . format ( outfile ) <EOL> def get_output_file ( results , iter_db ) : <EOL> output = [ "<STR_LIT>" <EOL> . format ( iter_db . db . dbname , datetime . now ( ) ) ] <EOL> def write_section ( header , ids ) : <EOL> output . append ( "<STR_LIT>" . format ( header ) ) <EOL> output . extend ( ids ) <EOL> write_section ( "<STR_LIT>" , results . restored ) <EOL> write_section ( "<STR_LIT>" , results . not_found ) <EOL> write_section ( "<STR_LIT>" , results . not_deleted ) <EOL> output . append ( "<STR_LIT>" ) <EOL> write_section ( "<STR_LIT>" , iter_db . saved_ids ) <EOL> write_section ( "<STR_LIT>" , iter_db . deleted_ids ) <EOL> write_section ( "<STR_LIT>" , iter_db . error_ids ) <EOL> return output </s>
