import dynpy
from dynpy.utilities.report import ReportText
from .report import Markdown
from .documents.document import ODESystemOverviewReport
from github import Github
import getpass

import pint
import inspect
from dynpy.utilities.documents import document
from dynpy.utilities.templates.document import Guide
from datetime import datetime

issue_no = 567
title = 'implementation of overview report for `ODESystem` child classes'
guide_class = ODESystemOverviewReport

meet_issue_code_str = """The following actions are to do, in order to prepare the meeting and the guide:

- [{tic}] #{issue_no+1}

- [{tic}] #{issue_no+2}

- [{tic}] #{issue_no+3}

"""


meet_time_issue_code_str = """The goal is to schedule the meeting in Teams calendar on {date} at {time}.

"""

meet_arrange_issue_code_str ="""The goal is to arrange and execute the meeting for {title}. The following things should be prepared:

- [{tic}] scope and schedule of meeting (in one week);

- [{tic}] conducting team (in two weeks);

- [{tic}] guide or instructions with examples (one week before meeting);

- [{tic}] announcement (in Slack and Teams channel) with basic information (conducting persons, date,scope,schedule) (one week before meeting);

- [{tic}] exemplary code in Jupyter (link) (one week before meeting).

"""


meet_guide_issue_code_str ="""The goal is to prepare or improve a guide or report that is generated by the following code:

```python
from {guide_class_module} import {guide_class_name}
{guide_class_name}()
```

"""

class MeetingIssueCreator:

    """

    Example:
    from dynpy.utilities.creators import MeetingIssueCreator
    from dynpy.utilities.documents.document import IntroDynPyProjectGuidelines
    title="Issue"
    last_issue_no=533
    MeetingIssueCreator(title=title,no=last_issue_no+1,guide=IntroDynPyProjectGuidelines,date='2024.09.26',time='20:30',done=False)

    """

    _title = 'implementation of ODESystem overview report'
    _issue_no = 567
    _guide = ODESystemOverviewReport
    _time = '20:30'
    _date = '2024.07.27'
    _assignees = ['bogumilchilinski']

    def __init__(self,title=None,no=None,guide=None,date=None,time=None,assignees=None,done=False,*args,**kwargs):
        if title is not None: self._title = title
        if no is not None: self._issue_no = no
        if guide is not None: self._guide = guide
        if date is not None: self._date= date
        if time is not None: self._time = time
        self._done = done
        if assignees is not None: self._assignees = assignees

    def _get_elems_dict(self):

        if self._done: tic = 'x'
        else: tic = ' '

        guide_class = self._guide

        elems_dict = {
                'title':self._title,
                'issue_no':self._issue_no,
                'issue_no+1':self._issue_no+1,
                'issue_no+2':self._issue_no+2,
                'issue_no+3':self._issue_no+3,
                'guide_class_module':guide_class.__module__,
                'guide_class_name':guide_class.__name__,
                'date':self._date,
                'time':self._time,
                'tic':tic
                }

        return elems_dict

    def get_issues_titles(self):

        # Issue no. #{issue_no}
        ## Preparation of meeting and guide for {title}

        issue_no = self._issue_no
        title = self._title

        titles_dict =   {
                        'meet':f'Preparation of meeting and guide for {title}',
                        'time':f'Scope and time scheduling of the meeting for {title} (issue #{issue_no} related)',
                        'arrange':f'Arrangement and execution of the meeting for {title} (issue #{issue_no} related)',
                        'guide':f'Preparation or improvement of the guide for {title} (issue #{issue_no} related)',
                        }

        return titles_dict

    def get_issue_str(self):
        elems_dict = self._get_elems_dict()
        issue_no = self._issue_no

        titles = self.get_issues_titles()

        meet_issue_dict = self.get_meet_issue_dict()
        time_issue_dict = self.get_time_issue_dict()
        arrange_issue_dict = self.get_arrange_issue_dict()
        guide_issue_dict = self.get_guide_issue_dict()


        issue_code_str = (f'# Issue no. #{issue_no} \n '+
                        '## '+meet_issue_dict['title'] + '\n ' +
                        meet_issue_dict['body'] + '\n ' +
                        f'# Issue no. #{issue_no+1} \n '+
                        '## '+ time_issue_dict['title'] + '\n ' +
                        time_issue_dict['body'] + '\n ' +
                        f'# Issue no. #{issue_no+2} \n '+
                        '## '+arrange_issue_dict['title'] + '\n ' +
                        arrange_issue_dict['body'] + '\n ' +
                        f'# Issue no. #{issue_no+3} \n ' +
                        '## '+guide_issue_dict['title'] + '\n ' +
                        guide_issue_dict['body'])

        return issue_code_str


    def get_meet_issue_dict(self):
        elems_dict = self._get_elems_dict()
        titles = self.get_issues_titles()

        return {'title':titles['meet'],'body':meet_issue_code_str.format(**elems_dict)}

    def get_time_issue_dict(self):
        elems_dict = self._get_elems_dict()
        titles = self.get_issues_titles()

        return {'title':titles['time'],'body':meet_time_issue_code_str.format(**elems_dict)}


    def get_arrange_issue_dict(self):
        elems_dict = self._get_elems_dict()
        titles = self.get_issues_titles()

        return {'title':titles['arrange'],'body':meet_arrange_issue_code_str.format(**elems_dict)}

    def get_guide_issue_dict(self):
        elems_dict = self._get_elems_dict()
        titles = self.get_issues_titles()

        return {'title':titles['guide'],'body':meet_guide_issue_code_str.format(**elems_dict)}

    def _repr_markdown_(self):
        return self.get_issue_str()

    @property
    def closing_comment_text_meet(self):
        
        # obj_class_module= self._obj.__module__
        # obj_class_name=self._obj.__name__
        
        comment_str_h1 = '> *Exemplary comment. You can copy example below and close issue with new comment or modify it and close the issue.* '
        comment_str_h2 = '*Remove this message or keep it if new comment is created.*'
        
        comment_str_2 = 'All done.'

        
        return f'{comment_str_h1}{comment_str_h2}\n\n{comment_str_2}'


    @property
    def closing_comment_text_time(self):
        
        # obj_class_module= self._obj.__module__
        # obj_class_name=self._obj.__name__
        
        comment_str_h1 = '> *Exemplary comment. You can copy example below and close issue with new comment or modify it and close the issue.* '
        comment_str_h2 = '*Remove this message or keep it if new comment is created.*'
        
        comment_str_2 = 'Meeting has been scheduled.'
        
        return f'{comment_str_h1}{comment_str_h2}\n\n{comment_str_2}'

    @property
    def closing_comment_text_arrange(self):
        
        # obj_class_module= self._obj.__module__
        # obj_class_name=self._obj.__name__
        
        comment_str_h1 = '> *Exemplary comment. You can copy example below and close issue with new comment or modify it and close the issue.* '
        comment_str_h2 = '*Remove this message or keep it if new comment is created.*'
        
        comment_str_2 = 'Meeting has been executed.'
        
        return f'{comment_str_h1}{comment_str_h2}\n\n{comment_str_2}'

    @property
    def closing_comment_text_guide(self):
        
        
        obj_class_module= self._guide.__module__
        obj_class_name=self._guide.__name__
        
        comment_str_h1 = '> *Exemplary comment. You can copy example below and close issue with new comment or modify it and close the issue.* '
        comment_str_h2 = '*Remove this message or keep it if new comment is created.*'
        
        comment_str_2 = 'Minor changes introduced. The code was checked with the following call:'
        comment_call_code = f'```python \nfrom {obj_class_module} import {obj_class_name} \n{obj_class_name}(None) \n```'
        
        return f'{comment_str_h1}{comment_str_h2}\n\n{comment_str_2}\n{comment_call_code}'


    def publish_on_github(self,repository):

        guide_label=repository.get_label('guidance')
        assignees=self._assignees

        issue_meet=repository.create_issue(**self.get_meet_issue_dict(),labels=[guide_label],assignees=assignees
                                )
        issue_time=repository.create_issue(**self.get_time_issue_dict(),labels=[guide_label],assignees=['bogumilchilinski'])
        issue_arrange=repository.create_issue(**self.get_arrange_issue_dict(),labels=[guide_label],assignees=assignees
                                )
        issue_guide=repository.create_issue(**self.get_guide_issue_dict(),labels=[guide_label],assignees=assignees
                                )

        issue_guide.create_comment(self.closing_comment_text_guide)
        issue_arrange.create_comment(self.closing_comment_text_arrange)
        issue_time.create_comment(self.closing_comment_text_time)
        issue_meet.create_comment(self.closing_comment_text_meet)

        return issue_meet,issue_time,issue_arrange,issue_guide





class HelpImplementationIssueCreator:

    """
    Example:
    from dynpy.utilities.creators import MeetingIssueCreator
    from dynpy.utilities.documents.document import IntroDynPyProjectGuidelines
    title="Issue"
    last_issue_no=533
    MeetingIssueCreator(title=title,no=last_issue_no+1,guide=IntroDynPyProjectGuidelines,date='2024.09.26',time='20:30',done=False)

    """
    
    _obj = ODESystemOverviewReport
    _issue_no = 567
    _guide = ODESystemOverviewReport
    _time = '20:30'
    _date = '2024.07.27'
    _assignees = ['bogumilchilinski']
    _module = 'exemplary.module'
    _default_labels = ['documentation','enhancement','maintenance']
    _issue_type = 'Help'
    _goal = 'creates a part of a report'

    def __init__(self,obj=None,goal=None,module=None,no=None,assignees=None,done=False,*args,**kwargs):
        if obj is not None: self._obj = obj
        if no is not None: self._issue_no = no
        if module is not None: self._module = module
        if goal is not None: self._goal = goal

        self._done = done
        if assignees is not None: self._assignees = assignees

    def _get_elems_dict(self):

        if self._done: tic = 'x'
        else: tic = ' '

        obj = self._obj

        elems_dict = {
                'obj':obj,
                'goal':self._goal,
                'issue_no':self._issue_no,
                'issue_no+1':self._issue_no+1,
                'issue_no+2':self._issue_no+2,
                'issue_no+3':self._issue_no+3,
                'obj_class_module':obj.__module__,
                'obj_class_name':obj.__name__,
                # 'date':self._date,
                # 'time':self._time,
                'tic':tic
                }

        return elems_dict

    @property
    def title(self):
        
        issue_no = self._issue_no
        obj_class_module= self._obj.__module__
        obj_class_name=self._obj.__name__
        
        return f'Help for `{obj_class_name}` class from `{obj_class_module}` module'

    @property
    def body_text(self):
        
        issue_no = self._issue_no
        obj_class_module= self._obj.__module__
        obj_class_name=self._obj.__name__
        
        issue_code_str=(f'Help *docstring* is to implement for `{obj_class_name}` class that is defined in `{obj_class_module}`. '+
        'It should list of all available information about class.')
        
        return issue_code_str

    @property
    def closing_comment_text(self):
        
        obj_class_module= self._obj.__module__
        obj_class_name=self._obj.__name__
        
        comment_str_h1 = '> *Exemplary comment. You can copy example below and close issue with new comment or modify it and close the issue.* '
        comment_str_h2 = '*Remove this message or keep it if new comment is created.*'
        
        comment_str_2 = 'Help *docstring* added. \n\nThe code was checked with the following call:'
        comment_call_code = f'```python \nfrom {obj_class_module} import {obj_class_name} \n{obj_class_name}() \n```'
        
        return f'{comment_str_h1}{comment_str_h2}\n\n{comment_str_2}\n{comment_call_code}'



    def get_issues_titles(self):

        # Issue no. #{issue_no}
        ## Preparation of meeting and guide for {title}



        titles_dict =   {
                        'issue':self.title,
                        }

        return titles_dict

    def get_issue_str(self):
        elems_dict = self._get_elems_dict()
        issue_no = self._issue_no

        titles = self.get_issues_titles()

        issue_dict = self.get_issue_dict()

        issue_code_str = (f'# Issue no. #{issue_no} \n '+
                        '## '+issue_dict['title'] + '\n ' +
                        issue_dict['body'] + '\n ' +
                        '### Closing comment (example)' + '\n ' +
                        self.closing_comment_text
                        )

        return issue_code_str



    def get_issue_dict(self):
        elems_dict = self._get_elems_dict()
        titles = self.get_issues_titles()

        issue_code_str=('Help *docstring* is to implement for `{obj_class_name}` class that is defined in `{obj_class_module}`. '+
        'It should list of all available information about class.')

        return {'title':self.title,'body':self.body_text.format(**elems_dict)}


    def _repr_markdown_(self):
        return self.get_issue_str()


    def publish_on_github(self,repository,labels=[]):

        labels = self._default_labels+labels

        issue_labels=[repository.get_label(label_arg) for label_arg in labels]
        assignees=self._assignees

        issue_help=repository.create_issue(**self.get_issue_dict(),labels=issue_labels,assignees=assignees
                                )
        
        issue_help.create_comment(self.closing_comment_text)

        return issue_help


class ReportComponentImplementationIssueCreator(HelpImplementationIssueCreator):
    
    _default_labels = ['documentation','enhancement','guide','maintenance','reporting']
    _issue_type = 'Implementation'
    _goal = 'creates a part of a report'
    
    @property
    def title(self):
        
        issue_no = self._issue_no
        obj_class_module= self._obj.__module__
        obj_class_name=self._obj.__name__
        goal = self._goal
        
        issue_type = self._issue_type
        
        return f'{issue_type} of `{obj_class_name}` class that {goal} (defined in `{obj_class_module}` module)'

    @property
    def body_text(self):
        
        issue_no = self._issue_no
        obj_class_module= self._obj.__module__
        obj_class_name=self._obj.__name__
        goal = self._goal
        
        

        issue_code_str=(f'`{obj_class_name}` class (report component) is to implement in `{obj_class_module}` module. '+
        'It should provide code that {goal}.')
        
        return issue_code_str

    @property
    def closing_comment_text(self):
        
        obj_class_module= self._obj.__module__
        obj_class_name=self._obj.__name__
        
        comment_str_h1 = '> *Exemplary comment. You can copy example below and close issue with new comment or modify it and close the issue.* '
        comment_str_h2 = '*Remove this message or keep it if new comment is created.*'
        
        comment_str_2 = 'The code was checked with the following call:'
        comment_call_code = f'```python \nfrom {obj_class_module} import {obj_class_name} \n{obj_class_name}(None) \n```'
        
        return f'{comment_str_h1}{comment_str_h2}\n\n{comment_str_2}\n{comment_call_code}'


class ReportComponentDevelopmentIssueCreator(HelpImplementationIssueCreator):
    
    _default_labels = ['documentation','enhancement','guide','maintenance','reporting']
    _issue_type = 'Development'
    _goal = 'creates a part of a report'
    
    @property
    def title(self):
        
        issue_no = self._issue_no
        obj_class_module= self._obj.__module__
        obj_class_name=self._obj.__name__
        goal = self._goal
        
        issue_type = self._issue_type
        
        return f'{issue_type} of `{obj_class_name}` class that {goal} (defined in `{obj_class_module}` module)'

    @property
    def body_text(self):
        
        issue_no = self._issue_no
        obj_class_module= self._obj.__module__
        obj_class_name=self._obj.__name__
        goal = self._goal
        
        

        issue_code_str=(f'`{obj_class_name}` class (report component) is to develop in `{obj_class_module}` module. '+
        'It should provide code that {goal}.')
        
        return issue_code_str

    @property
    def closing_comment_text(self):
        
        obj_class_module= self._obj.__module__
        obj_class_name=self._obj.__name__
        
        comment_str_h1 = '> *Exemplary comment. You can copy example below and close issue with new comment or modify it and close the issue.* '
        comment_str_h2 = '*Remove this message or keep it if new comment is created.*'
        
        comment_str_2 = 'The code was checked with the following call:'
        comment_call_code = f'```python \nfrom {obj_class_module} import {obj_class_name} \n{obj_class_name}(None) \n```'
        
        return f'{comment_str_h1}{comment_str_h2}\n\n{comment_str_2}\n{comment_call_code}'


class SyncIssueCreator(ReportComponentDevelopmentIssueCreator):
    
    _default_labels = ['daily','maintenance','sync']
    _issue_type = 'Sync'
    _goal = 'creates a part of a report'
 
    def _get_elems_dict(self):

        if self._done: tic = 'x'
        else: tic = ' '

        obj = self._obj

        elems_dict = {
                'obj':obj,
                'goal':self._goal,
                'issue_no':self._issue_no,
                'issue_no+1':self._issue_no+1,
                'issue_no+2':self._issue_no+2,
                'issue_no+3':self._issue_no+3,
                # 'obj_class_module':obj.__module__,
                # 'obj_class_name':obj.__name__,
                # 'date':self._date,
                # 'time':self._time,
                'tic':tic
                }

        return elems_dict
 
    
    @property
    def title(self):
        
        # issue_no = self._issue_no
        # obj_class_module= self._obj.__module__
        # obj_class_name=self._obj.__name__
        # goal = self._goal
        date = self._obj
        
        issue_type = self._issue_type
        
        return f'{issue_type} of GitHub repositories within {date}'

    @property
    def body_text(self):
        
        # issue_no = self._issue_no
        # obj_class_module= self._obj.__module__
        # obj_class_name=self._obj.__name__
        # goal = self._goal
        
        

        issue_code_str=(f'The aim is to synchronize GitHub repositories in the given period.')
        
        return issue_code_str

    @property
    def closing_comment_text(self):
        
        # obj_class_module= self._obj.__module__
        # obj_class_name=self._obj.__name__
        
        comment_str_h1 = '> *Exemplary comment. You can copy example below and close issue with new comment or modify it and close the issue.* '
        comment_str_h2 = '*Remove this message or keep it if new comment is created.*'
        
        comment_str_2 = 'Repositories are synchronised. Goal is achieved.'
        
        
        return f'{comment_str_h1}{comment_str_h2}\n\n{comment_str_2}'


class ClassImplementationIssueCreator(HelpImplementationIssueCreator):
    
    _default_labels = ['enhancement','module']
    _issue_type = 'Implementation'
    _goal = 'creates a part of a report'
    
    @property
    def title(self):
        
        issue_no = self._issue_no
        obj_class_module= self._obj.__module__
        obj_class_name=self._obj.__name__
        goal = self._goal
        
        issue_type = self._issue_type
        
        return f'{issue_type} of `{obj_class_name}` class that {goal} (defined in `{obj_class_module}` module)'

    @property
    def body_text(self):
        
        issue_no = self._issue_no
        obj_class_module= self._obj.__module__
        obj_class_name=self._obj.__name__
        goal = self._goal
        
        

        issue_code_str=(f'`{obj_class_name}` class is to implement in `{obj_class_module}` module. '+
        'It should provide code that {goal}.')
        
        return issue_code_str

    @property
    def closing_comment_text(self):
        
        obj_class_module= self._obj.__module__
        obj_class_name=self._obj.__name__
        
        comment_str_h1 = '> *Exemplary comment. You can copy example below and close issue with new comment or modify it and close the issue.* '
        comment_str_h2 = '*Remove this message or keep it if new comment is created.*'
        
        comment_str_2 = 'The code was checked with the following call:'
        comment_call_code = f'```python \nfrom {obj_class_module} import {obj_class_name} \n{obj_class_name}(None) \n```'
        
        return f'{comment_str_h1}{comment_str_h2}\n\n{comment_str_2}\n{comment_call_code}'

class MethodImplementationIssueCreator(HelpImplementationIssueCreator):
    
    _default_labels = ['enhancement','module']
    _issue_type = 'Implementation'
    _goal = 'creates a part of a report'
    
    @property
    def title(self):
        
        issue_no = self._issue_no
        obj_class_module= self._obj.__module__
        obj_class_name=self._obj.__qualname__
        goal = self._goal
        
        issue_type = self._issue_type
        
        return f'{issue_type} of `{obj_class_name}` method that {goal} (defined in `{obj_class_module}` module)'

    @property
    def body_text(self):
        
        issue_no = self._issue_no
        obj_class_module= self._obj.__module__
        obj_class_name=self._obj.__qualname__
        goal = self._goal
        
        

        issue_code_str=(f'`{obj_class_name}` method is to implement in `{obj_class_module}` module. '+
        'It should provide code that {goal}.')
        
        return issue_code_str

    @property
    def closing_comment_text(self):
        
        obj_class_module= self._obj.__module__
        obj_class_name=self._obj.__qualname__
        
        comment_str_h1 = '> *Exemplary comment. You can copy example below and close issue with new comment or modify it and close the issue.* '
        comment_str_h2 = '*Remove this message or keep it if new comment is created.*'
        
        comment_str_2 = 'The code was checked with the following call:'
        comment_call_code = f'```python \nfrom {obj_class_module} import {obj_class_name} \n{obj_class_name}(None) \n```'
        
        return f'{comment_str_h1}{comment_str_h2}\n\n{comment_str_2}\n{comment_call_code}'



class GitHubInterface():

    '''
    GitHubInterface is a class to use the Github API v3. With it, you can manage your Github resources (repositories, user profiles, organizations, etc.) from Python scripts.
    
    It is based on Github class from PyGithub library and it mimics its methods.

    Examples:

    from dynpy.utilities.creators import GitHubInterface

    # Authentication is defined via github.Auth
    # using an access token to Public Web Github

    client = GitHubInterface()

    repos = client.get_issues_list(repo_name="bogumilchilinski/dynpy", state='open', assignee='amvdek')

    '''

    _repo_name= "bogumilchilinski/dynpy"

    def __init__(self, token=None, repo_name=None):
        
        from github import Auth

        if token is None:
            pass_code = getpass.getpass('Github token')
            auth = Auth.Token(pass_code)
        else:
            auth = Auth.Token(token)


        g = Github(auth=auth)
        self.g = g

        if repo_name is not None:
            self._repo_name = repo_name
            

    def open(self):
        '''
        This method opens the connection with GitHub API by entering the token.

        Example:

        client = GitHubInterface()

        client.close()

        client.open()

        '''
        
        from github import Auth
        
        if self.g is None:
            pass_code = getpass.getpass('Github token')
            auth = Auth.Token(pass_code)
            g = Github(auth=auth)
            self.g = g
        else:
            pass

    def get_repos_list(self, string=False):
        '''
        This method returns the list of the repositories in form of Repository classes or in string type depending on boolean value of string argument.

        Example:

        client = GitHubInterface()

        client.get_repos_list(string=True)

        '''

        if string == False:
            return list(self.g.get_user().get_repos())
        else:
            repo_list=[]
            for repo in list(self.g.get_user().get_repos()):
                repo_list.append(repo.full_name)
            return repo_list

    def get_repo(self, full_name=None):
        '''
        This method returns the Repository class from GitHub.

        Example:

        client = GitHubInterface()

        client.get_repo(full_name='bogumilchilinski/dynpy')

        '''
        if full_name is None:
            full_name = self._repo_name

        return self.g.get_repo(full_name)

    def get_issues_list(self, repo_name=None, state='all', assignee=None, sort='none', since=None):#, milestone='none',  labels='none', sort='none', direction='none', creator='none', since='none'):

        '''
        This method returns the list of the issues from GitHub Repository in form of Issue classes.

        Example:

        client = GitHubInterface()

        client.get_issues_list(repo_name='bogumilchilinski/dynpy', state='all', assignee='amvdek')
        client.get_issues_list(repo_name='bogumilchilinski/dynpy', state='open', assignee='amvdek')
        client.get_issues_list(repo_name='bogumilchilinski/dynpy', state='closed', assignee='amvdek')

        '''

        if assignee is not None:
            arg_dict =  {'assignee':assignee}
        else:
            arg_dict = {}
            
        if since is not None:
            arg_since =  {'since':since}
        else:
            from datetime import datetime
            since_date = "2024-09-01"
            date_string = since_date + "T00:00:00Z"
            date_object = datetime.strptime(date_string, "%Y-%m-%dT%H:%M:%SZ")
            arg_since = {'since':date_object}

        if repo_name is None:
            repo_name = self._repo_name

        issue_list=[]
        for issue in list(self.g.get_repo(repo_name).get_issues(state=state, sort=sort, **arg_since, **arg_dict)):#, milestone=milestone,  labels=labels, sort=sort, direction=direction, creator=creator)):
            issue_list.append(issue)
        return issue_list


    def issues_list(self, repo_name=None, state='all', assignee=None, sort='none', since=None):
        '''
        This method refers to the method get_issues_list
        
        Example:

        client = GitHubInterface()

        client.issues_list(repo_name='bogumilchilinski/dynpy', state='all', assignee='lsikor')
        
        '''

        if repo_name is None:
            repo_name = self._repo_name

        return self.get_issues_list(repo_name=repo_name, state=state, assignee=assignee, sort=sort, since=since)


    def issues_table(self, repo_name=None, state='all', assignee=None, sort='none', since=None):
        '''
        This method returns the Pandas DataFrame containing Issues.

        Example:

        client = GitHubInterface()

        client.issues_table(repo_name='bogumilchilinski/dynpy', state='open', assignee='lsikor')

        '''

        import pandas as pd
        
        if assignee is not None:
            arg_dict =  {'assignee':assignee}
        else:
            arg_dict = {}
            
        if since is not None:
            arg_since =  {'since':since}
        else:
            from datetime import datetime
            since_date = "2018-09-01"
            date_string = since_date + "T00:00:00Z"
            date_object = datetime.strptime(date_string, "%Y-%m-%dT%H:%M:%SZ")
            arg_since = {'since':date_object}

        if repo_name is None:
            repo_name = self._repo_name

        issue_title_list=[]
        issue_number_list=[]
        issue_assignees_list=[]
        for issue in list(self.g.get_repo(repo_name).get_issues(state=state, sort=sort, **arg_since, **arg_dict)):
            issue_title_list.append(issue.title)
            issue_number_list.append(issue.number)
#             issue_assignees_list.append(issue.assignees)

        data_dict = {'Issue title':issue_title_list, 'Issue number':issue_number_list, }#'Assignees':issue_assignees_list}

        return pd.DataFrame(data_dict)

    def prepare_meeting_issues(self,title=None,last_issue_no=None,guide=None,date=None,time=None,done=False):
        '''
        This method prepare meeting issues by using class MeetingIssueCreator.

        Example:
        
        from dynpy.utilities.creators import GitHubInterface
        title="Issue"
        last_issue_no=999
        client = GitHubInterface(token)
        client.prepare_meeting_issues(title,last_issue_no)

        '''
        if last_issue_no is None:
            last_issue_no = 9999
        return MeetingIssueCreator(title=title,no=last_issue_no+1,guide=guide,date=date,time=time,done=done)
    

    def open_issues(self, state='open', since=None, sort=None):
        '''
        This method returns the list of the Issues.

        Example:

        client = GitHubInterface()

        client.open_issues(state='open', since=None, sort=None)

        '''
        if self.g is not None:
            dp_repo=list(self.g.get_user().get_repos())[1]
            (dp_repo).full_name

            repo = self.g.get_repo(dp_repo.full_name)
            open_issues = repo.get_issues(state=state, since=since, sort=sort)
            for issue in open_issues:
                if 'class that creates a part of a report' in issue.title:
                    print(issue.title.split('`')[1])

            [issue.title.split('`')[1] for issue in open_issues if 'class that creates a part of a report' in issue.title ]

        else:
            return 'Reopen the client'


    def close(self):
        '''
        This method closes the connection with GitHub API.

        Example:

        client = GitHubInterface()

        client.close()

        '''
        if self.g is not None:
            g = self.g
            self.g = g.close()
        else:
            pass


    def str_to_print_issue(self, issue):
        
        title = issue.title
        body = issue.body
        no = issue.number
        asignees = issue.assignees
        id = issue.id
        comms = issue.comments
        labels = issue.labels
        created = issue.created_at
        closed = issue.closed_at

        label_names='/ '

        for label in labels:
            label_names = label_names + label.name + ' / '


        asignees_names='/ '

        for asignee in asignees:
            asignees_names = asignees_names + asignee.login + ' / '

        str_to_print =f'''

        ---------------------\n
        Title: {title} --- number {no} \n
        ---------------------\n
        Assignees: {asignees_names} \n
        ---------------------\n
        Issue created at {created} \n
        ---------------------\n
        Issue closed at {closed}
        ---------------------\n
        Labels: {label_names}
        ---------------------\n
        Description: {body} \n
        ---------------------\n


        '''


        return str_to_print

    def print_issue(self, issue):
        '''
        
        This method refers to the method str_to_print_issue
        
        '''
        return print(self.str_to_print_issue(issue=issue))


    def get_issue(self, repo_name=None, user = None, issue_number = None):
                                        
        #SyntaxError: non-default argument follows default argument, w poprzedniej wersji pojawiał się błąd, tymczasowo dodałem None do pozostałych argumentów. d.stryjewski

        if repo_name is None:
            repo_name = self._repo_name


        since_date = "2018-08-26"
        date_string = since_date + "T00:00:00Z"
        date_object = datetime.strptime(date_string, "%Y-%m-%dT%H:%M:%SZ")

        issues_list = self.get_issues_list(repo_name=repo_name, state='all', assignee=user, sort='none', since=date_object)

        for issue in issues_list:

            if issue.number == issue_number:
                break

        return issue

    def project_summary(self, repo_name=None, issue_number = None):
        '''
        This method returns the list of the related issues with assigness as dataframe.

        Example:

        from dynpy.utilities.creators import GitHubInterface
        client = GitHubInterface()
        client.project_summary(issue_number = 954)

        '''
        
        import pandas as pd
        
        if repo_name is None:
            repo_name = self._repo_name
            
        if issue_number is None:
            issue_number = 954

        issues_list = self.get_issues_list(repo_name=repo_name, state='all', assignee="*", sort='none')
        issue_title_list=[]
        issue_number_list=[]
        issue_assignees_list=[]
        
        for issue in issues_list:

            if issue.number == issue_number:
                issue_title_list.append(issue.title)
                issue_number_list.append(issue.number)
                asignees = issue.assignees
                asignees_names=''
                for asignee in asignees:
                    asignees_names = asignees_names + asignee.login + ' / '
                issue_assignees_list.append(asignees_names)
            elif "issue #" + str(issue_number) + " related" in issue.title:
                issue_title_list.append(issue.title)
                issue_number_list.append(issue.number)
                asignees = issue.assignees
                asignees_names=''
                for asignee in asignees:
                    asignees_names = asignees_names + asignee.login + ' / '
                issue_assignees_list.append(asignees_names)
            
        data_dict = {'Issue number':issue_number_list, 'Issue title':issue_title_list, 'Assignees':issue_assignees_list}

        return pd.DataFrame(data_dict)

class ClassLister:
    
    '''
    This is temporary solution, working on better one.
    
    Example of call code:
    
    ###############################
    import dynpy
    from dynpy.utilities.report import ReportText

    dir_str = 'dynpy.utilities.components.guides'

    ClassLister(dir_str).printer()
    ###############################
    '''
    
    import dynpy
    from datetime import date, time

    
    def __init__(self, directory):
        self.directory = directory
        
        import_str = f'from {directory} import *'
        exec(import_str) #exec i eval to dwie funkcje pozwalające na wykonanie komendy podanej jako string
    
    def submodgetter(self): 
        '''
        metoda zbiera liste sbmodułów z wskazanej scieżki
        
        '''
        import os.path, pkgutil
        
        pkgpath_str = f"os.path.dirname({self.directory}.__file__)"
        
        pkgpath = eval(pkgpath_str)
        list_of_sub_modules = [name for _, name, _ in pkgutil.iter_modules([pkgpath])]

        return list_of_sub_modules
    
    def classlistgetter(self, submod):
        
        '''
        metoda zbiera liste klass w podanym submodule
        
        init: submod
        '''
        
        import inspect

        import_str = f"from {self.directory}.{submod} import *"
        command_str = f"inspect.getmembers({self.directory}.{submod}, predicate=inspect.isclass)" #ta komenda zbiera liste wszystkich klas istniejących w danym submodule, niestety sa tu też klasy nas nie interesujące np. importy klas z numpy czy pandasa etc, dlatego dalej ta lista jest filtrowana
        
        try:
            import_eval = exec(import_str)
            members = eval(command_str)
        except:
                pass
            
        #members = eval(command_str)

        tmp_list = []

        for i in range(len(members)):
            if "__class__" in members[i][0]: #ten warunek doda klase do listy w przypadku gdy submoduł ma ta samą nazwe co klasa sama w sobie 
                tmp_list.append(submod)
            elif f'{self.directory}.{submod}' in str(members[i][1]):
                tmp_list.append(members[i][0])

        return tmp_list
    
    def printer(self):
        lst = []
        lst = self.submodgetter()
        
        cnt = 0
        
        for submod in lst:    
            class_lst = self.classlistgetter(submod)
            size = len(class_lst)
            
            if size != 0:
                display(ReportText(f'# {submod}'))

            for cl in class_lst:
                    display(ReportText(f'* {cl}'))
                    cnt += 1
        display(ReportText(f'Total class count is:{cnt}'))
    
    
    def initupdater(self):
        intro_str = f'''"""
This module provides the examples of mechanical models being practically used in calculations

Last update was done on {date.today()}

"""

import importlib

'''

        from datetime import date, time
        
        f = open('./output/test.py', 'w')
        f.write(intro_str)
        
        lst = []
        lst = self.submodgetter()
        for submod in lst:    
            class_lst = self.classlistgetter(submod)
            f.write(f'from .{submod} import ')

            size = len(class_lst)
            i = 1

            for cl in class_lst:

                if i < size:
                    f.write(f'{cl}, ')
                else:
                    f.write(f'{cl}')

                i += 1 

            f.write('\n\n')

        f.close()
        
    def get_init_file_content(self):
        from datetime import date, time
        from dynpy.utilities.report import ObjectCode
        
        output_str = f'#Last update was done on {date.today()} \n\n'
        
        lst = []
        lst = self.submodgetter()
        for submod in lst:    
            class_lst = self.classlistgetter(submod)
            output_str = output_str + f'from .{submod} import '

            size = len(class_lst)
            i = 1

            for cl in class_lst:

                if i < size:
                    output_str = output_str + f'{cl}, '
                else:
                    output_str = output_str + f'{cl}'

                i += 1 

            output_str = output_str + '\n\n'
        return output_str



def list_of_guides_prime():
    md_str='Lista poradników: \n\n'
    for name, obj in inspect.getmembers(document):
        if inspect.isclass(obj) and 'Guide' in name:
                if obj is not Guide:
                    md_str=md_str+f'\t\t - {name} \n\n'
    return md_str



class ModuleStructure:
    
    '''
Class was created to support development work on dynsys.

Example usage:
===========================================================================================
import dynpy
from dynpy.utilities.report import ReportText

example = ModuleStructure(dynpy.utilities.components)
example.get_classes('Code') #Creates a list of components. Optional keyword used for filtering
example.printer() #Prints list in user friendly format
example.tree() #Prints created list in tree format
===========================================================================================
    '''
    
    def __init__(self, directory, depth = False):
        
        import dynpy
        from datetime import date, time
        import inspect, os, types
        from dynpy.utilities.report import ReportText
        
        self.full_list = []
        self.tree_str = []
        self.depth = depth

        if isinstance(directory, types.ModuleType):
            s = str(directory)
            self.directory = s[s.find("module '")+len("module '"):s.rfind("' from ")]
        elif isinstance(directory, str):
            self.directory = directory
        
        import_str = f'from {self.directory} import *'
        exec(import_str)

    def submodgetter(self):
        '''
Method collects a list of submodules in the given module
        
init: None
        '''
        import inspect
        import dynpy
        import dynpy.models
        
        tmp_list = eval(f"inspect.getmembers({self.directory}, predicate=inspect.ismodule)")
        
        self.mod_list = []
        
        for i in range(len(tmp_list)):
            s = str(tmp_list[i][1])

            tmp = s[s.find("module '")+len("module '"):s.rfind("' from ")]
            
            if f"{self.directory}" in tmp: 
                self.mod_list.append(f'{self.directory}')
        tmp_list.clear()
        
        if self.depth == 1:
            self.mod_list = sorted(self.mod_list)
            return self.mod_list
        
        for element in self.mod_list:
            exec(f'from {element} import *')
            tmp_list = eval(f"inspect.getmembers({element}, predicate=inspect.ismodule)")
            if tmp_list is not None:
                for i in range(len(tmp_list)):
                    s = str(tmp_list[i][1])
                    tmp = s[s.find("module '")+len("module '"):s.rfind("' from ")]
                    if (f"{self.directory}" in tmp) and (tmp not in self.mod_list):
                        self.mod_list.append(f'{tmp}')
        
        self.mod_list = sorted(self.mod_list)
        
        return self.mod_list

    def _classlistgetter(self, submod, keyword):
        '''
Method collects a list of classes in the given submodule
keyword - optional argument, keyword by which the classes are searched for
        
init: submod,keyword
        '''
        
        import inspect

        import_str = f"from {submod} import *"
        command_str = f"inspect.getmembers({submod}, predicate=inspect.isclass)" 
        
        import_eval = exec(import_str)
        members = eval(command_str)

        tmp_list = []

        for i in range(len(members)):
            if "__class__" in members[i][0]: 
                if (keyword != None) and (keyword in members[i][0]):
                    tmp_list.append((f'{self.directory}.submod', submod))
                elif keyword == None:
                    tmp_list.append((f'{self.directory}.submod', submod))
            if f'{submod}' in str(members[i][1]):
                if (keyword != None) and (keyword in members[i][0]):
                    s = str(members[i][1])
                    tmp = s[s.find("<class '")+len("<class '"):s.rfind(f".{members[i][0]}'>")]
                    tmp_list.append((tmp,members[i][0]))
                elif keyword == None:
                    s = str(members[i][1])
                    tmp = s[s.find("<class '")+len("<class '"):s.rfind(f".{members[i][0]}'>")]
                    tmp_list.append((tmp,members[i][0]))
                    
        return tmp_list
    
    def get_classes(self, keyword = None):
        
        '''
Method collects a list of classes and parent submodule directory in the given module
keyword - optional argument, keyword by which the classes are searched for
        
init: keyword
        '''
        
        self.submodgetter()
        
        self.full_list = []
        
        cnt = 0
        
        if not self.mod_list:
            class_lst = self._classlistgetter(self.directory, keyword)
            
            if len(class_lst) != 0:
                for cl in class_lst:
                        self.full_list.append(cl)
        
        for submod in self.mod_list:    
            class_lst = self._classlistgetter(submod, keyword)
            size = len(class_lst)
            
            if size != 0:
                for cl in class_lst:
                        self.full_list.append(cl)
                        
                        cnt += 1
        self.full_list = list(set(self.full_list))
        
        return self.full_list
    
    def printer(self, keyword = None):
        
        '''
Method prints a list of classes and parent submodule directory in the given module
keyword - optional argument, keyword by which the classes are searched for
        
init: keyword
        '''
        
        if not self.full_list:
            self.get_classes(keyword)
        
        i = 0
        a = 0
        
        submod_prev = None
        class_name = self.full_list[0][1]
        
        for element in self.full_list:
            if submod_prev == self.full_list[i][0]:
                display(ReportText(f'* {self.full_list[i][1]}'))
                submod_prev = self.full_list[i][0]
            else:
                display(ReportText(f'### {self.full_list[i][0]}'))
                display(ReportText(f'* {self.full_list[i][1]}'))
                
                submod_prev = self.full_list[i][0]

            i = i+1 
        
        display(ReportText(f'Total class count is: {i}'))
        
    def get_module_tree(self, keyword = None):
        
        '''
Method prints a tree of classes
keyword - optional argument, keyword by which the classes are searched for
        
init: keyword
        '''
        
        if not self.full_list:
            self.get_classes(keyword)
        
        self.tree_str = TreeNode.build_tree(self.full_list)
        
        return self.tree_str
    
    def as_reporttext(self, keyword = None):
        '''
Method format tree for ReportText and Markdown 
keyword - optional argument, keyword by which the classes are searched for        
        '''
        from dynpy.utilities.report import ReportText
        
        if not self.full_list:
            self.get_classes(keyword)
        
        return ReportText(TreeNode.as_reporttext(self.full_list).replace('\n', "<br />"))
    
    def as_objectcode(self, keyword = None):
        '''
Method format tree for ObjectCode
keyword - optional argument, keyword by which the classes are searched for        
        '''
        from dynpy.utilities.report import ObjectCode
        
        if not self.full_list:
            self.get_classes(keyword)
            
        if not self.tree_str:
            self.tree_str = TreeNode.build_tree(self.full_list)
        
        return ObjectCode(self.tree_str)
    
    def __repr__(self):
        return self.get_module_tree()
    
    def _get_init(self):
        from datetime import date, time
        from dynpy.utilities.report import ObjectCode

        output_str = f'''#Last update was done on {date.today()}  '''
        
        self.get_classes()
        self.full_list = list(set(self.full_list))        
        self.full_list = sorted(self.full_list)
        
        if len(self.full_list) == 0:
            try:
                output_str = ClassLister(self.directory).get_init_file_content()
            except:
                pass

        prev = []
        
        for element in self.full_list:
            if element[0] != prev:
                
                prev = element[0]
                output_str = output_str[:-2]
                output_str = output_str + '\n\n'
                
                s = prev
                s = s[s.find(self.directory)+len(self.directory):s.rfind("")]
                if "'" in s:
                    s = s[:-1]
                output_str = output_str + f'from {s} import '

                output_str = output_str + f'{element[1]}, '
            else:
                output_str = output_str + f'{element[1]}, '

        if output_str[-2] == ',':
            output_str = output_str[:-2]
            
        return output_str
    
    @staticmethod
    def get_init_file_content(dir_list = None):
        
        from dynpy.utilities.report import ReportText, ObjectCode
        
        if dir_list == None:
            dir_list = [
    'dynpy',
    'dynpy.models',
    'dynpy.models.control',
    'dynpy.models.electric',
    'dynpy.models.mechanics',
    'dynpy.models.odes',
    'dynpy.solvers',
    'dynpy.utilities',
    'dynpy.utilities.components',
    'dynpy.utilities.components.guides',
    'dynpy.utilities.components.guides.development',
    'dynpy.utilities.components.guides.github',
    'dynpy.utilities.components.guides.pandas',
    'dynpy.utilities.components.guides.reporting',
    'dynpy.utilities.components.guides.systems',
    'dynpy.utilities.components.mech',
    'dynpy.utilities.components.ode',
    'dynpy.utilities.components.miscellaneous',
    'dynpy.utilities.documents',
                        ]
        for element in dir_list:
            display(ReportText(f'This is __init__.py content for {element} module'))
            display(ObjectCode(__class__(element)._get_init()))
    
    @staticmethod
    def get_import(keyword , as_type = 'ObjectCode'):
        from dynpy.utilities.report import ObjectCode
        
        tmp = __class__('dynpy').get_classes(keyword)
        lst = []

        if len(tmp) == 0:
            return "Couldn't get import"
        else:
            for element in tmp:
                lst.append(f'from {element[0]} import {element[1]}')
            
            if as_type == 'ObjectCode':
                lst = '\n'.join(lst)
                return ObjectCode(lst)
            elif as_type == str:
                return lst

class TreeNode:
    '''
Class created to present table of directories in tree format known from commandline tool in Unix systems.

To call tree use bellow call code:

    TreeNode.build_tree(full_list, separator, rootnode)

    Where:
        full_list - list, where each node is separated by some symbol, for example: dynpy.utilities.components
        separator (optional, by defualt it is '.') - symbol used to separate each node '.' in above example 
        rootnode (optional, by defualt it is 'dynpy'- node that starts creating tree, for example 'dynpy'
    '''
    def __init__(self, name, parent = None):
        self.parent = parent
        self.name = name
        self.children = []
        self.str_out = []

    def add_child(self, node):
        self.children.append(node)
        return node

    def print(self, is_root = True, syntax = True):
        '''
Method is used to create tree string.

init: 
syntax - is used to define tab sign, Object code accept normal spaces, Markdown and ReportText require &nbsp; instead of tab
        '''
        if syntax is True:
            pre_0 = "    "
            pre_1 = "│   "
            pre_2 = "├── "
            pre_3 = "└── "
        elif syntax is False:
            pre_0 = "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"
            pre_1 = "│&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;"
            pre_2 = "├──&nbsp;"
            pre_3 = "└──&nbsp;"


        tree = self
        prefix = pre_2 if tree.parent and id(tree) != id(tree.parent.children[-1]) else pre_3

        while tree.parent and tree.parent.parent:
            if tree.parent.parent and id(tree.parent) != id(tree.parent.parent.children[-1]):
                prefix = pre_1 + prefix
            else:
                prefix = pre_0 + prefix

            tree = tree.parent

        if is_root:
            self.str_out.append(self.name)
        else:
            self.str_out.append(prefix + self.name)

        for child in self.children:
            self.str_out.append(child.print(is_root = False, syntax = syntax))
            
        return self.str_out

    @staticmethod
    def find_and_insert(parent, edges):
        if not edges:
            return

        match = [tree for tree in parent.children if tree.name == edges[0]]

        tree = match[0] if match else parent.add_child(TreeNode(edges[0], parent))

        __class__.find_and_insert(tree, edges[1:])
        
    @staticmethod
    def unpack(lst_to_unpack):
        '''
This method is a helper to unpack nested list returned by print method.
Method will return string ready to use by ObjectCode.

init: list returned from print() method
        '''
        
        unpacked_lst = []
        for element in lst_to_unpack:
            if isinstance(element, list):
                unpacked_lst.extend(__class__.unpack(element))
            else:
                unpacked_lst.append(element)
                unpacked_lst.append('\n')
            lst = ''.join(unpacked_lst)
        return lst
    
    @classmethod
    def build_tree(cls, full_list, separator = '.', rootnode = 'dynpy'):
        
        '''
Method created to build full tree from declared list

init: full_list, separator, rootnode

        '''
        
        lst_comp = []
        i = 0

        while i != len(full_list):
            t_str = f'{full_list[i][0]}.{full_list[i][1]}'
            lst_comp.append(t_str)
            i=i+1
            
        root = __class__(rootnode)

        for element in lst_comp:
            root.find_and_insert(root, element.split(separator)[1:])
        tree_str = root.print()
        
        cls.tree_comp = __class__.unpack(tree_str)
        
        return cls.tree_comp
    
    
    @classmethod
    def as_reporttext(cls, full_list, separator = '.', rootnode = 'dynpy'):
        '''
Staticmethod return tree string in ReportText format

Init: tree_comp - string returned by build_tree method
        '''
        lst_comp = []
        i = 0

        while i != len(full_list):
            t_str = f'{full_list[i][0]}.{full_list[i][1]}'
            lst_comp.append(t_str)
            i=i+1
            
        root = __class__(rootnode)

        for element in lst_comp:
            root.find_and_insert(root, element.split(separator)[1:])
        tree_str = root.print(is_root = True, syntax = False)
        
        comp = __class__.unpack(tree_str)
        
        return comp
        
        
def list_of_guides():

    #dir_str = 'dynpy.utilities.documents'

    #return [guide_cls for guide_cls in ModuleStructure(dir_str).get_classes() if 'guides' in guide_cls[0]]
    
    return ModuleStructure(dynpy.utilities.documents.guides).get_classes()

def list_of_components():

    dir_str = 'dynpy.utilities.components'
    
    return ModuleStructure(dir_str).get_classes()

def list_of_mechanical_systems():

    dir_str = 'dynpy.models.mechanics'

    return ModuleStructure(dir_str).get_classes()

def list_of_dynamic_systems():
        
    dyn_lst = []
         
    dyn_lst = ModuleStructure('dynpy.models.mechanics').get_classes() + ModuleStructure('dynpy.models.electric').get_classes()   
 
    return dyn_lst


advanced_modeling_schedule_code_str = """Następujące zadania mają na celu przygotowanie projektu wybranego przez studentów układu dynamicznego. Konieczne jest zaplanowanie wykonania kamieni milowych w terminie do {date} i w czasie {time}, celem utrzymania ciągłości realizacji projektu:

- [{tic}] #{issue_no+1}

- [{tic}] #{issue_no+2}

- [{tic}] #{issue_no+3}

- [{tic}] #{issue_no+4}

- [{tic}] #{issue_no+5}

- [{tic}] #{issue_no+6}

- [{tic}] #{issue_no+7}

"""
 

advanced_modeling_intro_code_str = """Celem jest przygotowanie toku postępowania, dzięki któremu możliwe będzie sprawne przygotowanie projektu zaawansowanego modelu dynamicznego:

 FAZA I - WSTĘPNA - 1 tydzień (ZESPÓŁ/ 1 osoba)

- [{tic}] Określenie wymagań oraz oczekiwań do projektu, w jakiej dziedzinie chcemy wykonać pracę:
                mechaniczna np. przekładnie, silniki, 
                elektryczna np. akumulatory, filtry, uklady sterowania
                budowlana np. dynamiczne eliminatory drgań, żurawie 
                dynamiczne np. zawieszenia, 
                kinematyczne np. ruch "pojazdu" po trasie wraz z jego charakteryzacją

- [{tic}] Wybór rzeczywistego obiektu, mechanizmu lub zjawiska, na bazie którego wykonany zostanie model.

- [{tic}] Prezentacja przykładowego rzeczywistego modelu doświadczalnego, który nawiązuje do modelu symulacyjnego

- [{tic}] Wybór metodyki stworzenia modelu dynamicznego,

- [{tic}] Określenie założeń do modelu, stopnia zaawansowania oraz konkretnych wyników jakie chcemy uzyskać

"""

advanced_modeling_basics_code_str = """Celem jest przygotowanie toku postępowania, dzięki któremu możliwe będzie sprawne przygotowanie projektu zaawansowanego modelu dynamicznego:

 FAZA II - PRZYGOTOWANIA PRACY - 2 tygodnie (1 osoba)

- [{tic}] Przegląd aktualnego stanu wiedzy oraz zawartości środowiska DynPy, celem rozpoznania możliwości,

- [{tic}] Przygotowanie dokumentu, w którym zaprezentowane będą podstawowe klasy i metody na bazie których możliwe będzie wykonanie projektu oraz raportu,

- [{tic}] Wyszukanie grafiki obiektu, mechanizmu lub zjawisa na podstawie którego należy wykonać rysunek schematyczny (np. w SolidWorks/Autocad/TiKz) 

- [{tic}] Przeprowadzenie całkowitej charakterystyki modelu (LSS, współrzędne, wiezy,  energie, rownania ruchu)

"""

advanced_modelling_modelling_code_str = """Celem jest przygotowanie toku postępowania, dzięki któremu możliwe będzie sprawne przygotowanie projektu zaawansowanego modelu dynamicznego:

 FAZA III - WYKONANIA MODELU - 3 tygodnie (1 osoba)

- [{tic}] Wybór modelu z biblioteki DynPy, który przypomina zdefiniowany model w poprzedniej fazie,

- [{tic}] Przygotowanie, bazując na modelu,  wszystkich potrzebnych do analizy równań, 

- [{tic}] Dobranie odpowiednich danych i parametrów do wybranego modelu symulacyjnego,

- [{tic}] Przeprowadzenie weryfikacji poprawności zaprogramowanych funckji,

"""

advanced_modeling_simulation_code_str = """Celem jest przygotowanie toku postępowania, dzięki któremu możliwe będzie sprawne przygotowanie projektu zaawansowanego modelu dynamicznego:

 FAZA IV - WYKONANIA SYMULACJI - 2 tygodnie (1 osoba) 

- [{tic}] Przygotowanie odpowiednich funckji do wykonania symulacji analitycznych i numerycznych,

- [{tic}] Wykonanie symulacji z wykorzystaniem analitycznych rozwiązań równanań ruchu,

- [{tic}] Przeprowadzenie symulacji z wykorzystaniem modelu znumeryzowanego,

- [{tic}] Przeprowadzenie analizy poprawności dobranych danych oraz wykonanie ewentualnej modyfikacji,

- [{tic}] Stworzenie matrycy porównującej wyniki obu symulacji,

- [{tic}] (+) Wykonanie obliczeń wielkości innych niż "x,v,a",

"""

advanced_modeling_analysis_code_str = """Celem jest przygotowanie toku postępowania, dzięki któremu możliwe będzie sprawne przygotowanie projektu zaawansowanego modelu dynamicznego:

 FAZA V - ANALIZY WYNIKÓW - 1 tydzień (1 osoba)

- [{tic}] Zaprezentowanie wyników w formie zbioru tabel

- [{tic}] Prezentacja wyników w formie wykresów wraz

- [{tic}] Wykonanie analizy wyników i zdefiniowanie zauważonych zjawisk

"""

advanced_modeling_report_code_str = """Celem jest przygotowanie toku postępowania, dzięki któremu możliwe będzie sprawne przygotowanie projektu zaawansowanego modelu dynamicznego:

 FAZA VI - WYKONANIA RAPORTU KOŃCOWEGO - 2 tygodnie (1 osoba)

- [{tic}] Stworzenie krótkiego opisu aktualnego stanu wiedzy wraz z opisem idei modelowania (FAZA II),

- [{tic}] Zaprezentowanie wybranego modelu rzeczywistego oraz jego interpretacji fizycznej (FAZA I),

- [{tic}] Prezentacja wykorzystanych równań charakteryzujących obiekt (FAZA III),

- [{tic}] Przygotowanie opisu stosowanego programu (FAZA III),

- [{tic}] Wykonanie tabel i wykresów oraz ich opis (FAZA IV, V)

- [{tic}] Sformułowanie wniosków (FAZA VI)

"""

advanced_modeling_schedule_code_str_en = """
The aim of the issue is to manage the process of project implementation.
The following activities are to do:

- [{tic}] assignment of tasks;

- [{tic}] presentation of requirements that need to be fulfilled (code, issues, other things);

- [{tic}] ???

- [{tic}] presentation of performed activities;

The tasks to complete are as follows:

- [{tic}] #{issue_no+1}

- [{tic}] #{issue_no+2}

- [{tic}] #{issue_no+3}

- [{tic}] #{issue_no+4}

- [{tic}] #{issue_no+5}

- [{tic}] #{issue_no+6}

- [{tic}] #{issue_no+7}

"""


advanced_modeling_intro_code_str_en = """The goal is preparation of work environment and initial content (draft) version of the report:

 PHASE I - PRELIMINARY - 1 week (TEAM/ 1 person)

[{tic}] Preparation of folder structure, necessary files and preleminary content report,

[{tic}] Definition of the requirements and expectations for the project, determination of the field in which the work will be conducted: mechanical, electrical, structural, dynamic, kinematic along a route, along with its characterization,

[{tic}] Selection of a real object, mechanism, or phenomenon on which the model will be based,

[{tic}] Presentation of an example of a real experimental model that relates to the simulation model,

[{tic}] Definition of the assumptions for the model, the level of advancement, and the specific results we aim to achieve.

"""

advanced_modeling_basics_code_str_en = """The goal is research on current state of art about the topic and methodology needed for project:

 PHASE II - PROJECT PREPARATION - 2 weeks (1 person)

[{tic}] Revision of the current state of knowledge,

[{tic}] Revision of the DynPy environment to assess its capabilities,

[{tic}] Selection of a methodology for creating the dynamic model,

[{tic}] Preparation of a document presenting the fundamental classes and methods that will form the basis of the project and report,

[{tic}] Searching for graphics of the object, mechanism, or phenomenon on which the schematic drawing will be based (e.g., in SolidWorks/AutoCAD/TiKz),

[{tic}] Conduction a complete characterization of the model (LSS, coordinates, constraints, energies, equations of motion).

"""

advanced_modelling_modelling_code_str_en = """The goal is creating the mathematical model of investigated object:

 PHASE III - MODEL DEVELOPMENT - 3 weeks (1 person)

[{tic}] Selection of a model from the DynPy library that resembles the model defined in the previous phase,

[{tic}] Praparation, based on the model, of all the necessary equations for the analysis,

[{tic}] Selection of appropriate data and parameters for the chosen simulation model,

[{tic}] Conduction verification process of the programmed functions to ensure correctness.

"""

advanced_modeling_simulation_code_str_en = """The goal is using the model to perform all necessary simulations and got valuable data:

 PHASE IV - SIMULATION EXECUTION - 2 weeks (1 person)

[{tic}] Preparation of appropriate functions for conducting analytical and numerical simulations,

[{tic}] Execution of simulations using analytical solutions of the equations of motion,

[{tic}] Conduction of simulations using the numerically discretized model,

[{tic}] Analysing accuracy of the selected data and making any necessary adjustments,

[{tic}] Creation of a matrix comparing the results of both simulations,

[{tic}] (+) Performation of calculations for quantities other than "x, v, a".

"""

advanced_modeling_analysis_code_str_en = """The goal is validation and proper visualization of all acquired data:

 PHASE V - RESULTS ANALYSIS - 1 week (1 person)

[{tic}] Presentation of the results in the form of a set of tables,

[{tic}] Displaying of the results in the form of graphs,

[{tic}] Performing of an analysis of the results and identification of the observed phenomena.

"""

advanced_modeling_report_code_str_en = """The goal is revision of all steps of the research and creation of the final report.

 PHASE VI - FINAL REPORT PREPARATION - 2 weeks (1 person)

[{tic}] Creation of a brief summary of the current state of knowledge along with a description of the modeling concept (PHASE II),

[{tic}] Presentation of the selected real model and its physical interpretation (PHASE I),

[{tic}] Presentation  of the equations used to characterize the object (PHASE III),

[{tic}] Preparation a description of the applied software (PHASE III),

[{tic}] Preparation of tables and graphs along with their descriptions (PHASE IV, V),

[{tic}] Formulation of conclusions (PHASE VI).

"""

class ResearchProjectIssueCreator:
    
    _title = 'name of the research project'
    _issue_no = 567
    _guide = ODESystemOverviewReport #### ResearchProjectGuidelines
    _time = '14:30' ## 
    _date = '2024.10.10' ##
    _lang = 'en'
    _project_type = 'research'

    def __init__(self,title=None,no=None,guide=None,date=None,time=None,done=False,lang=None,*args,**kwargs):
        if title is not None: self._title = title
        if no is not None: self._issue_no = no
        if guide is not None: self._guide = guide
        if date is not None: self._date= date
        if time is not None: self._time = time
        if lang is not None: self._lang = lang
        self._done = done
        
    
    def _get_elems_dict(self):

        if self._done: tic = 'x'
        else: tic = ' '

        guide_class = self._guide

        elems_dict = {
                'title':self._title,
                'issue_no':self._issue_no,
                'issue_no+1':self._issue_no+1,
                'issue_no+2':self._issue_no+2,
                'issue_no+3':self._issue_no+3, #### 7 ISSUE
                'issue_no+4':self._issue_no+4,
                'issue_no+5':self._issue_no+5,
                'issue_no+6':self._issue_no+6,
                'issue_no+7':self._issue_no+7,
                'guide_class_module':guide_class.__module__,
                'guide_class_name':guide_class.__name__,
                'date':self._date,
                'time':self._time,
                'tic':tic,
                'type':self._project_type,
                }
        
        return elems_dict
    
    def get_issues_titles(self):
        
        
        # Issue no. #{issue_no}
        ## Preparation of meeting and guide for {title}

        issue_no = self._issue_no
        title = self._title
        lang = self._lang
        project_type = self._project_type
        
        
        if lang == 'en':


            titles_dict =   {
                        'schedule': f'{project_type.capitalize()} project on {title}', ### 
                        'intro': f'Preparation of the preleminary content report for {project_type} on {title} (issue #{issue_no} related)', ### 
                        'basics': f'Investigation of state of the art and research methodology for {title} (issue #{issue_no} related)', ### 
                        'modelling': f'Modelling of the investigated object for the {title} (issue #{issue_no} related)', ### 
                        'simulation': f'Performing simulations of investigated case in the {project_type} {title} (issue #{issue_no} related)', ### 
                        'analysis': f'Analysis and validation of obtained results in the {project_type} on {title} (issue #{issue_no} related)', ### 
                        'report': f'Ending revision of a report covering all phases of {project_type} on {title} (issue #{issue_no} related)', ###
                        }
            
        else:
            titles_dict =   {
                        'schedule':f'Prezentacja harmonogramu prac na zajęciach z przedmiotu {title}', ### 
                        'intro':f'Wprowadzenie do środowiska DynPy do wykonania prac z przedmiotu {title} (powiązane z issue #{issue_no})', ### 
                        'basics':f'Przygotowanie podstawowych informacji o modelu oraz zaproponowanie koncepcji stanowiska badawczego z przedmiotu {title} (powiązane z issue #{issue_no})', ###
                        'modelling':f'Wykonanie modelu symulacyjnego z wybranego zagadnienia z przedmiotu {title} (powiązane z issue #{issue_no})', ###
                        'simulation':f'Przeporwadzenie symulacji rozważanego modelu z {project_type} o {title} (powiązane z issue #{issue_no})', ###
                        'analysis':f'Analiza i zatwierdzenie otrzymanych wyników z {project_type} o {title} (powiązane z issue #{issue_no})', ###
                        'report':f'Końcowa rewizja raportu z wszystkich faz {project_type} o {title} (powiązane z issue #{issue_no})', ###
                        }

        return titles_dict
        
    def get_issue_str(self):
        elems_dict = self._get_elems_dict()
        issue_no = self._issue_no
        
        titles = self.get_issues_titles()
        
        schedule_issue_dict = self.get_schedule_issue_dict()
        intro_issue_dict = self.get_intro_issue_dict()
        basics_issue_dict = self.get_basics_issue_dict()
        modelling_issue_dict = self.get_modelling_issue_dict()
        simulation_issue_dict = self.get_simulation_issue_dict()
        analysis_issue_dict = self.get_analysis_issue_dict()
        report_issue_dict = self.get_report_issue_dict()
        
        
        issue_code_str = (f'# Issue no. #{issue_no} \n '+
                        '## '+schedule_issue_dict['title'] + '\n ' +
                        schedule_issue_dict['body'] + '\n ' +
                        f'# Issue no. #{issue_no+1} \n '+
                        '## '+ intro_issue_dict['title'] + '\n ' +
                        intro_issue_dict['body'] + '\n ' +
                        f'# Issue no. #{issue_no+2} \n '+
                        '## '+basics_issue_dict['title'] + '\n ' +
                        basics_issue_dict['body'] + '\n ' +
                        f'# Issue no. #{issue_no+3} \n '+
                        '## '+modelling_issue_dict['title'] + '\n ' +
                        modelling_issue_dict['body'] + '\n ' +
                        f'# Issue no. #{issue_no+4} \n '+
                        '## '+simulation_issue_dict['title'] + '\n ' +
                        simulation_issue_dict['body'] + '\n ' +
                        f'# Issue no. #{issue_no+5} \n '+
                        '## '+analysis_issue_dict['title'] + '\n ' +
                        analysis_issue_dict['body'] + '\n ' +
                        f'# Issue no. #{issue_no+6} \n '+
                        '## '+report_issue_dict['title'] + '\n ' +
                        report_issue_dict['body'])
        
        return issue_code_str


    def get_schedule_issue_dict(self):
        elems_dict = self._get_elems_dict()
        titles = self.get_issues_titles()
        lang = self._lang

        if lang == 'en':
            return {'title':titles['schedule'],'body':advanced_modeling_schedule_code_str_en.format(**elems_dict)}
        else:
            return {'title':titles['schedule'],'body':advanced_modeling_schedule_code_str.format(**elems_dict)}

    def get_intro_issue_dict(self):
        elems_dict = self._get_elems_dict()
        titles = self.get_issues_titles()
        lang = self._lang

        if lang == 'en':
            return {'title':titles['intro'],'body':advanced_modeling_intro_code_str_en.format(**elems_dict)}
        else:
            return {'title':titles['intro'],'body':advanced_modeling_intro_code_str.format(**elems_dict)}

    def get_basics_issue_dict(self):
        elems_dict = self._get_elems_dict()
        titles = self.get_issues_titles()
        lang = self._lang

        if lang == 'en':
            return {'title':titles['basics'],'body':advanced_modeling_basics_code_str_en.format(**elems_dict)}
        else:
            return {'title':titles['basics'],'body':advanced_modeling_basics_code_str.format(**elems_dict)}
    
    def get_modelling_issue_dict(self):
        elems_dict = self._get_elems_dict()
        titles = self.get_issues_titles()
        lang = self._lang

        if lang == 'en':
            return {'title':titles['modelling'],'body':advanced_modelling_modelling_code_str_en.format(**elems_dict)}
        else:
            return {'title':titles['modelling'],'body':advanced_modelling_modelling_code_str.format(**elems_dict)}
    
    def get_simulation_issue_dict(self):
        elems_dict = self._get_elems_dict()
        titles = self.get_issues_titles()
        lang = self._lang
        
        if lang == 'en':
            return {'title':titles['simulation'],'body':advanced_modeling_simulation_code_str_en.format(**elems_dict)}
        else:
            return {'title':titles['simulation'],'body':advanced_modeling_simulation_code_str.format(**elems_dict)}
    
    def get_analysis_issue_dict(self):
        elems_dict = self._get_elems_dict()
        titles = self.get_issues_titles()
        lang = self._lang
        
        if lang == 'en':
            return {'title':titles['analysis'],'body':advanced_modeling_analysis_code_str_en.format(**elems_dict)}
        else:
            return {'title':titles['analysis'],'body':advanced_modeling_analysis_code_str.format(**elems_dict)}
    
    def get_report_issue_dict(self):
        elems_dict = self._get_elems_dict()
        titles = self.get_issues_titles()
        lang = self._lang

        if lang == 'en':
            return {'title':titles['report'],'body':advanced_modeling_report_code_str_en.format(**elems_dict)}
        else:
            return {'title':titles['report'],'body':advanced_modeling_report_code_str.format(**elems_dict)}
    
    def _repr_markdown_(self):
        return self.get_issue_str()

    def publish_on_github(self,repository):
        
        guide_label=repository.get_label('guidance')
        
        issue_schedule = repository.create_issue(**self.get_schedule_issue_dict(),labels=[guide_label]#,assignees=wykon
                                )
        
        issue_intro = repository.create_issue(**self.get_intro_issue_dict(),labels=[guide_label]#,assignees=wykon
                                )
        
        issue_basics = repository.create_issue(**self.get_basics_issue_dict(),labels=[guide_label]#,assignees=wykon
                                )
        
        issue_modelling = repository.create_issue(**self.get_modelling_issue_dict(),labels=[guide_label],assignee='bogumilchilinski')
        
        issue_simulation = repository.create_issue(**self.get_simulation_issue_dict(),labels=[guide_label]#,assignees=wykon
                                )
        
        issue_analysis = repository.create_issue(**self.get_analysis_issue_dict(),labels=[guide_label]#,assignees=wykon
                                )
        
        issue_report = repository.create_issue(**self.get_report_issue_dict(),labels=[guide_label]#,assignees=wykon
                                )
        
        return issue_schedule, issue_intro, issue_basics, issue_modelling, issue_simulation, issue_analysis, issue_report
    
class PDF_link:
    '''
Class create hyperlink to PDF file, that will open document in new browser tab.

====================================================================================================
Arguments:
    
    document - instance of class from dynpy.utilities.templates.document like Guide, WUTthesis etc.
====================================================================================================
Use example:

    from dynpy.utilities.templates.document import WutThesis, Guide

    doc = WutThesis('./output/file')
    doc.generate_pdf(clean_tex=True)

    PDF_link(doc)
====================================================================================================
    '''
    def __init__(self, document):
        import IPython
        from IPython.display import display_pdf
        path = f'{document.default_filepath}'
        with open((document.default_filepath + '.pdf'), "rb") as f:
            display_pdf(f.read(),raw=True)

        f.close()