<s> from __future__ import unicode_literals <EOL> import collections <EOL> import datetime <EOL> import six <EOL> from . parser import DateLine , EntryLine , TextLine , TimesheetParser <EOL> def synchronized ( func ) : <EOL> """<STR_LIT>""" <EOL> def wrapper ( * args ) : <EOL> if args [ <NUM_LIT:0> ] . synchronized : <EOL> return func ( * args ) <EOL> return wrapper <EOL> class EntriesCollection ( collections . defaultdict ) : <EOL> """<STR_LIT>""" <EOL> def __init__ ( self , entries = None , date_format = '<STR_LIT>' ) : <EOL> super ( EntriesCollection , self ) . __init__ ( EntriesList ) <EOL> self . lines = [ ] <EOL> self . synchronized = True <EOL> self . add_date_to_bottom = None <EOL> self . date_format = date_format <EOL> if entries : <EOL> self . synchronized = False <EOL> try : <EOL> self . init_from_str ( entries ) <EOL> finally : <EOL> self . synchronized = True <EOL> try : <EOL> self . add_date_to_bottom = self . is_top_down ( ) <EOL> except UnknownDirectionError : <EOL> pass <EOL> def __missing__ ( self , key ) : <EOL> """<STR_LIT>""" <EOL> self [ key ] = self . default_factory ( self , key ) <EOL> return self [ key ] <EOL> def __delitem__ ( self , key ) : <EOL> """<STR_LIT>""" <EOL> if self . synchronized : <EOL> self . delete_entries ( self [ key ] ) <EOL> self . delete_date ( key ) <EOL> super ( EntriesCollection , self ) . __delitem__ ( key ) <EOL> def __setitem__ ( self , key , value ) : <EOL> if key in self : <EOL> del self [ key ] <EOL> if not isinstance ( value , self . default_factory ) : <EOL> value = self . default_factory ( self , key ) <EOL> super ( EntriesCollection , self ) . __setitem__ ( key , value ) <EOL> if self . synchronized : <EOL> self . add_date ( key ) <EOL> for entry in value : <EOL> self . add_entry ( entry ) <EOL> @ synchronized <EOL> def add_entry ( self , date , entry ) : <EOL> """<STR_LIT>""" <EOL> in_date = False <EOL> insert_at = None <EOL> for ( lineno , line ) in enumerate ( self . lines ) : <EOL> if isinstance ( line , DateLine ) and line . date == date : <EOL> in_date = True <EOL> insert_at = lineno <EOL> continue <EOL> if in_date : <EOL> if isinstance ( line , EntryLine ) : <EOL> insert_at = lineno <EOL> elif isinstance ( line , DateLine ) : <EOL> break <EOL> new_line = EntryLine ( entry . alias , entry . duration , entry . description ) <EOL> entry . line = new_line <EOL> self . lines . insert ( insert_at + <NUM_LIT:1> , new_line ) <EOL> if not isinstance ( self . lines [ insert_at ] , EntryLine ) : <EOL> self . lines . insert ( insert_at + <NUM_LIT:1> , TextLine ( '<STR_LIT>' ) ) <EOL> def delete_entry ( self , entry ) : <EOL> """<STR_LIT>""" <EOL> self . delete_entries ( [ entry ] ) <EOL> @ synchronized <EOL> def delete_entries ( self , entries ) : <EOL> """<STR_LIT>""" <EOL> lines_to_delete = [ entry . line for entry in entries ] <EOL> self . lines = [ <EOL> line for line in self . lines <EOL> if not isinstance ( line , EntryLine ) or line not in lines_to_delete <EOL> ] <EOL> self . trim ( ) <EOL> @ synchronized <EOL> def delete_date ( self , date ) : <EOL> """<STR_LIT>""" <EOL> self . lines = [ <EOL> line for line in self . lines <EOL> if not isinstance ( line , DateLine ) or line . date != date <EOL> ] <EOL> self . trim ( ) <EOL> def trim ( self ) : <EOL> """<STR_LIT>""" <EOL> trim_top = None <EOL> trim_bottom = None <EOL> for ( lineno , line ) in enumerate ( self . lines ) : <EOL> if isinstance ( line , TextLine ) and not line . text . strip ( ) : <EOL> trim_top = lineno <EOL> else : <EOL> break <EOL> for ( lineno , line ) in enumerate ( reversed ( self . lines ) ) : <EOL> if isinstance ( line , TextLine ) and not line . text . strip ( ) : <EOL> trim_bottom = lineno <EOL> else : <EOL> break <EOL> if trim_top is not None : <EOL> self . lines = self . lines [ trim_top + <NUM_LIT:1> : ] <EOL> if trim_bottom is not None : <EOL> trim_bottom = len ( self . lines ) - trim_bottom - <NUM_LIT:1> <EOL> self . lines = self . lines [ : trim_bottom ] <EOL> @ synchronized <EOL> def add_date ( self , date ) : <EOL> """<STR_LIT>""" <EOL> self . trim ( ) <EOL> if self . add_date_to_bottom : <EOL> self . lines . append ( TextLine ( '<STR_LIT>' ) ) <EOL> self . lines . append ( DateLine ( date , date_format = self . date_format ) ) <EOL> else : <EOL> self . lines . insert ( <NUM_LIT:0> , TextLine ( '<STR_LIT>' ) ) <EOL> self . lines . insert ( <NUM_LIT:0> , DateLine ( date , date_format = self . date_format ) ) <EOL> self . trim ( ) <EOL> def init_from_str ( self , entries ) : <EOL> """<STR_LIT>""" <EOL> self . lines = TimesheetParser . parse ( entries ) <EOL> for line in self . lines : <EOL> if isinstance ( line , DateLine ) : <EOL> current_date = line . date <EOL> self [ current_date ] = self . default_factory ( self , line . date ) <EOL> elif isinstance ( line , EntryLine ) : <EOL> timesheet_entry = TimesheetEntry ( <EOL> line . alias , line . duration , line . description <EOL> ) <EOL> timesheet_entry . ignored = line . ignored <EOL> timesheet_entry . line = line <EOL> if len ( self [ current_date ] ) > <NUM_LIT:0> : <EOL> timesheet_entry . previous_entry = self [ current_date ] [ - <NUM_LIT:1> ] <EOL> self [ current_date ] [ - <NUM_LIT:1> ] . next_entry = timesheet_entry <EOL> self [ current_date ] . append ( timesheet_entry ) <EOL> def to_lines ( self ) : <EOL> return [ line . text for line in self . lines ] <EOL> def is_top_down ( self ) : <EOL> date_lines = [ <EOL> line for line in self . lines if isinstance ( line , DateLine ) <EOL> ] <EOL> if len ( date_lines ) < <NUM_LIT:2> or date_lines [ <NUM_LIT:0> ] . date == date_lines [ <NUM_LIT:1> ] . date : <EOL> raise UnknownDirectionError ( ) <EOL> else : <EOL> return date_lines [ <NUM_LIT:1> ] . date > date_lines [ <NUM_LIT:0> ] . date <EOL> class EntriesList ( list ) : <EOL> """<STR_LIT>""" <EOL> def __init__ ( self , entries_collection , date ) : <EOL> super ( EntriesList , self ) . __init__ ( ) <EOL> self . entries_collection = entries_collection <EOL> self . date = date <EOL> def append ( self , x ) : <EOL> """<STR_LIT>""" <EOL> super ( EntriesList , self ) . append ( x ) <EOL> if self . entries_collection is not None : <EOL> self . entries_collection . add_entry ( self . date , x ) <EOL> def __delitem__ ( self , key ) : <EOL> """<STR_LIT>""" <EOL> if self . entries_collection is not None : <EOL> self . entries_collection . delete_entry ( self [ key ] ) <EOL> super ( EntriesList , self ) . __delitem__ ( key ) <EOL> if not self and self . entries_collection is not None : <EOL> self . entries_collection . delete_date ( self . date ) <EOL> @ six . python_2_unicode_compatible <EOL> class TimesheetEntry ( object ) : <EOL> """<STR_LIT>""" <EOL> def __init__ ( self , alias , duration , description ) : <EOL> self . line = None <EOL> self . ignored = False <EOL> self . commented = False <EOL> self . previous_entry = None <EOL> self . next_entry = None <EOL> self . alias = alias <EOL> self . description = description <EOL> self . duration = duration <EOL> def __str__ ( self ) : <EOL> if self . is_ignored ( ) : <EOL> project_name = u'<STR_LIT>' % self . alias <EOL> else : <EOL> project_name = self . alias <EOL> return u'<STR_LIT>' % ( project_name , self . hours , <EOL> self . description ) <EOL> def __setattr__ ( self , name , value ) : <EOL> """<STR_LIT>""" <EOL> super ( TimesheetEntry , self ) . __setattr__ ( name , value ) <EOL> if self . line is not None : <EOL> if hasattr ( self . line , name ) : <EOL> setattr ( self . line , name , value ) <EOL> @ property <EOL> def hash ( self ) : <EOL> return u'<STR_LIT>' % ( <EOL> self . alias , <EOL> self . description , <EOL> self . ignored <EOL> ) <EOL> def is_ignored ( self ) : <EOL> return self . ignored or self . hours == <NUM_LIT:0> <EOL> def get_start_time ( self ) : <EOL> """<STR_LIT>""" <EOL> if not isinstance ( self . duration , tuple ) : <EOL> return None <EOL> if self . duration [ <NUM_LIT:0> ] is not None : <EOL> return self . duration [ <NUM_LIT:0> ] <EOL> else : <EOL> if ( self . previous_entry and <EOL> isinstance ( self . previous_entry . duration , tuple ) and <EOL> self . previous_entry . duration [ <NUM_LIT:1> ] is not None ) : <EOL> return self . previous_entry . duration [ <NUM_LIT:1> ] <EOL> return None <EOL> @ property <EOL> def hours ( self ) : <EOL> """<STR_LIT>""" <EOL> if not isinstance ( self . duration , tuple ) : <EOL> return self . duration <EOL> if self . duration [ <NUM_LIT:1> ] is None : <EOL> return <NUM_LIT:0> <EOL> time_start = self . get_start_time ( ) <EOL> if time_start is None : <EOL> return <NUM_LIT:0> <EOL> now = datetime . datetime . now ( ) <EOL> time_start = now . replace ( <EOL> hour = time_start . hour , <EOL> minute = time_start . minute , second = <NUM_LIT:0> <EOL> ) <EOL> time_end = now . replace ( <EOL> hour = self . duration [ <NUM_LIT:1> ] . hour , <EOL> minute = self . duration [ <NUM_LIT:1> ] . minute , second = <NUM_LIT:0> <EOL> ) <EOL> total_time = time_end - time_start <EOL> total_hours = total_time . seconds / <NUM_LIT> <EOL> return total_hours <EOL> def fix_start_time ( self ) : <EOL> """<STR_LIT>""" <EOL> if ( isinstance ( self . duration , tuple ) and self . duration [ <NUM_LIT:0> ] is None <EOL> and self . previous_entry is not None <EOL> and self . previous_entry . commented <EOL> and not self . commented ) : <EOL> self . duration = ( <EOL> self . get_start_time ( ) , <EOL> self . duration [ <NUM_LIT:1> ] <EOL> ) <EOL> @ six . python_2_unicode_compatible <EOL> class AggregatedTimesheetEntry ( object ) : <EOL> """<STR_LIT>""" <EOL> def __init__ ( self ) : <EOL> super ( AggregatedTimesheetEntry , self ) . __setattr__ ( '<STR_LIT>' , [ ] ) <EOL> def __getattr__ ( self , name ) : <EOL> if not self . entries : <EOL> raise AttributeError ( ) <EOL> if hasattr ( self . entries [ <NUM_LIT:0> ] , name ) : <EOL> return getattr ( self . entries [ <NUM_LIT:0> ] , name ) <EOL> else : <EOL> raise AttributeError ( ) <EOL> def __setattr__ ( self , name , value ) : <EOL> for entry in self . entries : <EOL> setattr ( entry , name , value ) <EOL> def __str__ ( self ) : <EOL> if self . is_ignored ( ) : <EOL> project_name = u'<STR_LIT>' % self . alias <EOL> else : <EOL> project_name = self . alias <EOL> return u'<STR_LIT>' % ( project_name , self . hours , <EOL> self . description ) <EOL> @ property <EOL> def hours ( self ) : <EOL> return sum ( [ entry . hours for entry in self . entries ] ) <EOL> class UnknownDirectionError ( Exception ) : <EOL> pass </s>
