''' -*- coding: cp1252 -*-
                                                   How This Script Works
                                    --------------------------------------------------

                                                          Purpose
                                                      ---------------
    The general purpose of this script is to convert an xml version of a team's Jira Structure into an ordered list within an odf file, its
    contents part of a Srs generated by the rest of this script. This allows the Srs to be written without human error, and to maintain a
    strict and consistant format with the creation of the Srs. The Srs will also include a table of contents, and tables pertinent to the
    information in the Srs.

                                                      Necessary Files
                                                    -------------------
    There are a few necessary files for this script to function properly. In order to zip contents into an odf file, there are files that
    are required to be contained in the zipped folder in order to properly zip it into a working odt file. I will list each necessary file
    and its purpose. First, the content.xml file. This file contains all of the information that is visibile, or seen, when the odt file is
    opened. This includes tables, the ordered lists, table of contents, and all other content that is to be displayed to the user. There is
    also a large amount of tagging information inserted into this document that retain the properties of titles, paragraphs, lists, tables,
    and any other information that is relevant to the contents. This tagging information is usually found at the top of content.xml. In this
    script, however, the content is stored in a file called defaultContentHeading.txt, and is read and written into content.xml by the
    function ReadContentHeading. The second necessary file is meta.xml. This file contains meta information. The third is the META-INF folder.
    This folder contains the information that makes relevant the other necessary files in the zipping process. The fourth is the mimetype file.
    This file is a label for a specific data type so software can handle the data. The fifth is the media folder. This folder contains any images
    used in the document. The sixth is the settings.xml file. This file contains all of the settings necessary for configuration with Microsoft Office.
    Lastly, the styles.xml file. This file contains all of the styling information that formats and styles all of the information in the content.xml file.


                                                    Using the Jira Structure
                                                  ----------------------------
    In Jira, each team has a structure pertaining to all of the requirements in their application. This structure can be exported as an excel document.
    Exporting it is exactly what you want to do. Once exported, open the excel document and save it as an XML Spreadsheet 2003 (*.xml). This will enable
    this script to use it properly. You must replicate this process for each Jira Structure that the user wants in the Srs.
    

                                                   How To Use This Script
                                                  ------------------------    
    This script functions by taking in an exported Jira Structure XML file as input, reading its contents for styling ID's pertaining
    to indent levels, and a list of summaries from the Jira Structure. It then uses these styling ID's to dynamically sort the summaries
    into an ordered list. This is where ListOfLists comes to play. By appending into a list each time a summary is entered, we can keep track
    of the position that the summary should be in the ordered list by the length of the list. By deleting the current list after decrementing,
    we can restart the list count to ensure the ordered list is properly retained, i.e. 1.1, 1.2, 1.3, 2.1, 2.2, 2.3, ect.
    The table of contents is generated by writing only the main and secondary titles of the input Jira Structure XML. These titles are then
    Hyperlinked to their respective title in the document. The Introduction and its components are hardcoded, as it will remain a constant
    template for any SRS document. There is a .txt file containing all of the odf styling and content information called defaultContentHeading
    that is read by the ReadContentHeading function, and written into the Content.xml file. The Content.xml file is where the ordered list,
    table of contents, tables, and all other information regarding the generated SRS document is writen. Content.xml, along with meta.xml,
    settings.xml, styles.xml, mimetype, META-INF, and media files are all required to be in the same folder, as they will be zipped into a
    .zip archive, transforming them into an odf file. This will allow the user to left click on the .zip archive, and open with Microsoft Word.



                                                    The Jira Client Class
                                                   -----------------------
    This class logs into the Reflexion Health Jira server (https://projects.reflexionhealth.com), and pulls the Issue Priorities and Issue Types
    so that we can weed those out when parsing through the xml file. There are two constants, declared JIRA_USERNAME and JIRA_PASSWORD that must
    match an accepted Jira account. Once logged in, the JiraClient class will instantiate two empty lists to input the names of each Issue Priority
    and Issue Type into by the SetIssuePrioritiesAndIssueTypes function. It will then return these lists by the GetIssuePriorities and GetIssueTypes
    function.

                                                    Using the Command Prompt
                                                  ---------------------------
    As default, the input and output files will be searched for and written in the local directory as 'srsXml.xml'
    and 'content.xml'. If the XML files are not in the local directory, please indicate the directory
    that they are located in via -i and -o in the command prompt for input file path and output file path.
    By default the content heading folder is in the local directory as 'defaultContentHeading.txt'. If not in the immediate
    local directory, please indicate the directory that it is located in via the -hf in the command prompt.
'''



import xml.etree.ElementTree as etree
import argparse, re, getpass
import jira



#Constants for JiraClient Class
JIRA_SERVER = 'https://projects.reflexionhealth.com'
LOGIN_WAIT_STRING = 'Logging into Jira Server, please wait...'
LOGIN_SUCCESS_STRING = 'Login Successful!'


#Constants for the information contained within the data cells of the xml file
DATA_CELL = '{urn:schemas-microsoft-com:office:spreadsheet}Data'
TICKET_CELL = '{urn:schemas-microsoft-com:office:spreadsheet}HRef'
INDENT_LEVEL_ID = '{urn:schemas-microsoft-com:office:spreadsheet}StyleID'
ROW_CELL = '{urn:schemas-microsoft-com:office:spreadsheet}Row'


#Input, Output, and data folder containing the heading file path
DEFAULT_PATIENT_INPUT_PATH = 'PatientSRS.xml'
DEFAULT_CLINICIAN_INPUT_PATH = 'ClinicianSRS.xml'
DEFAULT_API_INPUT_PATH = 'APISRS.xml'
DEFAULT_OUTPUT_PATH = 'content.xml'
DEFAULT_HEADING_FILE_PATH = 'defaultContentHeading.txt'



#Constants for the Table of Contents
DEFAULT_TABLE_OF_CONTENTS_INTRODUCTION_ITEMS = '''<text:h text:style-name="P38" text:outline-level="1">Table of Contents</text:h>\n
                    <text:p text:style-name="Normal"><text:s text:c="4"/><text:a xlink:href="#Introduction" office:target-frame-name="_top" xlink:show="replace">I. Introduction</text:a></text:p>\n
                    <text:p text:style-name="Normal"><text:s text:c="8"/><text:a xlink:href="#Purpose" office:target-frame-name="_top" xlink:show="replace">A. Purpose</text:a></text:p>\n
                    <text:p text:style-name="Normal"><text:s text:c="8"/><text:a xlink:href="#Scope" office:target-frame-name="_top" xlink:show="replace">B. Scope</text:a></text:p>\n
                    <text:p text:style-name="Normal"><text:s text:c="8"/><text:a xlink:href="#References" office:target-frame-name="_top" xlink:show="replace">C. References</text:a></text:p>\n
                    <text:p text:style-name="Normal"><text:s text:c="8"/><text:a xlink:href="#Acronyms&amp;Definitions" office:target-frame-name="_top" xlink:show="replace">D. Acronyms &amp; Definitions</text:a></text:p>\n
                    <text:p text:style-name="Normal"><text:s text:c="8"/><text:a xlink:href="#Users" office:target-frame-name="_top" xlink:show="replace">E. Users</text:a></text:p>\n
                    <text:p text:style-name="Normal"><text:s text:c="8"/><text:a xlink:href="#Applications" office:target-frame-name="_top" xlink:show="replace">F. Applications</text:a></text:p>\n'''

DEFAULT_TABLE_OF_CONTENTS_PATIENT_APPLICATION_TITLE = '<text:p text:style-name="Normal"><text:s text:c="4"/><text:a xlink:href="#PatientApplicationRequirements" office:target-frame-name="_top" xlink:show="replace">II. Patient Application Requirements</text:a></text:p>\n'

DEFAULT_TABLE_OF_CONTENTS_CLINICIAN_APPLICATION_TITLE = '<text:p text:style-name="Normal"><text:s text:c="4"/><text:a xlink:href="#ClinicianApplicationRequirements" office:target-frame-name="_top" xlink:show="replace">III. Clinician Application Requirements</text:a></text:p>\n'

DEFAULT_TABLE_OF_CONTENTS_API_APPLICATION_TITLE = '<text:p text:style-name="Normal"><text:s text:c="4"/><text:a xlink:href="#APIApplicationRequirements" office:target-frame-name="_top" xlink:show="replace">IV. API Application Requirements</text:a></text:p>\n'

TABLE_OF_CONTENTS_TITLE_STRING = '''<text:p text:style-name="Normal"><text:s text:c="{0}"/><text:a xlink:href="#{1}" office:target-frame-name="_top"
                                    xlink:show="replace">{2}. {3}</text:a></text:p>\n'''

TABLE_OF_CONTENTS_REVISION_TITLE_STRING = '''<text:p text:style-name="Normal"><text:s text:c="4"/><text:a xlink:href="#DocumentRevision" office:target-frame-name="_top" xlink:show="replace">IV. Document Revision</text:a></text:p>\n'''



#Default tables for the Reference, and Acronyms & Definitions, and Document Revision sections
DEFAULT_REFERENCE_TABLE = '''<text:p text:style-name="P101"/><table:table table:style-name="Table102"><table:table-columns><table:table-column table:style-name="TableColumn103"/><table:table-column table:style-name="TableColumn104"/>
                            </table:table-columns><table:table-header-rows><table:table-row table:style-name="TableRow105"><table:table-cell table:style-name="TableCell106">
                            <text:p text:style-name="P107">Document</text:p></table:table-cell><table:table-cell table:style-name="TableCell108"><text:p text:style-name="P109">Description</text:p>
                            </table:table-cell></table:table-row></table:table-header-rows><table:table-row table:style-name="TableRow110"><table:table-cell table:style-name="TableCell111">
                            <text:p text:style-name="P112">TECH20087</text:p></table:table-cell><table:table-cell table:style-name="TableCell113"><text:p text:style-name="P114">VERA Project Quality Plan</text:p>
                            </table:table-cell></table:table-row><table:table-row table:style-name="TableRow115"><table:table-cell table:style-name="TableCell116"><text:p text:style-name="P117">SOP20004</text:p>
                            </table:table-cell><table:table-cell table:style-name="TableCell118"><text:p text:style-name="P119">Design Controls SOP<text:s/></text:p></table:table-cell></table:table-row></table:table>
                            <text:p text:style-name="Normal"/>'''

DEFAULT_ACRONYMS_TABLE = '''<text:p text:style-name="Normal"/><table:table table:style-name="Table122"><table:table-columns><table:table-column table:style-name="TableColumn123"/><table:table-column table:style-name="TableColumn124"/>
</table:table-columns><table:table-header-rows><table:table-row table:style-name="TableRow125"><table:table-cell table:style-name="TableCell126"><text:p text:style-name="P127">Term</text:p></table:table-cell>
<table:table-cell table:style-name="TableCell128"><text:p text:style-name="P129">Description</text:p></table:table-cell></table:table-row></table:table-header-rows><table:table-row table:style-name="TableRow130">
<table:table-cell table:style-name="TableCell131"><text:p text:style-name="P132">AES</text:p></table:table-cell><table:table-cell table:style-name="TableCell133"><text:p text:style-name="P134">Advanced Encryption 
Standard</text:p></table:table-cell></table:table-row><table:table-row table:style-name="TableRow135"><table:table-cell table:style-name="TableCell136"><text:p text:style-name="P137">API</text:p></table:table-cell>
<table:table-cell table:style-name="TableCell138"><text:p text:style-name="P139">Application Program Interface</text:p></table:table-cell></table:table-row><table:table-row table:style-name="TableRow140">
<table:table-cell table:style-name="TableCell141"><text:p text:style-name="P142">Application</text:p></table:table-cell><table:table-cell table:style-name="TableCell143"><text:p text:style-name="P144">Software 
executable application</text:p></table:table-cell></table:table-row><table:table-row table:style-name="TableRow145"><table:table-cell table:style-name="TableCell146"><text:p text:style-name="P147">Avatar</text:p>
</table:table-cell><table:table-cell table:style-name="TableCell148"><text:p text:style-name="P149"><text:span text:style-name="T150">A computer-generated animated human figure, used to illustrate a pose or motion.
</text:span></text:p></table:table-cell></table:table-row><table:table-row table:style-name="TableRow151"><table:table-cell table:style-name="TableCell152"><text:p text:style-name="P153">Confidence</text:p>
</table:table-cell><table:table-cell table:style-name="TableCell154"><text:p text:style-name="P155"><text:span text:style-name="T156">A number<text:s/></text:span><text:span text:style-name="T157">
value returned by the Microsoft Kinect software system on a per joint basis</text:span></text:p></table:table-cell></table:table-row><table:table-row table:style-name="TableRow158"><table:table-cell 
table:style-name="TableCell159"><text:p text:style-name="P160">Exercise</text:p></table:table-cell><table:table-cell table:style-name="TableCell161"><text:p text:style-name="P162"><text:span text:style-name="T163">
A specific movement, ending where it started.</text:span></text:p></table:table-cell></table:table-row><table:table-row table:style-name="TableRow164"><table:table-cell table:style-name="TableCell165">
<text:p text:style-name="P166">EULA</text:p></table:table-cell><table:table-cell table:style-name="TableCell167"><text:p text:style-name="P168">End User License Agreement</text:p></table:table-cell></table:table-row>
<table:table-row table:style-name="TableRow169"><table:table-cell table:style-name="TableCell170"><text:p text:style-name="P171">HTTP</text:p></table:table-cell><table:table-cell table:style-name="TableCell172">
<text:p text:style-name="P173">Hypertext Transfer Protocol</text:p></table:table-cell></table:table-row><table:table-row table:style-name="TableRow174"><table:table-cell table:style-name="TableCell175">
<text:p text:style-name="P176">HTTPS</text:p></table:table-cell><table:table-cell table:style-name="TableCell177"><text:p text:style-name="P178">HTTP Secure</text:p></table:table-cell></table:table-row>
<table:table-row table:style-name="TableRow179"><table:table-cell table:style-name="TableCell180"><text:p text:style-name="P181">Motion</text:p></table:table-cell><table:table-cell table:style-name="TableCell182">
<text:p text:style-name="P183"><text:span text:style-name="T184">A specific movement involving<text:s/></text:span><text:span text:style-name="T185">one or more joints</text:span></text:p></table:table-cell>
</table:table-row><table:table-row table:style-name="TableRow186"><table:table-cell table:style-name="TableCell187"><text:p text:style-name="P188">PT</text:p></table:table-cell><table:table-cell 
table:style-name="TableCell189"><text:p text:style-name="P190"><text:span text:style-name="T191">Physical Therapist.</text:span></text:p></table:table-cell></table:table-row><table:table-row 
table:style-name="TableRow192"><table:table-cell table:style-name="TableCell193"><text:p text:style-name="P194">Rep(s)</text:p></table:table-cell><table:table-cell table:style-name="TableCell195">
<text:p text:style-name="P196"><text:span text:style-name="T197">Repetition(s)- Number of times an Exercise is repeated</text:span></text:p></table:table-cell></table:table-row>
<table:table-row table:style-name="TableRow198"><table:table-cell table:style-name="TableCell199"><text:p text:style-name="P200">Set(s)</text:p></table:table-cell><table:table-cell table:style-name="TableCell201">
<text:p text:style-name="P202"><text:span text:style-name="T203">A defined number of repetitions followed by a brief rest</text:span></text:p></table:table-cell></table:table-row><table:table-row 
table:style-name="TableRow204"><table:table-cell table:style-name="TableCell205"><text:p text:style-name="P206">SMS</text:p></table:table-cell><table:table-cell table:style-name="TableCell207">
<text:p text:style-name="P208"><text:span text:style-name="T209">Short Message Service; a system that enables cellular phone users to send and<text:s/></text:span><text:span text:style-name="T210">
receive text messages.</text:span></text:p></table:table-cell></table:table-row><table:table-row table:style-name="TableRow211"><table:table-cell table:style-name="TableCell212">
<text:p text:style-name="P213">Stat(s)</text:p></table:table-cell><table:table-cell table:style-name="TableCell214"><text:p text:style-name="P215">Statistic(s)</text:p></table:table-cell></table:table-row>
<table:table-row table:style-name="TableRow216"><table:table-cell table:style-name="TableCell217"><text:p text:style-name="P218">ROM</text:p></table:table-cell><table:table-cell table:style-name="TableCell219">
<text:p text:style-name="P220">Range of Motion</text:p></table:table-cell></table:table-row><table:table-row table:style-name="TableRow221"><table:table-cell table:style-name="TableCell222">
<text:p text:style-name="P223">SOAP</text:p></table:table-cell><table:table-cell table:style-name="TableCell224"><text:p text:style-name="P225">Simple Object Access Protocol</text:p></table:table-cell>
</table:table-row><table:table-row table:style-name="TableRow226"><table:table-cell table:style-name="TableCell227"><text:p text:style-name="P228">Therapy Plan</text:p></table:table-cell>
<table:table-cell table:style-name="TableCell229"><text:p text:style-name="P230">A group of sessions repeated for a number of days or weeks.</text:p><text:p text:style-name="P231"/></table:table-cell>
</table:table-row><table:table-row table:style-name="TableRow232"><table:table-cell table:style-name="TableCell233"><text:p text:style-name="P234">Therapy Session</text:p></table:table-cell>
<table:table-cell table:style-name="TableCell235"><text:p text:style-name="P236"><text:span text:style-name="T237">A group of exercises repeated each with its own number of reps</text:span>
<text:span text:style-name="T238"><text:s/>and sets.</text:span></text:p></table:table-cell></table:table-row><text:soft-page-break/><table:table-row table:style-name="TableRow239">
<table:table-cell table:style-name="TableCell240"><text:p text:style-name="P241">UI</text:p></table:table-cell><table:table-cell table:style-name="TableCell242"><text:p text:style-name="P243">
User Interface</text:p><text:p text:style-name="P244"/></table:table-cell></table:table-row><table:table-row table:style-name="TableRow245"><table:table-cell table:style-name="TableCell246">
<text:p text:style-name="P247">WCF</text:p></table:table-cell><table:table-cell table:style-name="TableCell248"><text:p text:style-name="P249">Windows Communication Foundation</text:p></table:table-cell>
</table:table-row></table:table><text:p text:style-name="Normal"/>'''

DEFAULT_REVISION_TABLE = '''<text:soft-page-break/><text:h text:style-name="P38"></text:h><text:p text:style-name="P318"><text:bookmark-start text:name="DocumentRevision"/>
DOCUMENT REVISION:<text:bookmark-end text:name="DocumentRevision"/></text:p><table:table table:style-name="Table319"><table:table-columns><table:table-column 
table:style-name="TableColumn320"/><table:table-column table:style-name="TableColumn321"/><table:table-column table:style-name="TableColumn322"/><table:table-column table:style-name="TableColumn323"/>
</table:table-columns><table:table-row table:style-name="TableRow324"><table:table-cell table:style-name="TableCell325"><text:p text:style-name="P326">DCO</text:p></table:table-cell><table:table-cell 
table:style-name="TableCell327"><text:p text:style-name="P328">Rev.</text:p></table:table-cell><table:table-cell table:style-name="TableCell329"><text:p text:style-name="P330">Description of Change</text:p>
</table:table-cell><table:table-cell table:style-name="TableCell331"><text:p text:style-name="P332">Release Date</text:p></table:table-cell></table:table-row><table:table-row table:style-name="TableRow333">
<table:table-cell table:style-name="TableCell334"><text:p text:style-name="P335">069</text:p></table:table-cell><table:table-cell table:style-name="TableCell336"><text:p text:style-name="P337">1</text:p>
</table:table-cell><table:table-cell table:style-name="TableCell338"><text:p text:style-name="P339">Initial release.</text:p></table:table-cell><table:table-cell table:style-name="TableCell340">
<text:p text:style-name="P341">4-23-14</text:p></table:table-cell></table:table-row><table:table-row table:style-name="TableRow342"><table:table-cell table:style-name="TableCell343">
<text:p text:style-name="P344">077</text:p></table:table-cell><table:table-cell table:style-name="TableCell345"><text:p text:style-name="P346">2</text:p></table:table-cell><table:table-cell 
table:style-name="TableCell347"><text:p text:style-name="P348">Addition of Clinician Web Application</text:p></table:table-cell><table:table-cell table:style-name="TableCell349"><text:p 
text:style-name="P350">5-09-14</text:p></table:table-cell></table:table-row></table:table>'''



#Constants for Introduction section
INTRODUCTION_TITLE_STRING = '''<text:soft-page-break/><text:p text:style-name="P67"><text:s text:c="4"/><text:bookmark-start text:name="Introduction"/><text:span text:style-name="T96">I.</text:span><text:span text:style-name="T97">
                               <text:tab/><text:s/>Introduction</text:span><text:bookmark-end text:name="Introduction"/></text:p>\n'''

INTRODUCTION_SECONDARY_TITLE_STRING = '''<text:p text:style-name="Normal"><text:bookmark-start text:name="{0}"/><text:s text:c="4"/><text:span text:style-name="T98">{1}. {2}
                                        </text:span><text:bookmark-end text:name="{3}"/></text:p>\n'''


#Constants for Numbered List section
ORDERED_LIST_MAIN_TITLE_STRING = '''<text:p text:style-name="Normal"><text:bookmark-start text:name="{0}"/><text:s text:c="4"/><text:span text:style-name="T290">{1}. {2}
                            </text:span><text:bookmark-end text:name="{3}"/></text:p>\n'''

ORDERED_LIST_SECONDARY_TITLE_STRING = '''<text:p text:style-name="Normal"><text:bookmark-start text:name="{0}"/><text:span text:style-name="T291">{1}. {2}
                                        </text:span><text:bookmark-end text:name="{3}"/></text:p>\n'''

PATIENT_APPLICATION_HEADER_STRING = '''<text:h text:style-name="P38"></text:h><text:soft-page-break/><text:p text:style-name="P259"><text:s text:c="4"/><text:bookmark-start text:name="PatientApplicationRequirements"/><text:span 
                    text:style-name="T96">II.</text:span><text:span text:style-name="T97"><text:tab/><text:s/>Patient Application Requirements</text:span><text:bookmark-end text:name="PatientApplicationRequirements"/></text:p>\n'''                                       

CLINICIAN_APPLICATION_HEADER_STRING = '''<text:h text:style-name="P38"></text:h><text:soft-page-break/><text:p text:style-name="P259"><text:s text:c="4"/><text:bookmark-start text:name="ClinicianApplicationRequirements"/><text:span 
                    text:style-name="T96">III.</text:span><text:span text:style-name="T97"><text:tab/><text:s/>Clinician Application Requirements</text:span><text:bookmark-end text:name="ClinicianApplicationRequirements"/></text:p>\n'''

API_APPLICATION_HEADER_STRING = '''<text:h text:style-name="P38"></text:h><text:soft-page-break/><text:p text:style-name="P259"><text:s text:c="4"/><text:bookmark-start text:name="APIApplicationRequirements"/><text:span 
                    text:style-name="T96">IV.</text:span><text:span text:style-name="T97"><text:tab/><text:s/>API Application Requirements</text:span><text:bookmark-end text:name="APIApplicationRequirements"/></text:p>\n'''


#Ending document constant
DEFAULT_CONTENT_ENDING = '</office:text></office:body></office:document-content>'





class RequirementIdentifierStringGenerator:
    '''This class generates requirement identifier strings. It's meant to be an
    unusually internally-complex iterator, so that you tell it about the indent
    level of your current requirement, it adjusts its internals appropriately,
    and then when you ask what the resulting identifier string is, it tells you.
    Note that "identifier string" here refers to the prefix of a requirement,
    or, actually, any item in a hierarchical ordered list. E.g:
        A. Top-level thing
            A.1. Second-level thing
                A.1.2. Third-level thing
                A.1.3. Another third-level thing
    and so on. "A.1.2" and "A.1.3" are the strings this class generates. Asking
    around our office stuffed with smart people I couldn't find a better name
    than "identifier string", so deal.
    '''

    def __init__(self, alphabetList):
        self.lists = []
        self.alphaList = alphabetList

        
    def GetCurrentIdentifierString(self):
        '''Return e.g. "A.1.1.4", based on the contents of 
            self.lists.
        '''
        ctr = 0
        toGoOut = ""
        for currentIdentifierLevel in self.lists:
            if(ctr == 0):
                # First ordinal in the identifier string should be a Caps-character.
                toGoOut += (self.alphaList[currentIdentifierLevel])
            else:
                toGoOut += (str(currentIdentifierLevel + 1))
                
            if(ctr + 1 < len(self.lists)):
                toGoOut += (".")

            ctr += 1
        return toGoOut


    def SetNextRequirementIndentLevel(self, indentLevel):
        if indentLevel + 1 == len(self.lists):
            self.lists[indentLevel] += 1
            
        if indentLevel + 1 > len(self.lists):
            self.lists.append(0)

        if indentLevel + 1 < len(self.lists):
            #colon is inclusive
            self.lists = self.lists[:indentLevel + 1]
            self.lists[indentLevel] += 1





class IndentCodeList:
    '''A sequential list of the stylesheet codes that correspond to an
       "indent level", which should be both the number of tabs before a
       particular item in a sequential list and the number of ordinals in
       its identifier string. Codes are things like "s54", "s55", "s56".
    '''

    def __init__(self):
        self.idList = [0]


    def Parse(self, inputPath):
        self.openFile = open(inputPath)
        for line in self.openFile:
            #16:-3 signifies the area in this line that contains the ids, i.e. s24 or s25
            if re.search('ss:ID="s', line):
                self.idList.append(line[16:-3])
        del self.idList[1:4]
        '''Remove first 3 id's in list because they do not contain any pertinent information.
           Keep zero as first element so numbers are incrimented instead of letters
           and looks like: A.
                            A.1
                             A.1.1
                              A.1.2
                               A.1.3
           instead of: A.
                       B.
                       C.
        '''

        
    def GetIndentLevelFromIndentCode(self, indentCodeIn):
        idListIndentLevelIndex = self.idList.index(indentCodeIn)
        #return current index, as well as the index of the last value in the array
        return idListIndentLevelIndex


    def GetLastIndentLevelFromIDList(self):
        return len(self.idList)-1
        




class JiraClient:
    '''This class accesses Reflexion Health's Jira server to retrieve information about Issue Types and Properties.
        This is done by logging in through Jira's Basic Authentication (basic_auth), and using that object to get
        a list of Priority Resources as well as a list of Issue Type Resources from the Jira server.
    '''

    def __init__(self, username, password):
        self.jiraOptions = {'server' : JIRA_SERVER}
        self.priorityTypeList = []
        self.issueTypeList = []
        self.username = username
        self.password = password

    def JiraLogin(self):
        print(LOGIN_WAIT_STRING)
        while True:
            if self.username is None or self.password is None:
                self.username, self.password = GetUsernameAndPassword()
            try:
                self.authenticateAndConnect = jira.client.JIRA(options = self.jiraOptions, basic_auth = (self.username, self.password))
            except jira.utils.JIRAError as e:
                if(e.status_code == 401):
                    print('ERROR: Wrong Username or Password')
                    self.username = None
                    self.password = None
                else:
                    raise
            else:
                print(LOGIN_SUCCESS_STRING)
                break

    def SetIssuePrioritiesAndIssueTypes(self):
        jiraDatabase = self.authenticateAndConnect

        #Get Priority resource types from Jira Server (i.e. "1 - Highest Priority")
        jiraPrioritiesList= jiraDatabase.priorities()
        
        #Get Issue resource types from Jira Server (i.e. 'Bug', 'Requirement', ect)
        jiraIssueTypeList = jiraDatabase.issue_types()

        for priority in jiraPrioritiesList:
            self.priorityTypeList.append(priority.name)

        for issueType in jiraIssueTypeList:
            self.issueTypeList.append(issueType.name)

    
    def GetIssuePriorities(self):
        return self.priorityTypeList


    def GetIssueTypes(self):
        return self.issueTypeList






def GetUsernameAndPassword():
    username = raw_input('Jira Username: ')
    password = getpass.getpass('Jira Password: ')
    return username, password
    


def ReadContentHeading(argsobj):
    '''This function reads an external (and very long) text file called defaultContentHeading.txt, which contains
    information regarding the tags and hyperlink information correlating to the ordered list, table of contents
    and table items in the document.
    This function returns the entire document as a string
    '''
    headingFile = open(argsobj, 'r')
    contentHeading = headingFile.read()
    return contentHeading



def CreateAlphaList():
    '''Creates a list of the alphabet for use in the RequirementIdentifierStringGenerator Class.
    '''
    alphaList = []
    for c in range(ord('A'), ord('Z')+1):
        alphaList.append(chr(c))
    return alphaList

    


def WriteTableOfContents(rowElement, RequirementIdentifierStringGenerator, indentCodeListIdentifierObject, odfDocument, jiraIssueType, jiraIssuePriorities):
    '''Generates a list of hyperlinked table of contents linking to Main and Secondary titles within the numbered list found in the
        WriteNumberedList Function.
    '''
    for child in rowElement:
        #Get Jira ticket number as object to exclude when writing
        jiraTicketLink = child.attrib.get(TICKET_CELL)
        
        #Get indent level of summaries
        xmlIndentValueString = child.attrib.get(INDENT_LEVEL_ID)

        #Create Jira List objects for the Issue Types (i.e. Requirement, Bug, ect) and Priority Types (i.e. 1 - Highest Priority, ect)
        jiraIssueTypeList =  jiraIssueType
        jiraIssuePriorityList =  jiraIssuePriorities
        
        #Exclude ticket number when writing list of srs tickets
        if jiraTicketLink:
            continue

        #If a cell in the xml does not contain empty, continue, otherwise it will return NoneType and break
        if child.find(DATA_CELL) == None:
            continue


        #Child.find(DATA_CELL).text contains the text for each requirement
        requirementTextInDataCell = child.find(DATA_CELL).text
        
        #Main titles have no indents
        if xmlIndentValueString is None:
            if requirementTextInDataCell in jiraIssueTypeList:
                continue

            elif requirementTextInDataCell in jiraIssuePriorityList:
                continue

            else:
                RequirementIdentifierStringGenerator.SetNextRequirementIndentLevel(0)
                odfDocument.write(TABLE_OF_CONTENTS_TITLE_STRING.format(8, requirementTextInDataCell.replace(' ',''), RequirementIdentifierStringGenerator.GetCurrentIdentifierString(), requirementTextInDataCell))
                continue


        #If the indentValue is not in idList, it was intentionally excluded, and therefore not necessary. Most likely an empty link, or a link containing data
        #that is not pertinent
        if xmlIndentValueString not in indentCodeListIdentifierObject.idList:
            continue

        #Get indentLevel (i.e. '1', '2', '3', ect)
        indentLevel = indentCodeListIdentifierObject.GetIndentLevelFromIndentCode(xmlIndentValueString)

        #Secondary titles appear as 4th element in idList (idList[3])
        if indentLevel == 1:
            if requirementTextInDataCell in jiraIssueTypeList:
                continue

            elif requirementTextInDataCell in jiraIssuePriorityList:
                continue

            else:
                RequirementIdentifierStringGenerator.SetNextRequirementIndentLevel(indentLevel)
                odfDocument.write(TABLE_OF_CONTENTS_TITLE_STRING.format(12, requirementTextInDataCell.replace(' ',''), RequirementIdentifierStringGenerator.GetCurrentIdentifierString(), requirementTextInDataCell))
                continue
        else:
            if indentLevel != 1 or xmlIndentValueString != None:
                continue
            
            else:
                print ('Error writing table of Contents')



def WriteIntroduction(odfDocument, alphaList):
    '''Generates a standard Introduction section in the document, indicated by the introductionItems list.append
        Also included in this section are two tables via the References and Acronyms & Definitions sections
    '''
    introductionItems = ['Purpose', 'Scope', 'References', 'Acronyms &amp; Definitions', 'Users', 'Applications']
    
    #Hard code Introduction title
    odfDocument.write(INTRODUCTION_TITLE_STRING)
    
    #Iterate through Introduction subtitles
    for introItem in introductionItems:
        if introItem == 'References':
            odfDocument.write(INTRODUCTION_SECONDARY_TITLE_STRING.format(introItem.replace(' ',''), alphaList[introductionItems.index(introItem)], introItem, introItem.replace(' ','')))
            #Write in the table for References
            odfDocument.write(DEFAULT_REFERENCE_TABLE)
            
        elif introItem == 'Acronyms &amp; Definitions':
            odfDocument.write(INTRODUCTION_SECONDARY_TITLE_STRING.format(introItem.replace(' ',''), alphaList[introductionItems.index(introItem)], introItem, introItem.replace(' ','')))
            #Write in the table for Acronyms and Definitions
            odfDocument.write(DEFAULT_ACRONYMS_TABLE)

        #Write all other items in introductionItems. Note: .replace() is because hyperlink id's (text:name) in the table of content do not contain whitespace, so we are removing them here so that id's match
        else:
            odfDocument.write(INTRODUCTION_SECONDARY_TITLE_STRING.format(introItem.replace(' ',''), alphaList[introductionItems.index(introItem)], introItem, introItem.replace(' ','')))
            
    


def WriteNumberedList(rowElement, RequirementIdentifierStringGenerator, indentCodeListIdentifierObject, odfDocument, jiraIssueType, jiraIssuePriorities):
    '''This function takes in a row/cell from the xml (rowElement), the RequirementIdentifierStringGenerator which gets the current prefix for each title (i.e. A. , A.1 , A.2.1 , ect.)
        It also takes in odfDocument, which is the document that is being written into, and codeListClassObject which is the
    '''
    for child in rowElement:
        jiraTicketLink = child.attrib.get(TICKET_CELL)
        xmlIndentValueString = child.attrib.get(INDENT_LEVEL_ID)

        #Create Jira List objects for the Issue Types (i.e. Requirement, Bug, ect) and Priority Types (i.e. 1 - Highest Priority, ect)
        jiraIssueTypeList = jiraIssueType
        jiraIssuePriorityList = jiraIssuePriorities
        
        if jiraTicketLink:
            continue

        #If a cell in the xml does not contain empty, continue, otherwise it will return NoneType and break
        if child.find(DATA_CELL) == None:
            continue


        #Child.find(DATA_CELL).text contains the text for each requirement
        requirementTextInDataCell = child.find(DATA_CELL).text
            
        if xmlIndentValueString is None:
            if requirementTextInDataCell in jiraIssueTypeList:
                continue

            elif requirementTextInDataCell in jiraIssuePriorityList:
                continue

            else:
                RequirementIdentifierStringGenerator.SetNextRequirementIndentLevel(0)
                odfDocument.write(ORDERED_LIST_MAIN_TITLE_STRING.format(requirementTextInDataCell.replace(' ',''), RequirementIdentifierStringGenerator.GetCurrentIdentifierString(), requirementTextInDataCell, requirementTextInDataCell.replace(' ','')))
                continue


        #If an indent ID (i.e. 's65', 's66' is not in the Indent Code List, continue, otherwise we will be trying to
        #use an indent ID that is not accessible
        if xmlIndentValueString not in indentCodeListIdentifierObject.idList:
                continue


        #Get indentLevel (i.e. '1', '2', '3', ect)
        indentLevel = indentCodeListIdentifierObject.GetIndentLevelFromIndentCode(xmlIndentValueString)

        #The last indent level is treated differently in the xml because it contains slightly different values in the ODT format.
        lastIndentLevel = indentCodeListIdentifierObject.GetLastIndentLevelFromIDList()

        
        #Isolate last indent because xml treats it as a different indentation
        if indentLevel == lastIndentLevel:
            #Indicates the number of tabs by stripping the periods from prefix of summary (i.e. A.1.2.) to get number of tabs it should have relative to other items on this numbered list
            tabIndentValue =  len(RequirementIdentifierStringGenerator.GetCurrentIdentifierString().replace('.',''))
            RequirementIdentifierStringGenerator.SetNextRequirementIndentLevel(indentLevel)            
            odfDocument.write('<text:p text:style-name="Normal"><text:s text:c="' + str((tabIndentValue) * 4) + '"/>' + RequirementIdentifierStringGenerator.GetCurrentIdentifierString() + '. ' + requirementTextInDataCell + '</text:p>\n')
            continue

        #Isolate indent in 4th element of idList because it is a Secondary Title, and must be bolded
        elif indentLevel == 1:
            RequirementIdentifierStringGenerator.SetNextRequirementIndentLevel(indentLevel)
            odfDocument.write(ORDERED_LIST_SECONDARY_TITLE_STRING.format(requirementTextInDataCell.replace(' ',''), RequirementIdentifierStringGenerator.GetCurrentIdentifierString(), requirementTextInDataCell, requirementTextInDataCell.replace(' ','')))
            continue

        elif indentLevel:
            #Check if text in Cell are an Issue Type or Priority Type. If so, do not write to text file
            if requirementTextInDataCell in jiraIssueTypeList:
                continue

            if requirementTextInDataCell in jiraIssuePriorityList:
                continue
            
            else:
                #idElement is the element in the array that is identical to the 'indents' value i.e. s65, s66, s67, ect
                RequirementIdentifierStringGenerator.SetNextRequirementIndentLevel(indentLevel)
                #Multiply str(indentLevel*4) by the 4 because tab indents are measured by multiples of 4 in Microsoft Word
                odfDocument.write('<text:p text:style-name="Normal"><text:s text:c="' + str(indentLevel*4) + '"/>' + RequirementIdentifierStringGenerator.GetCurrentIdentifierString() + ' ' + requirementTextInDataCell + '</text:p>\n')
                continue
        else:
            print('Error writing the ordered list')

       
def Parse():
    #Create input and output path parameters
    parser = argparse.ArgumentParser(description = 'Writes Structure to an SRS-style, odf-format document')
    parser.add_argument('-ip', '--patient_input_path', type=str, default=DEFAULT_PATIENT_INPUT_PATH, help='Path to the Patient Structure xml file')
    parser.add_argument('-ic', '--clinician_input_path', type=str, default=DEFAULT_CLINICIAN_INPUT_PATH, help='Path to the Clinician Structure xml file')
    parser.add_argument('-ia', '--api_input_path', type=str, default=DEFAULT_API_INPUT_PATH, help='Path to the API Structure xml file')
    parser.add_argument('-o', '--output_path', type=str, default=DEFAULT_OUTPUT_PATH, help='Path to content.xml file')
    parser.add_argument('-hf', '--heading_file', type=str, default=DEFAULT_HEADING_FILE_PATH, help='Path to defaultContentHeadingFile.txt')
    args = parser.parse_args()
    return args
        


def main():
    #Instantiate argparse objects, the writing of the default content heading for the content.xml file, and the alphabet list
    argsobj = Parse()
    defaultContentHeading = ReadContentHeading(argsobj.heading_file)
    alphaList = CreateAlphaList()
    jiraUsername, jiraPassword = GetUsernameAndPassword()


    #Authenticate Jira Client and create Jira Priority and Issue Type lists
    jiraDatabaseObject = JiraClient(jiraUsername, jiraPassword)
    jiraDatabaseObject.JiraLogin()
    jiraDatabaseObject.SetIssuePrioritiesAndIssueTypes()
    jiraIssueTypes = jiraDatabaseObject.GetIssueTypes()
    jiraIssuePriorities = jiraDatabaseObject.GetIssuePriorities()
    
    #Open file for writing
    odtOutputContentFile = open(argsobj.output_path, 'w')


    '''Begin Table Of Contents and Header section
       ------------------------------------------
    '''
    #Write default header for entire document and first default entries in the Table of Contents
    odtOutputContentFile.write(defaultContentHeading)
    odtOutputContentFile.write(DEFAULT_TABLE_OF_CONTENTS_INTRODUCTION_ITEMS)

    #Create a list of input paths to parse through when writing Table of Contents and Numbered List
    jiraStructurePathList = [argsobj.patient_input_path]
    tableOfContentsTitleStringList = [DEFAULT_TABLE_OF_CONTENTS_PATIENT_APPLICATION_TITLE, DEFAULT_TABLE_OF_CONTENTS_CLINICIAN_APPLICATION_TITLE, DEFAULT_TABLE_OF_CONTENTS_API_APPLICATION_TITLE]
    titleStringCounter = 0

    for jiraStructurePath in jiraStructurePathList:

        #Instantiate IndentCodeList and RequirementIdentifierStringGenerator objects to parse through and determine indent levels of new xml file
        indentCodeListIdentifier = IndentCodeList()
        indentCodeListIdentifier.Parse(jiraStructurePath)
        RequirementIdentifierStringGeneratorClass = RequirementIdentifierStringGenerator(alphaList)

        #Create object to parse through the given jira structure
        jiraStructureTree = etree.parse(jiraStructurePath)          

        odtOutputContentFile.write(tableOfContentsTitleStringList[titleStringCounter])

        '''Iterate through jiraStructureTree to write a table of contents based on the significant titles in
            the jiraStructureTree xml file. These items are commonly found in the <Data></Data> blocks within a <Cell></Cell> which is normally located
            within a <Row></Row>'''
        for value in jiraStructureTree.iter(ROW_CELL):
            WriteTableOfContents(value, RequirementIdentifierStringGeneratorClass, indentCodeListIdentifier, odtOutputContentFile, jiraIssueTypes, jiraIssuePriorities)

        titleStringCounter += 1                
    odtOutputContentFile.write(TABLE_OF_CONTENTS_REVISION_TITLE_STRING)



    '''Begin Introduction Section
       --------------------------
    '''
    
    WriteIntroduction(odtOutputContentFile, alphaList)



    '''Begin Numbered List Section
       ---------------------------
    '''
    numberedListHeaderStringList = [PATIENT_APPLICATION_HEADER_STRING, CLINICIAN_APPLICATION_HEADER_STRING, API_APPLICATION_HEADER_STRING]
    titleStringCounter = 0
    
    for jiraStructurePath in jiraStructurePathList:
        
        #Re-initialize these class objects to reset all lists and the information contained within them
        indentCodeListIdentifier = IndentCodeList()
        indentCodeListIdentifier.Parse(jiraStructurePath)
        RequirementIdentifierStringGeneratorClass = RequirementIdentifierStringGenerator(alphaList)
        
        #Create object to parse through the given jira structure
        jiraStructureTree = etree.parse(jiraStructurePath)

        odtOutputContentFile.write(numberedListHeaderStringList[titleStringCounter])

        '''Iterate through jiraStructureTree to write a numbered list based on each of items in each of the Jira Structure xml documents.
            These items are commonly found in the <Data></Data> blocks within a <Cell></Cell> which is normally located
            within a <Row></Row>'''
        for value in jiraStructureTree.iter(ROW_CELL):
            WriteNumberedList(value, RequirementIdentifierStringGeneratorClass, indentCodeListIdentifier, odtOutputContentFile, jiraIssueTypes, jiraIssuePriorities)

        titleStringCounter += 1


    #Write in default revision table as it is always the last item in the SRS
    odtOutputContentFile.write(DEFAULT_REVISION_TABLE)
    odtOutputContentFile.write(DEFAULT_CONTENT_ENDING)

    print('\nYour ODT file is now ready!')



if __name__ == '__main__':
    main()
        
