# Path: vasppy/summary.py
# def load_vasp_summary( filename ):
# def potcar_spec(filename, return_hashes=False):
# def find_vasp_calculations():
#     def __init__( self, directory='.' ):
#     def parse_vasprun( self ):
#     def stoich( self ):
#     def functional( self ):
#     def potcars_are_pbe( self ):
#     def output( self, to_print ):
#     def print_type( self ):
#     def print_title( self ):
#     def print_description( self ):
#     def print_notes( self ):
#     def print_status( self ):
#     def print_lreal( self ):
#     def print_stoichiometry( self ):
#     def print_potcar( self ):
#     def print_energy( self ):
#     def print_neb_energy( self ):
#     def print_version( self ):
#     def print_eatom( self ):
#     def print_kpoints( self ):
#     def print_functional( self ):
#     def print_ibrion( self ):
#     def print_ediffg( self ):
#     def print_encut( self ):
#     def print_converged( self ):
#     def print_vasprun_md5( self ):
#     def print_file_tracking( self ):
#     def print_directory( self ):
#     def print_plus_u( self ):
#     def print_cbm( self ):
#     def print_vbm( self ):
#     def print_nelect( self ):
# class Summary:
# 
# Path: vasppy/vaspmeta.py
# class VASPMeta:
#     """
#     VASPMeta class for storing additional VASP calculation metadata
#     """
# 
#     def __init__( self, title, description, status, notes=None, type=None, track=None ):
#         """
#         Initialise a VASPMeta object.
# 
#         Args:
#             title (Str): The title string for this calculation
#             description (Str): Long description
#             status (Str): Current status of the calculation. 
#                 Expected strings are (to-run, incomplete, finished, dropped)
#             notes (:obj:Str, optional): Any additional notes. Defaults to None.
#             type  (:obj:Str, optional): Can be used to describe the calculation type. 
#                 Defaults to None.
#             track (:obj:dict(str: str), optional): An optional dict of pairs of filenames
#                 for files to track. For each key: value pair, the key is the current filename
#                 in the directory. The value is the new filename that list of filenames to calculate md5 hashes
#                 files to calculate hashes for when summarising the calculation output.
#                 Defaults to None.
# 
#         Returns:
#             None
#         """
#         self.title = title
#         self.description = description
#         self.notes = notes
#         expected_status = [ 'to-run', 'incomplete', 'finished', 'dropped' ]
#         if status not in expected_status:
#             raise ValueError(f'Unexpected calculations status: "{status}"'
#                              f' for calculation {title}')
#         self.status = status
#         expected_types = [ 'single-point', 'neb' ]
#         if type:
#             if type not in expected_types:
#                 raise ValueError(f'Unexpected calculation type: "{type}"'
#                                  f' for calculation {title}')
#             self.type = type 
#         else:
#             self.type = None
#         self.track = track
# 
#     @classmethod
#     def from_file( cls, filename ):
#         """
#         Create a VASPMeta object by reading a `vaspmeta.yaml` file
# 
#         Args:
#             filename (Str): filename to read in.
# 
#         Returns:
#             (vasppy.VASPMeta): the VASPMeta object
#         """
#         with open( filename, 'r' ) as stream:
#             data = yaml.load( stream, Loader=yaml.SafeLoader )
#             notes = data.get( 'notes' )
#             v_type = data.get( 'type' )
#             track = data.get( 'track' )
#             xargs = {}
#             if track:
#                 if type( track ) is str:
#                     track = [ track ]
#                 xargs['track'] = track 
#             vaspmeta = VASPMeta( data['title'], 
#                                  data['description'], 
#                                  data['status'], 
#                                  notes=notes, 
#                                  type=v_type,
#                                  **xargs )
#         return vaspmeta


import unittest
import numpy as np
import io
import inspect
import os
from unittest.mock import Mock, patch, call
from io import StringIO
from vasppy.summary import (Summary, md5sum, potcar_spec, find_vasp_calculations,
                            load_vasp_summary)
from vasppy.vaspmeta import VASPMeta
from pymatgen.io.vasp.outputs import Vasprun

    @patch('sys.stdout', new_callable=StringIO)
    def test_print_notes_handles_empty_notes_attribute( self, mock_stdout ):
        self.summary.print_notes()
        self.assertEqual( mock_stdout.getvalue(), 'notes: ~\n' )

class SummaryHelperFunctionsTestCase( unittest.TestCase ):

    def test_md5sum( self ):
        self.assertEqual( md5sum('hello\n'), 'b1946ac92492d2347c6235b4d2611184' )

    def test_potcar_spec(self):
        mock_potcar_filename = 'POTCAR'
        md5sum_return_values = ('12', '56', '23') 
        with patch('builtins.open', return_value=io.StringIO(mock_potcar_string)) as mock_open:
            with patch('vasppy.summary.md5sum', side_effect=md5sum_return_values) as mock_md5sum:
                with patch.dict('vasppy.data.potcar_data.potcar_md5sum_data', mock_potcar_data, clear=True):
                    p_spec = potcar_spec(mock_potcar_filename)
                    mock_open.assert_called_with(mock_potcar_filename, 'r')
                    mock_md5sum.assert_has_calls([ call('foo\nEnd of Dataset\n'), 
                                                   call('bar\nEnd of Dataset\n'),
                                                   call('sds\nEnd of Dataset\n')])
        self.assertEqual( p_spec, {'A': 'PBE', 'E': 'PBE_54', 'D': 'PBE_52'} )

    def test_potcar_spec_returns_hashes(self):
        mock_potcar_filename = 'POTCAR'
        md5sum_return_values = ('12', '56', '23')
        with patch('builtins.open', return_value=io.StringIO(mock_potcar_string)) as mock_open:
            with patch('vasppy.summary.md5sum', side_effect=md5sum_return_values) as mock_md5sum:
                with patch.dict('vasppy.data.potcar_data.potcar_md5sum_data', mock_potcar_data, clear=True):

# Path: vasppy/summary.py
# def load_vasp_summary( filename ):
# def potcar_spec(filename, return_hashes=False):
# def find_vasp_calculations():
#     def __init__( self, directory='.' ):
#     def parse_vasprun( self ):
#     def stoich( self ):
#     def functional( self ):
#     def potcars_are_pbe( self ):
#     def output( self, to_print ):
#     def print_type( self ):
#     def print_title( self ):
#     def print_description( self ):
#     def print_notes( self ):
#     def print_status( self ):
#     def print_lreal( self ):
#     def print_stoichiometry( self ):
#     def print_potcar( self ):
#     def print_energy( self ):
#     def print_neb_energy( self ):
#     def print_version( self ):
#     def print_eatom( self ):
#     def print_kpoints( self ):
#     def print_functional( self ):
#     def print_ibrion( self ):
#     def print_ediffg( self ):
#     def print_encut( self ):
#     def print_converged( self ):
#     def print_vasprun_md5( self ):
#     def print_file_tracking( self ):
#     def print_directory( self ):
#     def print_plus_u( self ):
#     def print_cbm( self ):
#     def print_vbm( self ):
#     def print_nelect( self ):
# class Summary:
# 
# Path: vasppy/vaspmeta.py
# class VASPMeta:
#     """
#     VASPMeta class for storing additional VASP calculation metadata
#     """
# 
#     def __init__( self, title, description, status, notes=None, type=None, track=None ):
#         """
#         Initialise a VASPMeta object.
# 
#         Args:
#             title (Str): The title string for this calculation
#             description (Str): Long description
#             status (Str): Current status of the calculation. 
#                 Expected strings are (to-run, incomplete, finished, dropped)
#             notes (:obj:Str, optional): Any additional notes. Defaults to None.
#             type  (:obj:Str, optional): Can be used to describe the calculation type. 
#                 Defaults to None.
#             track (:obj:dict(str: str), optional): An optional dict of pairs of filenames
#                 for files to track. For each key: value pair, the key is the current filename
#                 in the directory. The value is the new filename that list of filenames to calculate md5 hashes
#                 files to calculate hashes for when summarising the calculation output.
#                 Defaults to None.
# 
#         Returns:
#             None
#         """
#         self.title = title
#         self.description = description
#         self.notes = notes
#         expected_status = [ 'to-run', 'incomplete', 'finished', 'dropped' ]
#         if status not in expected_status:
#             raise ValueError(f'Unexpected calculations status: "{status}"'
#                              f' for calculation {title}')
#         self.status = status
#         expected_types = [ 'single-point', 'neb' ]
#         if type:
#             if type not in expected_types:
#                 raise ValueError(f'Unexpected calculation type: "{type}"'
#                                  f' for calculation {title}')
#             self.type = type 
#         else:
#             self.type = None
#         self.track = track
# 
#     @classmethod
#     def from_file( cls, filename ):
#         """
#         Create a VASPMeta object by reading a `vaspmeta.yaml` file
# 
#         Args:
#             filename (Str): filename to read in.
# 
#         Returns:
#             (vasppy.VASPMeta): the VASPMeta object
#         """
#         with open( filename, 'r' ) as stream:
#             data = yaml.load( stream, Loader=yaml.SafeLoader )
#             notes = data.get( 'notes' )
#             v_type = data.get( 'type' )
#             track = data.get( 'track' )
#             xargs = {}
#             if track:
#                 if type( track ) is str:
#                     track = [ track ]
#                 xargs['track'] = track 
#             vaspmeta = VASPMeta( data['title'], 
#                                  data['description'], 
#                                  data['status'], 
#                                  notes=notes, 
#                                  type=v_type,
#                                  **xargs )
#         return vaspmeta


# Path: tests/test_summary.py
import unittest
import numpy as np
import io
import inspect
import os
from unittest.mock import Mock, patch, call
from io import StringIO
from vasppy.summary import (Summary, md5sum, potcar_spec, find_vasp_calculations,
                            load_vasp_summary)
from vasppy.vaspmeta import VASPMeta
from pymatgen.io.vasp.outputs import Vasprun


    @patch('sys.stdout', new_callable=StringIO)
    def test_print_notes_handles_empty_notes_attribute( self, mock_stdout ):
        self.summary.print_notes()
        self.assertEqual( mock_stdout.getvalue(), 'notes: ~\n' )

class SummaryHelperFunctionsTestCase( unittest.TestCase ):

    def test_md5sum( self ):
        self.assertEqual( md5sum('hello\n'), 'b1946ac92492d2347c6235b4d2611184' )

    def test_potcar_spec(self):
        mock_potcar_filename = 'POTCAR'
        md5sum_return_values = ('12', '56', '23') 
        with patch('builtins.open', return_value=io.StringIO(mock_potcar_string)) as mock_open:
            with patch('vasppy.summary.md5sum', side_effect=md5sum_return_values) as mock_md5sum:
                with patch.dict('vasppy.data.potcar_data.potcar_md5sum_data', mock_potcar_data, clear=True):
                    p_spec = potcar_spec(mock_potcar_filename)
                    mock_open.assert_called_with(mock_potcar_filename, 'r')
                    mock_md5sum.assert_has_calls([ call('foo\nEnd of Dataset\n'), 
                                                   call('bar\nEnd of Dataset\n'),
                                                   call('sds\nEnd of Dataset\n')])
        self.assertEqual( p_spec, {'A': 'PBE', 'E': 'PBE_54', 'D': 'PBE_52'} )

    def test_potcar_spec_returns_hashes(self):
        mock_potcar_filename = 'POTCAR'
        md5sum_return_values = ('12', '56', '23')
        with patch('builtins.open', return_value=io.StringIO(mock_potcar_string)) as mock_open:
            with patch('vasppy.summary.md5sum', side_effect=md5sum_return_values) as mock_md5sum:
                with patch.dict('vasppy.data.potcar_data.potcar_md5sum_data', mock_potcar_data, clear=True):

                    p_spec = potcar_spec(mock_potcar_filename, return_hashes=True)