# HG changeset patch # User Miguel Ángel Bárcena Rodríguez # Date 1288552053 -3600 # Node ID 2ac1551ad2ab976458fccb3443c3c10538572b16 # Parent a1703c4f29901c3c20788b9212b0e08efff25568 add code diff -r a1703c4f2990 -r 2ac1551ad2ab COPYING --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/COPYING Sun Oct 31 20:07:33 2010 +0100 @@ -0,0 +1,674 @@ + GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + Copyright (C) + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +. diff -r a1703c4f2990 -r 2ac1551ad2ab Generic/__init__.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Generic/__init__.py Sun Oct 31 20:07:33 2010 +0100 @@ -0,0 +1,31 @@ +# -*- coding: utf-8 -*- +## +## This file is part of pyArq-Presupuestos. +## +## Copyright (C) 2010 Miguel Ángel Bárcena Rodríguez +## +## +## pyArq-Presupuestos is free software: you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## pyArq-Presupuestos is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program. If not, see . +# +# define __all__ so 'from Generic import *' works +# +__all__ = [ + 'base', + 'config', + 'durusdatabase', + 'fiebdc', + 'globals', + 'openwith', + 'utils', + ] diff -r a1703c4f2990 -r 2ac1551ad2ab Generic/base.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Generic/base.py Sun Oct 31 20:07:33 2010 +0100 @@ -0,0 +1,3453 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +## File base.py +## This file is part of pyArq-Presupuestos. +## +## Copyright (C) 2010 Miguel Ángel Bárcena Rodríguez +## +## +## pyArq-Presupuestos is free software: you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## pyArq-Presupuestos is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program. If not, see . + +"""base module + +In this module are defined the data structures in the +classes: + * Record: data of each record + * ParamentricRecord: data of each parametric record + * Decomposition: data of the decomposition of each record + * Measure: data of the measure of each record + * MeasureLine: each measure line data + * Decimals: data of the decimal places of all the numbers in a budget + * Sheet: data of the sheet of conditions of a budget + * Budget: all data of a budget + * Company: company data + * Office: company office data + * File: file data + * RecordType: Record type data + +schema: + * Budget: + +-- __records: dictionary records { code : Record } + * Record: + +-- code: record code + +-- synonyms: list of synonym codes + +-- hierarchy: A integer number: + 0 -> root + 1 -> Chapter/Subchapter + 2 -> Other + +-- unit: unit of measure of the record + +-- summary: Short description of the record + +-- prices: List of Prices/Dates + +-- type + +-- subtype + "type" and "subtype": + 0 Without classifying + EA Auxiliary element + EU Unitary element + EC Complex element + EF Functional element + OB Construction site + PA Cost overrun + PU Unitary budget + 1 Labourforce + H Labourforce + 2 Machinery and auxiliary equipment + Q Machinery + % Auxiliary equipment + 3 Building materials + MC Cement + MCr Ceramic + MM Wood + MS Iron and steel + ME Energy + MCu Copper + MAl Aluminium + ML Bonding agents + M Others materials + Hierarchy type subtype + 0->root -> 0 -> None,OB + 1->[sub]chapter -> 0 -> None,PU + 2->Other -> 0 -> None,EA,EU,EC,EF,PA + 1 -> None,H + 2 -> None,Q,% + 3 -> None,MC,MCr,MM,MS,ME,MCu,Mal,ML,M + +-- parents: List of parent codes + +-- children: list of Decomposition + * Decomposition: + +-- position: Position of the child in the parent descomposition + TODO: change this: the position of the record in the budget + +-- code: child record code + +-- budget: list of budget and amended budget measures + * Measure: + +-- measure: Total result of measure + +-- lines: List of measure lines + * MeasureLine: + +-- type: Line type: + empty string -> Normal + 1 -> Parcial Subtotal + 2 -> Accumulated Subtotal + 3 -> Formula, the comment is a formula. + +-- comment: Can be a descriptive text or a formula + Valid Operator: '(', ')', '+', '-', '*', '/' and + '^' + Valid variable: 'a', 'b', 'c','d' y 'p' + (Pi=3.1415926) + +-- units: Number of Units (a) + +-- length: length (b) + +-- width: width (c) + +-- height: height (d) + +-- label: Record Identifiers that are used by some measure + programs + +-- factor: Factor + +-- yield_: Yield + +-- certification: list of certifications for months measures + * Measure + +-- real_cost: list of real cost of construction for months + measures + * Measure + +-- cost_goals: list of cost goals of construction for months + measures + * Measure + +-- cost_planned: list of costs planned and amended cost planned + measures + * Measure + +-- text: Long Description of the record + +-- sheet: Sheet of conditions object + * Sheet: + +-- sheet_dict: + { : {
: }} + +-- files: List of file object + +-- file + * Name + * Type + * Description + +-- __synonyms: synonyms dictionary. TODO + +-- __root: root record code + +-- __decimals: decimals dictionay = { int : Decimals } + * Decimals: + +-- DN: Number of decimal places of the field "equal-size parts" in the + measure lines. + Default: 2 decimal places. + +-- DD: Number of decimal places of the three dimensions in the + measure lines. + Default: 2 decimal places. + +-- DS: Number of decimal places of the total sum of a measure. + Default: 2 decimal places. + +-- DFP: Number of decimal places of the yield factor in a + decomposition of a budget record. + Default: 3 decimal places. + +-- DFC: Number of decimal places of the yield factor in a + decomposition of a chapter or subchapter, and in its measure lines. + Dafault: 3 decimal places. + +-- DFUO: Number of decimal places of the yield factor in a + decomposition of a unit of work. + Default: 3 decimal places. + +-- DFA: Number of decimal places of the yield factor in a + decomposition of a Auxiliary element. + Default: 3 decimal places. + +-- DRP: Number of decimal places of the yield in a decomposition + of a budget record. + Number of decumal places of the result of the multiplication of + the factor and the yield in a decompositon of a budget. + Default: 3 decimal places. + +-- DRC: Number of decimal places of the yield (or measure) in a + decomposition of a chapter or subchapter. + Number of decimal places of the result of the multiplictaion of + the yield (or measure) and the factor in a decomposition of a + chapter or subcharter. + Default: 3 decimal places. + +-- DRUO: Number of decimal places of the yield in a decomposition of a + unit of work. + Decimal places of the result of the multiplication of the yield + and the factor in a descomposition of a unit of work. + Default: 3 decimal places. + +-- DRA: Number of decimal places of the yield in a decompositon of a + auxiliar element. + Number of decimal places of the result of the multiplication of + the yield and the factor in a descomposition of a auxilar element. + Default: 3 decimal places. + +-- DP: Number of decimal places of the price of a budget. + Default: 2 decimal places. + +-- DC: Number of decimal places of the price of a chapter or + subchapter. + Default: 2 decimal places. + +-- DUO: Number of decimal places of the price of a unit of work. + Default: 2 decimal places. + +-- DEA: Number of decimal places of the price of a auxiliar element. + Default: 2 decimal places. + +-- DES: Number of decimal places of the price of the simple elements. + Default: 2 decimal places. + +-- DIR: Number of decimal places of the resulting amount to multiply + the total yield and the price of the elements of a unit of work. + (When there are not measures) + +-- DIM: Number of decimal places of the resulting amount to multiply + the total yield and the price of the elements of a unit of work. + (When there are measures) + +-- DIRC: Number of decimal places of the resulting amount to multiply + the total yield and the price of the elements of a budget, chapter + or a subchapter.(When there are not measures) + +-- DIMC: Number of decimal places of the resulting amount to multiply + the total yield and the price of the elements of a budget, chapter + or a subchapter. (When there are measures) + +-- DCD: Number of decimal places ot the resulting amount to sum the + direct costs of a unit of work (and auxiliar element). + Number of decimal places of the indirect costs. + Default: 2 decimal places. + +-- DIVISA: monetary unit. + +-- __percentages: percentages dictionary: + { "CI" : "", + "GG" : "", + "BI" : "", + "BAJA": "", + "IVA" : ""} + +-- __file_owner + +-- __title_list: titles list: [ "Header", ["Title1", "Title2", ... ] ] + +-- __title_index: A integer. The active group of Prices and Decimals. + +-- __sheet_sections: sheet sections dictionary { sheet_code : sheet_title } + +-- __sheet_fields: sheet fields dictionary { field_code : field_title } + +-- __sheet_paragraphs: sheet paragraphs dictionary + { paragraph_code : paragraph_text} + +-- __companys: Dictionary whith companys object + { company_code: company_object } + * Comapany: + +-- code: company code + +-- summary: short name + +-- name: long name + +-- offices: List of offices: + * Office: + +-- type: office type + "C" Central office. + "D" Local Office. + "R" Performer. + +-- subname: Office name + +-- address: Ofiice address + +-- postal_code: postal code + +-- town: town + +-- province: province/state + +-- country: country + +-- phone: list of phone numbers + +-- fax: list of fax numbers + +-- contact_person: Contact person in the office + +-- cif: CIF + +-- web: web page + +-- email: email +""" + +# Modules +import re +import datetime +import os + +# pyArq-Presupuestos modules +from Generic import fiebdc +from Generic import utils + +class Record(object): + """base.Record: + + Description: + Record object + Constructor: + base.Record(code, synonyms, hierarchy, unit, summary, prices, type, + subtype, text="") + Ancestry: + +-- object + +-- Record + Atributes: + "code": Code string + "recordType": RecordType object + "synonyms": List of synonym codes. + "parents":List of parent codes + "children": Decomposition list, + list of "Decomposition" instances + "unit": measure unit of the record + "summary": Short description of the record + "prices": List of prices/dates + "text": Long Description of the record + "sheet": Sheet of conditions object + "files": List of file object + "labels": List of record labels + Methods: + __getstate__(self) + __setstate__(self, tuple) + __init__(self, filename=None, budget=None) + {get/set}Code + {get/set}Synonyms + {get/set}RecordType + {get/set}Unit + {get/set}Summary + {get/set}Prices + addPrice + _validate_price_date + getPrice + getAmount + getDate + {get/set}Parents + appendParent + {get/set}children + appendChild + {get/set}Text + {get/set}Sheet + {get/set}Files + addFile + {get/set}Labels + addLabel + """ + __slots__ = ["_Record__code", "_Record__synonyms", + "_Record__recordType", "_Record__unit", + "_Record__summary", "_Record__prices", + "_Record__parents", "_Record__children", + "_Record__text", "_Record__sheet", + "_Record__files", "_Record__labels"] + + def __getstate__(self): + return (self.__code, self.__synonyms, self.__recordType, + self.__unit, self.__summary, self.__prices, + self.__parents, self.__children, self.__text, + self.__sheet, self.__files, self.__labels) + + def __setstate__(self, tuple): + self.__code = tuple[0] + self.__synonyms = tuple[1] + self.__recordType = tuple[2] + self.__unit = tuple[3] + self.__summary = tuple[4] + self.__prices = tuple[5] + self.__parents = tuple[6] + self.__children = tuple[7] + self.__text = tuple[8] + self.__sheet = tuple[9] + self.__files = tuple[10] + self.__labels = tuple[11] + + def __init__(self, decimals, code, synonyms, hierarchy, unit, summary, + prices, type, subtype, parents=[], text=""): + self.code = code + self.synonyms = synonyms + self.recordType = (hierarchy, type, subtype) + self.unit = unit + self.summary = summary + self.setPrices(prices, decimals) + self.parents = parents + self.children = [] + self.text = text + self.sheet = Sheet() + self.files = [] + self.labels = [] + + def getCode(self): + return self.__code + + def setCode(self, code): + """setCode(self,code) + + Sets the code, must be a valid code + """ + if not utils.is_valid_code(code)[0]: + raise ValueError, utils.mapping(_("Invalid code: $1"),(str(code),)) + self.__code = code + + def getSynonyms(self): + return self.__synonyms + + def setSynonyms(self,synonyms): + """setSynonyms(self,synonyms) + + Sets the synonyms codes of the record. + synonyms must fulfill: + - must be a list + - the items must be valid codes + """ + if not isinstance(synonyms, list): + raise TypeError, utils.mapping(_("Synonyms ($1) must be a list, " \ + "code: $2"), (str(synonyms), self.__code)) + for code in synonyms: + if not utils.is_valid_code(code)[0]: + raise ValueError, utils.mapping(_("Invalid Code in synomyms "\ + "list ($1) code: $2"), (str(code), self.__code)) + self.__synonyms = synonyms + + def getRecordType(self): + return self.__recordType + + def setRecordType(self, recordType): + """setRecordType(self, recordType) + + Set the record type. + recordType (hierarchy, type,subtype) + + hierarchy must be -1, 0, 1 or 2 + type must be 0, 1, 2, 3 or a empty string + subtype must be EA, EU, EC, EF, OB, PA, PU, H, Q, %, MC, MCr, + MM, MS, ME, MCu, MAl, ML, M, or a empty string + """ + _recordType = RecordType(recordType[0],recordType[1],recordType[2]) + self.__recordType = _recordType + + def getUnit(self): + return self.__unit + + def setUnit(self,unit): + """setUnit(self,unit) + + Set the unit of measure + The unit must be a string. + """ + if not isinstance(unit, str): + raise TypeError, utils.mapping(_("Unit ($1) must be a string: $2"), + (str(unit), self.__code)) + self.__unit = unit + + def getSummary(self): + return self.__summary + + def setSummary(self,summary): + """setSummary(self,summary) + + Set the summary of a record + The summary must be a string. + """ + if not isinstance(summary, str): + raise TypeError, utils.mapping(_("Summary ($1) must be a string: "\ + "$1"), (str(summary), self.__code)) + self.__summary = summary + + def getPrices(self): + return self.__prices + + def setPrices(self, prices, decimals): + """setPrice(self, prices, decimals) + + Set the price list of the record. + prices must fulfill: + - it must be a list + - the items must be a list with two items + - the first item: price must be a float + """ + if not isinstance(prices, list): + raise TypeError, utils.mapping(_("Prices ($1) must be a list: $2"), + (str(prices), self.__code)) + for index in range(len(prices)): + _price_date = prices[index] + _price_date = self._validate_price_date(_price_date, decimals) + prices[index] = _price_date + self.__prices = prices + + def addPrice(self, price_date, decimals): + """addPrice(self, price_date, decimals) + + Add a price to the price list of the record. + price must fulfill: + - must be a list with two items + - the first item: price must be a float + """ + price_date = self._validate_price_date(price_date, decimals) + self.__prices.append(price_date) + + def _validate_price_date(self, price_date, decimals): + if not isinstance(price_date, list) and len(price_date) == 2: + raise ValueError, utils.mapping(_("Price ($1) must be a list"\ + " with two items: $2"), (str(price_date), self.__code)) + _price = price_date[0] + _date = price_date[1] + if not isinstance(_price, float): + raise TypeError, utils.mapping(_("Price must be a float "\ + "number: $1"), (str(_price),)) + _D = decimals.getD(self.recordType) + _price = round(_price, _D) + price_date[0] = _price + # TODO: validate date + return price_date + + def getPrice(self, index_price): + if len(self.__prices) <= index_price: + raise IndexError, _("The record do not have this Price. Code: %s" + % self.__code) + return self.__prices[index_price][0] + + def getDate(self, index_price): + if len(self.__prices) <= index_price: + raise IndexError, _("The record do not have this Price") + return self.__prices[index_price][1] + + def getParents(self): + return self.__parents + + def setParents(self,parents): + """setParents(self,parents) + + Sets the list of parents codes of the record. + parents must fulfill + - it must be a list + - the items must be valid codes + """ + if not isinstance(parents, list): + raise TypeError, utils.mapping(_("Parents ($1) must be a list: $2"), + (str(parents), self.__code)) + for parent in parents: + if not utils.is_valid_code(parent)[0]: + raise ValueError, utils.mapping(_("Invalid parent code ($1) " \ + "in the record: $2"), (str(padre), self.__code)) + self.__parents = parents + + def appendParent(self, parent): + """appendParent(self, parent) + + parent must be a valid code + Append a parent to the list of parents codes of the record. + + """ + if not utils.is_valid_code(parent)[0]: + raise ValueError, utils.mapping(_("Invalid parent code ($1) " \ + "in the record: $2"), (str(parent), self.__code)) + self.__parents.append(parent) + + def getchildren(self): + return self.__children + + def setchildren(self,children): + """setchildren(self,children) + + Sets the list of children of a record + children must fulfill + - it must be a list + - the items must be instances of Decomposition class + """ + if not isinstance(children, list): + raise TypeError, utils.mapping(_("children ($1) must be a list, "\ + "record: $2"), (str(children), self.__code)) + for _child in children: + if not isinstance(_child, Decomposition): + raise ValueError, utils.mapping(_("child ($1) must be a "\ + "Decomposition object, record: $2"), + (str(_child), self.__code)) + _record_code = self.code + for _measure_list in [_child.budgetMeasures, _child.certification, + _child.real_cost, _child.cost_goals, + _child.cost_planned]: + if isinstance(_measure_list, list): + for _measure in _measure_list: + _measurerecordCode = _record_code + self.__children = children + + def appendChild(self, child_code, decimals, factor=0.0, yield_=0.0, + measure=0.0, measure_list=[], type ="", label=""): + """appendChildren(self, child_code, factor=0.0, yield_=0.0, + measure=0.0, measure_list=[], type ="", label="")) + + position: + child_code: + factor: + yield_: + measure: + measure_list: + type: + label: + + Append a child to the list of children + """ + _measure = Measure(decimals, self.recordType, + measure, [], label, factor, yield_) + if len(measure_list) > 0: + measure.buildMeasure(_measure, measure_list, type, decimals, + self.recordType) + _position = len(self.__children) + _child = Decomposition(_position, child_code, [_measure]) + self.__children.append(_child) + return _child + + def getText(self): + return self.__text + + def setText(self,text): + """setText(self,text) + + Sets the text of the record + It must be a string + """ + if not isinstance(text, str): + raise TypeError, utils.mapping(_("Text ($1) must be a string, "\ + "record: $2"), (str(text), self.__code)) + self.__text = text + + def getSheet(self): + return self.__sheet + + def setSheet(self, sheet): + """setSheet(self, sheet) + + Sets the sheet of condition object + """ + if not isinstance(sheet, Sheet): + raise ValueError, _("sheet must be a Sheet instance") + self.__sheet = sheet + + def getFiles(self): + return self.__files + + def setFiles(self, files): + """setFiles(self, files) + + Sets the files list + """ + # TODO: only sets files and File object format (durusdatabase) + if not isinstance(files, list): + raise ValueError, utils.mapping(_("files must be a list: $1"), + str(files)) + _files = [] + for file in files: + if isinstance(file, File): + _files.append(file) + elif isinstance(file, list): + _file_path = file[0] + _type = file[1] + _description = file[2] + if not os.path.exists(file[0]): + raise ValueError, _("Incorrect path") + _file = File(file_path, type, description) + _files.append(_file) + else: + raise ValueError, utils.mapping(_( + "file must be a list or a File object: $1"),str(file)) + self.__files = _files + + + def addFile(self, file_path, type, description): + """addFile(self, file_path, type, description) + + Add a file to a record instance + """ + if not os.path.exists(file_path): + raise ValueError, _("Incorrect path") + _name = os.path.basename(file_path) + _isin = False + for _ofile in self.__files: + if _ofile.name == _name: + _isin = True + if not _isin: + _file = File(_name, type, description) + self.__files.append(_file) + + def getLabels(self): + return self.__labels + + def setLabels(self, labels): + """setLabels(self, labels) + + Sets the labels list of a record + """ + if not isinstance(labels, list): + raise ValueError, _("labels must be a list") + _labels = [] + for _label in labels: + if isinstance(_label, str): + _labels.append(_label) + else: + raise ValueError, _("label must be a string") + self.__labels = _labels + + def addLabel(self, label): + """addLabel(self, label) + + Add a label to a record instance + """ + if not isinstance(label, str): + raise ValueError, _("Label must be a string") + if not label in self.__labels: + self.__labels.append(label) + + recordType = property(getRecordType, setRecordType, None, + """Record Type object + """) + code = property(getCode, setCode, None, + """Record code + """) + synonyms = property(getSynonyms, setSynonyms, None, + """List of codes synonyms of the code + """) + unit = property(getUnit,setUnit, None, + """Measure Unit of the record + """) + summary = property(getSummary, setSummary, None, + """Short description of the record + """) + prices = property(getPrices, None, None, + """List of Price/Date + """) + parents = property(getParents, setParents, None, + """List of codes of the records which the record is in + its decomposition + """) + children = property(getchildren, setchildren, None, + """List of Decompositon intances""") + text = property(getText, setText, None, + """Long description of the record""") + sheet = property(getSheet, setSheet, None, + """Sheet of conditions object""") + files = property(getFiles, setFiles, None, + """File list""") + labels = property(getLabels, setLabels, None, + """Label list""") + +class ParametricRecord(Record): + """base.ParametricRecord: + + Description: + Parametric Record object + Constructor: + base.ParametricRecord(code, synonyms, hierarchy, unit, summary, prices, + type, subtype, text="") + Ancestry: + +-- object + +-- Record + +-- ParametricRecord + Atributes: + + Methods: + + """ + + __slots__ = ["_ParametricRecord__budget", + "_ParametricRecord__code", "_ParametricRecord__synonyms", + "_ParametricRecord__hierarchy", "_ParametricRecord__unit", + "_ParametricRecord__summary", "_ParametricRecord__prices", + "_ParametricRecord__type", "_ParametricRecord__subtype", + "_ParametricRecord__parents", "_ParametricRecord__children", + "_ParametricRecord__text", "_ParametricRecord__sheet", + "_ParametricRecord__files", "_ParametricRecord__labels", + "_ParametricRecord__parameters", + "_ParametricRecord__select_comment", + "_ParametricRecord__vars", + "_ParametricRecord__parametric_summary", + "_ParametricRecord__parametric_text",] + + def __getstate__(self): + return (self.__budget, self.__code, self.__synonyms, self.__hierarchy, + self.__unit, self.__summary, self.__prices, self.__type, + self.__subtype, self.__parents, self.__children, self.__text, + self.__sheet, self.__files, self.__labels, self.__parameters, + self.__select_comment, self.__vars, + self.__parametric_summary, self.__parametric_text) + + def __setstate__(self, tuple): + self.__budget = tuple[0] + self.__code = tuple[1] + self.__synonyms = tuple[2] + self.__hierarchy = tuple[3] + self.__unit = tuple[4] + self.__summary = tuple[5] + self.__prices = tuple[6] + self.__type = tuple[7] + self.__subtype = tuple[8] + self.__parents = tuple[9] + self.__children = tuple[10] + self.__text = tuple[11] + self.__sheet = tuple[12] + self.__files = tuple[13] + self.__labels = tuple[14] + self.__parameters = tuple[15] + self.__select_comment = tuple[16] + self.__vars = tuple[17] + self.__parametric_summary = tuple[18] + self.__parametric_text = tuple[19] + + def __init__(self, budget, code, synonyms, hierarchy, unit, summary, + prices, type, subtype, parents=[], text=""): + Record.__init__(self, budget, code, synonyms, hierarchy, unit, summary, + prices, type, subtype, parents=[], text="") + self.__parameters = {} + self.__select_comment = "" + self.__vars = {} + self.parametric_summary = "" + self.parametric_text = "" + + def getParameter(self, parameter): + if parameter in self.__parameters: + return self.__parameters[parameter] + else: + return None + + def setParameter(self, parameter, parameter_list): + self.__parameters[parameter] = parameter_list + + def getSelectComment(self): + return self.__select_comment + + def setSelectComment(self, select_comment): + self.__select_comment = select_comment + def getVar(self, var): + if var in self.__vars: + return self.__vars[var] + else: + return None + + def setVar(self, var, var_list): + self.__vars[var] = var_list + + def getParametricSummary(self): + return self.__parametric_summary + + def setParametricSummary(self, parametric_summary): + self.__parametric_summary = parametric_summary + + def getParametricText(self): + return self.__parametric_text + + def setParametricText(self, parametric_text): + self.__parametric_text = parametric_text + + parameter = property(getParameter, setParameter, None, + """Record parameter + """) + select_comment = property(getSelectComment, setSelectComment, None, + """Seclect comment + """) + var = property(getVar, setVar, None, + """Record var + """) + parametric_summary = property(getParametricSummary, setParametricSummary, + None, + """Parametric summary + """) + parametric_text = property(getParametricText, setParametricText, None, + """Seclect comment + """) + +class Decomposition(object): + """base.Decomposition: + + Description: + Decomposition object + Constructor: + base.Decomposition(position, code, budgetMeasures, certification=None, + real_cost=None, cost_goals=None, cost_planned=None) + Ancestry: + +-- object + +-- Decomposition + Atributes: + "position": the position of the child record in the parent record + "code": Record code. + Measures: + "budgetMeasures": list of budget and Amended budget measures + "certification": list of certifications for months measures + "real_cost": list of real cost of construction for months measures + "cost_goals": list of cost goals of construction for months measures + "cost_planned": list of costs planned and amended cost planned measures + Methods: + __getstate__(self) + __setstate__(self, tuple) + __init__( position, code, budgetMeasures, certification=None, + real_cost=None, cost_goals=None, cost_planned=None) + {get/set}position + {get/set}Code + {get/set}BudgetMeasures + {get/set}Certification + {get/set}RealCost + {get/set}CostGoals + {get/set}CostPlanned + """ + __slots__ = ["_Decomposition__position", + "_Decomposition__code", + "_Decomposition__budgetMeasures", + "_Decomposition__certification", + "_Decomposition__real_cost", + "_Decomposition__cost_goals", + "_Decomposition__cost_planned", + ] + def __getstate__ (self): + return (self.__position, self.__code, self.__budgetMeasures, + self.__certification, self.__real_cost, self.__cost_goals, + self.__cost_planned) + def __setstate__(self,tuple): + self.__position = tuple[0] + self.__code = tuple[1] + self.__budgetMeasures = tuple[2] + self.__certification = tuple[3] + self.__real_cost = tuple[4] + self.__cost_goals = tuple[5] + self.__cost_planned = tuple[6] + + def __init__(self, position, code, budgetMeasures, certification=None, + real_cost=None, cost_goals=None, cost_planned=None): + self.position = position + self.code = code + self.budgetMeasures = budgetMeasures + self.certification = certification + self.real_cost = real_cost + self.cost_goals = cost_goals + self.cost_planned = cost_planned + def getPosition(self): + return self.__position + def setPosition(self, position): + if not isinstance(position, int): + raise ValueError, _("Position must be a integer") + self.__position = position + def getCode(self): + return self.__code + def setCode(self, code): + self.__code = code + def getBudgetMeasures(self): + return self.__budgetMeasures + def setBudgetMeasures(self, budgetMeasures): + if not isinstance(budgetMeasures, list): + raise ValueError, _("BudgetMeasures atribute must be a list") + for _measure in budgetMeasures: + if not isinstance(_measure, Measure): + raise ValueError, _("BudgetMeasures item must be a Measure "/ + "object") + self.__budgetMeasures = budgetMeasures + def getCertification(self): + return self.__certification + def setCertification(self, certification): + if not (certification is None or isinstance(certification, list)): + raise ValueError, _("Certification atribute must be a list or None") + self.__certification = certification + def getRealCost(self): + return self.__real_cost + def setRealCost(self, real_cost): + if not (real_cost is None or isinstance(real_cost, list)): + raise ValueError, _("Real cost atribute must be a list or None") + self.__real_cost = real_cost + def getCostGoals(self): + return self.__cost_goals + def setCostGoals(self, cost_goals): + if not (cost_goals is None or isinstance(cost_goals, list)): + raise ValueError, _("Cost goals atribute must be a list or None") + self.__cost_goals = cost_goals + def getCostPlanned(self): + return self.__cost_planned + def setCostPlanned(self, cost_planned): + if not (cost_planned is None or isinstance(cost_planned, list)): + raise ValueError, _("Cost Planned atribute must be a list or None") + self.__cost_planned = cost_planned + position = property(getPosition, setPosition, None, + """Postion of the record in the budget + """) + code = property(getCode, setCode, None, + """Record code + """) + budgetMeasures = property(getBudgetMeasures, setBudgetMeasures, None, + """list of budget and Amended budget measures + """) + certification = property(getCertification, setCertification,None, + """ list of certifications by months measures + """) + real_cost = property(getRealCost, setRealCost, None, + """ list of real cost of construction for months measures + """) + cost_goals = property(getCostGoals, setCostGoals, None, + """ list of cost goals of construction for months measures + """) + cost_planned = property(getCostPlanned, setCostPlanned, None, + """ list of costs planned and amended cost planned measures + """) + + +class Measure(object): + """base.Measure: + + Description: + Measure object + Constructor: + base.Measure(decimals, recordType, measure, lines, + label, factor, yield_) + Ancestry: + +-- object + +-- Measure + Atributes: + "measure": Total result of measure. + "lines": List of measure lines, List of LineM instances. + "label": Record Identifiers that are used by some measure programs. + "factor": + "yield": + "fixed": If fixed is True the yield is not calculated from measure + Methods: + __getstate__() + __setstate__(tuple) + __init__(decimals, recordType, measure, lines, + label, factor, yield_) + getMeasure() + setMeasure(measure, decimals) + {get/set}Lines + {get/set}Label + getFactor() + setFactor(factor, decimals, recordType) + getYield() + setYield(yield_, decimals, recordType) + getFixed() + setFixed(decimals) + buildMeasure(list_lines, type, decimals) + calculateMeasure(decimals) + updateYield(decimals) + """ + __slots__ = ["_Measure__measure", + "_Measure__lines", + "_Measure__label", + "_Measure__factor", + "_Measure__yield_", + "_Measure__fixed"] + def __getstate__ (self): + return (self.__measure, self.__lines, self.__label, + self.__factor, self.__yield_, self.__fixed) + def __setstate__(self,tuple): + self.__measure = tuple[0] + self.__lines = tuple[1] + self.__label = tuple[2] + self.__factor = tuple[3] + self.__yield_ = tuple[4] + self.__fixed = tuple[5] + def __init__(self, decimals, recordType, measure, lines, + label, factor, yield_): + self.setMeasure(measure, decimals) + self.lines = lines + self.label = label + self.setFactor(factor, decimals, recordType) + self.setYield(yield_, decimals, recordType) + self.__fixed = False + + def getMeasure(self): + return self.__measure + def setMeasure(self, measure, decimals): + if not isinstance(measure, float): + raise ValueError, utils.mapping(_("Measure must be a float "\ + "number. Type: $1"), (type(measure),)) + # TODO: test after + _DS = decimals.DS + measure = round(measure, _DS) + self.__measure = measure + + def getLines(self): + return self.__lines + def setLines(self, lines): + if not isinstance(lines, list): + raise ValueError, _("Lines must be a list") + for _line in lines: + if not isinstance(_line, MeasureLine): + raise ValueError, _("Line must be a MeasureLine objetc") + self.__lines = lines + def getLabel(self): + return self.__label + def setLabel(self, label): + self.__label = label + def setFactor(self, factor, decimals, recordType): + if not isinstance(factor, float): + raise ValueError, utils.mapping(_("Factor must be a float number "\ + "|$1|"), (factor,)) + # TODO: test after + _DF = decimals.getDF(recordType) + factor = round(factor, _DF) + self.__factor = factor + + def getFactor(self): + return self.__factor + + def setYield(self, yield_, decimals, recordType): + if not isinstance(yield_, float): + raise ValueError, _("Yield must be a float number") + # TODO: test after + _DR = decimals.getDR(recordType) + yield_ = round(yield_, _DR) + self.__yield_ = yield_ + + def getYield(self): + return self.__yield_ + + def setFixed(self, fixed, decimals): + if not isinstance(fixed, bool): + raise ValueError, _("Fixed must be boolean object") + self.__fixed = fixed + self.updateYield(decimals) + + def getFixed(self): + return self.__fixed + + measure = property(getMeasure, None, None, + """Total result of the measure + """) + lines = property(getLines, setLines, None, + """List of measure lines, List of "MeasureLine" instances + """) + label = property(getLabel, setLabel, None, + """Record identifiers that are used in some measure programs + """) + factor = property(getFactor, None, None, + """Factor + """) + yield_ = property(getYield, None, None, + """Yield of a record + """) + fixed = property(getFixed, setFixed,None, + """If fixed is True the yield is not calculated from measure + """) + + def buildMeasure(self, list_lines, type, decimals, recordType): + """setMeasure(self, list_lines, type, decimals) + + list_lines: list of measure lines + [ [linetype, comment, units, length, width, height, formula], ... ] + linetype: + #-#empty string -> Normal + 0 -> Normal + 1 -> Parcial Subtotal + 2 -> Accumulated Subtotal + 3 -> Formula + comment: comment string + units: Number of Units (a) + length: Length (b) + width: Width (c) + height: Height (d) + formula: Can be a formula or a empty string + Valid Operator: '(', ')', '+', '-', '*', '/' and '^' + Valid variable: 'a', 'b', 'c','d' and 'p' (Pi=3.1415926) + type: type of action + M: Set measure + A: Add measure + decimal: budget decimals object + + Sets the measurelines for a record + """ + # TODO: calcutate measure from lines + _parcial = 0 + _total = 0 + _lines = [] + for _line in list_lines: + _type, _comment = _line[0], _line[1] + _units, _length = _line[2], _line[3] + _width, _height = _line[4], _line[5] + _formula = _line[6] + _measure_line = MeasureLine(decimals, _type, _comment, _units, + _length, _width, _height, _formula) + _lines.append(_measure_line) + if type == "M": + self.lines = _lines + elif type == "A": + self.lines.extend(_lines) + else: + raise ValueError, utils.mapping(_("Type must be M or A. Type: $1"), + (type,)) + self.calculateMeasure(decimals, recordType) + + def calculateMeasure(self, decimals, recordType): + #TODO: round acumulated_subtotal and parcial_subtotal + if len(self.lines) > 0: + _acumulated_total = 0.0 + _parcial_total = 0.0 + for line in self.lines: + _parcial = line.parcial + _acumulated_total += _parcial + if line.lineType == 2: + line.setAcumulatedSubtotal(_acumulated_total, decimals) + elif line.lineType == 1: + _parcialSubtotal = _acumulated_total - _parcial_total + line.setParcialSubtotal(_parcialSubtotal, decimals) + _parcial_total = _acumulated_total + self.setMeasure(_acumulated_total, decimals) + _DR = decimals.getDR(recordType) + self.updateYield(decimals, recordType) + def updateYield(self, decimals, recordType): + if not self.fixed: + self.setYield(self.measure, decimals, recordType) + +class MeasureLine(object): + """base.MeasureLine: + + Description: + MeasureLine object + Constructor: + base.MeasureLine(budget, type, comment, units, length, width, height, + formula) + Ancestry: + +-- object + +-- MeasureLine + Atributes: + "lineType": Line type: + #-#empty string -> Normal + 0 -> Normal + 1 -> Parcial Subtotal + 2 -> Accumulated Subtotal + 3 -> Formula, the comment is a formula. + "comment": Descriptive text string + "units": Number of Units (a) + "length": length (b) + "width": Width (c) + "height": Height (d) + "formula": can be a valid formula or a empty string + Valid Operator: '(', ')', '+', '-', '*', '/' and '^' + Valid variable: 'a', 'b', 'c','d'y 'p' (Pi=3.1415926) + "partial" : result of measure line + "parcial_subtotal" + "acumulated_subtotal" + Methods: + __getstate__(self) + __setstate__(self, tuple) + __init__(self, decimals, type, comment, units, length, width, height, + formula) + {get/set}LineType + {get/set}Comment + {get/set}Units + {get/set}Length + {get/set}Width + {get/set}Height + {get/set}Formula + getParcial + {get/set}ParcialSubtotal + {get/set}AcumulatedSubtotal + calculateParcial + eval_formula + """ + __slots__ = ["_MeasureLine__lineType", + "_MeasureLine__comment", + "_MeasureLine__units", + "_MeasureLine__length", + "_MeasureLine__width", + "_MeasureLine__height", + "_MeasureLine__formula", + "_MeasureLine__parcial", + "_MeasureLine__parcial_subtotal", + "_MeasureLine__acumulated_subtotal", + ] + def __getstate__ (self): + return (self.__lineType, self.__comment, self.__units, + self.__length, self.__width, self.__height, self.__formula, + self.__parcial) + def __setstate__(self,tuple): + self.__lineType = tuple[0] + self.__comment = tuple[1] + self.__units = tuple[2] + self.__length = tuple[3] + self.__width = tuple[4] + self.__height = tuple[5] + self.__formula = tuple[6] + self.__parcial = tuple[7] + #self.calculateParcial() + def __init__(self, decimals, type, comment, units, length, width, height, + formula): + self.__parcial = 0.0 + self.__parcial_subtotal = 0.0 + self.__acumulated_subtotal = 0.0 + self.lineType = type + self.comment = comment + self.setUnits(units, decimals) + self.setLength(length, decimals) + self.setWidth(width, decimals) + self.setHeight(height, decimals) + self.setFormula(formula, decimals) + #self.calculateParcial() + def getLineType(self): + return self.__lineType + def getComment(self): + return self.__comment + def getUnits(self): + return self.__units + def getLength(self): + return self.__length + def getWidth(self): + return self.__width + def getHeight(self): + return self.__height + def getFormula(self): + return self.__formula + def getParcial(self): + return self.__parcial + def getParcialSubtotal(self): + return self.__parcial_subtotal + def getAcumulatedSubtotal(self): + return self.__acumulated_subtotal + def setParcialSubtotal(self, parcial_subtotal, decimals): + if not isinstance(parcial_subtotal, float): + raise ValueError, utils.mapping(_(" Parcial Subtotal must be a "\ + "float number. Parcial: $1"), (str(parcial_subtotal),)) + _DS = decimals.DS + parcial_subtotal = round(parcial_subtotal, _DS) + self.__parcial_subtotal = parcial_subtotal + def setAcumulatedSubtotal(self, acumulated_subtotal, decimals): + if not isinstance(acumulated_subtotal, float): + raise ValueError, utils.mapping(_(" Acumulated Subtotal must be "\ + "a float number. Parcial: $1"), + (str(acumulated_subtotal),)) + _DS = decimals.DS + acumulated_subtotal = round(acumulated_subtotal, _DS) + self.__acumulated_subtotal = acumulated_subtotal + def calculateParcial(self, decimals): + _DS = decimals.DS + if self.lineType == 1 or self.lineType == 2: + _parcial = 0.0 + elif self.lineType == 0: # self.formula == "": + if isinstance(self.units, float): + _a = self.units + else: + _a = 0.0 + if isinstance(self.length, float): + _b = self.length + else: + _b = 1.0 + if isinstance(self.width, float): + _c = self.width + else: + _c = 1.0 + if isinstance(self.height, float): + _d = self.height + else: + _d = 1.0 + _parcial = _a * _b * _c * _d + else: + _parcial = self.eval_formula() + _parcial = round(_parcial, _DS) + self.__parcial = _parcial + + def setLineType(self, type): + if not type in [0, 1, 2, 3]: + raise ValueError, utils.mapping(_("Invalid measure line type ($1)"), + (str(type),)) + self.__lineType = type + def setComment(self, comment): + if not isinstance(comment, str): + raise ValueError, utils.mapping(_("Measure Comment must be a "\ + "string ($1)"), (str(comment),)) + self.__comment = comment + def setUnits(self, units, decimals): + if units != "": + if not isinstance(units, float): + raise ValueError, utils.mapping(_("Invalid Measure Units ($1)"), + (str(units),)) + _DN = decimals.DN + units = round(units, _DN) + self.__units = units + try: + self.calculateParcial(decimals) + except AttributeError: + pass + def setLength(self, length, decimals): + if length != "": + if not isinstance(length, float): + raise ValueError, utils.mapping(_("Invalid Measure length ($1)"), + (str(units),)) + _DD = decimals.DD + length = round(length, _DD) + self.__length = length + try: + self.calculateParcial(decimals) + except AttributeError: + pass + def setWidth(self, width, decimals): + if width != "": + if not isinstance(width, float): + raise ValueError, utils.mapping(_("Invalid Measure Width ($1)"), + (str(units),)) + _DD = decimals.DD + width = round(width, _DD) + self.__width = width + try: + self.calculateParcial(decimals) + except AttributeError: + pass + def setHeight(self, height, decimals): + if height != "": + if not isinstance(height, float): + raise ValueError, utils.mapping(_("Invalid Measure Height ($1)"), + (str(height),)) + _DD = decimals.DD + height = round(height, _DD) + self.__height = height + try: + self.calculateParcial(decimals) + except AttributeError: + pass + def setFormula(self, formula, decimals): + if not isinstance(formula, str): + raise ValueError, utils.mapping(_("Formula must be a "\ + "string ($1)"), (str(formula),)) + if re.match(".*[^0123456789\.()\+\-\*/\^abcdp ].*", formula): + raise ValueError, utils.mapping(_("There is invalid characters"\ + "in formula ($1)"), (str(formula),)) + self.__formula = formula + try: + self.calculateParcial(decimals) + except AttributeError: + pass + + lineType = property(getLineType, setLineType, None, + """Type of measure line + """) + comment = property(getComment, setComment, None, + """Text + """) + units = property(getUnits, None, None, + """Number of units + """) + length = property(getLength, None, None, + """Length measure + """) + width = property(getWidth, None, None, + """Width measure + """) + height = property(getHeight, None, None, + """Height measure + """) + formula = property(getFormula, None, None, + """Formula + """) + parcial = property(getParcial, None, None, + """result of measure line + """) + acumulated_subtotal = property(getAcumulatedSubtotal, + None, None, + """Acumulated subtotal + """) + parcial_subtotal = property(getParcialSubtotal, + None, None, + """Parcial subtotal + """) + def eval_formula(self): + """eval_formula() + + formula: + Valid Operator: '(', ')', '+', '-', '*', '/' and '^' + Valid variable: 'a', 'b', 'c','d'y 'p' (Pi=3.1415926) + units: Number of Units (a) + length: Length (b) + width: Width (c) + height: Height (d) + + Evals the formula and return the result + """ + formula = self.formula + a = self.units + b = self.length + c = self.width + d = self.height + if a == "": a = 0.0 + if b == "": b = 0.0 + if c == "": c = 0.0 + if d == "": d = 0.0 + try: + a = float(a) + except: + raise ValueError, _("'a' value must be a float number") + try: + b = float(b) + except: + raise ValueError, _("'b' value must be a float number") + try: + c = float(c) + except: + raise ValueError, _("'c' value must be a float number") + try: + d = float(d) + except: + raise ValueError, _("'d' value must be a float number") + # spaces are erased + formula.replace(" ","") + # operators and varibles are replaced + formula = formula.replace("+", " + ") + formula = formula.replace("-", " - ") + formula = formula.replace("*", " * ") + formula = formula.replace("/", " / ") + formula = formula.replace("^", " ** ") + formula = formula.replace("(", " ( ") + formula = formula.replace(")", " ) ") + formula = formula.replace("a", str(a)) + formula = formula.replace("b", str(b)) + formula = formula.replace("c", str(c)) + formula = formula.replace("d", str(d)) + formula = formula.replace("p", "3.1415926") + _list_formula = formula.split(" ") + _formula2 = "" + for oper in _list_formula: + try: + _float_oper= str(float(oper)) + _formula2 = _formula2 + _float_oper + except ValueError: + _formula2 = _formula2 + oper + _g ={"__builtins__":{}} + try: + return eval(_formula2, _g) + except: + raise ValueError, _("Invalid formula") + +class Decimals(object): + """base.Decimals: + + Description: + Decimals object + Constructor: + base.Decimals(DN=2, DD=2, DSP=2, DS=2, + DFC=3, DFPU=3, DFUO=3, DFA=3, + DRP=3, DRC=3, DRUO=3, DRA=3, + DP=2, DC=2, DPU=2, DUO=2, DEA=2, DES=2, + DIR=2, DIM=2, DIRC=2, DIMC=2, DCD=2, + DIVISA="EUR") + Ancestry: + +-- object + +-- Decimals + Atributes: + "DN": Number of decimal places of the field "equal-size parts" in the + measure lines. + Default: 2 decimal places. + "DD": Number of decimal places of the three dimensions in the + measure lines. + Default: 2 decimal places. + "DSP": Number of decimal places of the subtotal of a measure. + Default: 2 decimal places. + "DS": Number of decimal places of the total sum of a measure. + Default: 2 decimal places. + "DFC": Number of decimal places of the yield factor in a decomposition + of a chapter or subchapter. + Dafault: 3 decimal places. + "DFPU": Number of decimal places of the yield factor in a decomposition + of a unitary budget. + Default: 3 decimal places. + "DFUO": Number of decimal places of the yield factor in a decomposition + of a unit of work. + Default: 3 decimal places. + "DFA": Number of decimal places of the yield factor in a decomposition + of a Auxiliary element. + Default: 3 decimal places. + "DRC": Number of decimal places of the yield in a + decomposition of a chapter or subchapter. + Number of decimal places of the result of the multiplictaion of + the yield (or measure) and the factor in a decomposition of a + chapter or subcharter. + Default: 3 decimal places. + "DRPU": Number of decimal places of the yield in a decomposition + of a unitary budget record. + Number of decumal places of the result of the multiplication of + the factor and the yield in a decompositon of a untitary budget. + Default: 3 decimal places. + "DRUO": Number of decimal places of the yield in a decomposition of a + unit of work. + Decimal places of the result of the multiplication of the yield + and the factor in a descomposition of a unit of work. + Default: 3 decimal places. + "DRA": Number of decimal places of the yield in a decompositon of a + auxiliar element. + Number of decimal places of the result of the multiplication of + the yield and the factor in a descomposition of a auxilar element.Decimales + Default: 3 decimal places. + "DP": Number of decimal places of the price of a budget. + Default: 2 decimal places. + "DC": Number of decimal places of the price of a chapter or subchapter. + Default: 2 decimal places. + "DPU": Number of decimal places of the price of a unitary budget. + Default: 2 decimal places. + "DUO": Number of decimal places of the price of a unit of work. + Default: 2 decimal places. + "DEA": Number of decimal places of the price of a auxiliar element. + Default: 2 decimal places. + "DES": Number of decimal places of the price of the simple elements. + Default: 2 decimal places. + "DIR": Number of decimal places of the resulting amount to multiply + the total yield and the price of the elements of a unit of work or + a auxiliar element. + "DIRC": Number of decimal places of the resulting amount to multiply + the total yield and the price of the elements of a budget, chapter + or a subchapter. + "DCD": Number of decimal places ot the resulting amount to sum the + direct costs of a unit of work (and auxiliar element). + Number of decimal places of the indirect costs. + Default: 2 decimal places. + "DIVISA": monetary unit. + Methods: + __init__(DN=2, DD=2, DSP=2, DS=2, + DFC=3, DFPU=3, DFUO=3, DFA=3, + DRC=3, DRPU=3, DRUO=3, DRA=3, + DP=2, DC=2, DPU=2, DUO=2, DEA=2, DES=2, + DIR=2, DIRC=2, DCD=2, + DIVISA="EUR") + __getitem__(key) + haskey(key) + getD(recordtype) + getDF(recordType) + getDR(recordType) + getDI(recordType) + """ + # TODO: get/set methods + def __init__(self, + DN=2, DD=2, DSP=2, DS=2, + DFC=3, DFPU=3, DFUO=3, DFA=3, + DRC=3, DRPU=3, DRUO=3, DRA=3, + DP=2, DC=2, DPU=2, DUO=2, DEA=2, DES=2, + DIR=2, DIRC=2, DCD=2, + DIVISA="EUR" ): + self.DN = DN + self.DD = DD + self.DSP = DSP + self.DS = DS + self.DFP = 3 + self.DFC = DFC + self.DFPU = DFPU + self.DFUO = DFUO + self.DFA = DFA + self.DRP = 3 + self.DRC = DRC + self.DRPU = DRPU + self.DRUO = DRUO + self.DRA = DRA + self.DP = DP + self.DC = DC + self.DPU = DPU + self.DUO = DUO + self.DEA = DEA + self.DES = DES + self.DIR = DIR + self.DIRC = DIRC + self.DCD = DCD + self.DIVISA = DIVISA + def __getitem__(self, key): + return self.__dict__[key] + def haskey(self, key): + return key in self.__dict__ + def getD(self, recordType): + # DP: budget. + # DC: chapter and subcharter. + # DUO: unit. + # DEA: auxiliar element. + # DES: simple element. + _hierarchy = recordType.hierarchy + if _hierarchy == 0: #budget, type 0, subtipe "OB" + _decimal = self.DP + elif _hierarchy == 1: #chapter/subcharter, type 0, subtipe "" + _decimal = self.DC + else: # other + _type = recordType.type + _subtype = recordType.subtype + if _subtype == "EA": # auxiliar element type 0 subitype "EA" + _decimal = self.DEA + if _subtype == "PU": # unitary budget type 0 subitype "PU" + _decimal = self.DPU + elif (_type in [1, 2, 3] or + _subtype in ["H", "Q", "%", "MC", "MCr", "MM", "MS", "ME", + "MCu", "Mal","ML", "M"] + ): # simple element + _decimal = self.DES + else: # unit type 0, subtipe ["EU", "EC", "EF", "PA"] + _decimal = self.DUO + return _decimal + def getDF(self, recordType): + # Factor: DF + # ->DFP: Budget + # ->DFC: Chapter/Subchapter + # ->DFUO: Unit + # ->DFA: Auxiliar + # ->DFPU: Unitary budget + if recordType.hierarchy == 0: #budget + _decimal = self.DFP + elif recordType.hierarchy == 1: #chapter/subcharter + _decimal = self.DFC + else: # other + if recordType.subtype == "EA": # auxiliar element + _decimal = self.DFA + if recordType.subtype == "PU": # unitary budget element + _decimal = self.DFPU + else: # unit EU EC EF PA + _decimal = self.DFUO + return _decimal + def getDR(self, recordType): + # Yield: DR + # ->DRP: Budget + # ->DRC: Chapter/Subchapter + # ->DRUO: Unit + # ->DRA: Auxiliar + # ->DRPU: Unitary budget + if recordType.hierarchy == 0: #budget + _decimal = self.DRP + elif recordType.hierarchy == 1: #chapter/subcharter + _decimal = self.DRC + else: # other + if recordType.subtype == "EA": # auxiliar element + _decimal = self.DRA + if recordType.subtype == "PU": # unitary budget element + _decimal = self.DRPU + else: # unit + _decimal = self.DRUO + return _decimal + def getDI(self, recordType): + # DIRC: budget, chapter and subcharter. + # DIR: unit, auxiliar element. + _hierarchy = recordType.hierarchy + _subtype = recordType.subtype + if _hierarchy == 0 or _hierarchy == 1 or _subtype == "PU": + #budget, type 0, subtipe "OB" + #chapter/subcharter, type 0, subtipe "" + #unitary budget, type 2, subtype "PU" + _decimal = self.DIRC + else: # other + # auxiliar element type 0 subitype "EA" + # unit type 0, subtipe ["EU", "EC", "EF", "PA", "PU"] + _decimal = self.DIR + return _decimal + +class Sheet(object): + """base.Sheet: + Description: + Sheet of conditions object + Constructor: + base.Sheet(sheet_dict) + Ancestry: + +-- object + +-- Sheet + Atributes: + "sheet_dict": { : {
: } + : must be in Budget.SheetFields +
: must be in Budget.SheetSections + : must be in Budget.SheetParagraph + Methods: + __getstate__(self) + __setstate__(self, tuple) + __init__(self, sheet_dict={}) + {get/set}Sheet_dict + getFields + getSections + getParagraph + addField + addSection + """ + __slots__ = ["_Sheet__sheet_dict"] + def __getstate__ (self): + return (self.__sheet_dict,) + def __setstate__(self,tuple): + self.__sheet_dict = tuple[0] + def __init__(self): + self.__sheet_dict = {} + def getSheet_dict(self): + return self.__sheet_dict + def setSheet_dict(self, sheet_dict): + if not isinstance(sheet_dict, dict): + raise ValueError, _("sheet_dict must be a dictionay") + self.__sheet_dict = sheet_dict + def getFields(self): + return self.sheet_dict.keys() + def getSections(self, field): + if field in self.__sheet_dict: + return self.__sheet_dict[field].keys() + else: + return None + def getParagraph(self, field, section): + if (field in self.__sheet_dict and + section in self.__sheet_dict[field]): + return self.__sheet_dict[field][section] + else: + return None + def addField(self, field, section_dict): + if not isinstance(field, str): + raise ValueError, _("sheet field must be a string") + if not isinstance(section_dict, dict): + raise ValueError, _("section_dict must be a dictionary") + self.__sheet_dict[field] = section_dict + def addSection(self, field, section, paragraph): + if not isinstance(field, str): + raise ValueError, _("sheet field must be a string") + if not isinstance(section, str): + raise ValueError, _("sheet section must be a string") + if not isinstance(paragraph, str): + raise ValueError, _("sheet paragraph must be a string") + if not field in self.__sheet_dict: + self.addField(field, { }) + _field = self.__sheet_dict[field] + _field[section] = paragraph + sheet_dict = property(getSheet_dict, setSheet_dict, None, + """Sheet dictionary { : {
: }""") + +class Budget(object): + """base.Budget: + + Description: + Budget objetc + Constructor: + base.Budget() + Ancestry: + +-- object + +-- Budget + Atributes: + "filename": file name of the budget file (FIEBDC) + "__records": Dictionary with the budget records. + { "code" : Record object, } + "__synonyms": Dictionary with the records synonums. + { "code" : ["synonym",],} + Each record code can have synonym codes. + "__root": The root record code. + "__title_list": List with the Headers and list of Titles for prices and + decimal places. + [ "Header", ["Title1", "Title2", ... ] ] + The records can have diferent prices for diferent ages, geografical + places, ... + The Headers is the type of hierarchy of the prices + Each Title have a group of Prices and a Decimals definition + "__decimals": List with the decimal places used to round the + result of the calculations with prices and measures + The values are Decimals objects + The <0> objets is the default Decimals (seted in FIEBDC-3), + The others keys are for the diferent groups of Prices + "__percentages": Dictionary with the percentages + keys: + "CI" Indirect Cost + "GG" General expenses + "BI" Industrial benefit + "BAJA" Low (what this do here?) + "IVA" Tax + "__file_owner" + "__comment" + "__date" + "__budgetType" A integer. Type of data in budget + 0 -> Undefined + 1 -> Base data. + 2 -> Budget. + 3 -> Certificate. + 4 -> Base date update. + "__budgetCerficateOrder" Only valid if budgetType is 3. + "__budgetCerficateDate" Only valid if budgetType is 3 + "__title_index": A integer. The active group of Prices and Decimals. + "__sheet_sections": Dictionary whith de sheet sections + "__sheet_fields": Dictionary whith sheet fields + "__sheet_paragraphs": Dictionary whith sheet paragraphs + "__companys": Dictionary whith companys object + { company_code: company_object } + "__tec_info": Dictionary whith tecnical information + {ti_code : ["desciption text", "unit"]} + "__labels": Label dictionary { "label": [ "code", ], } + Methods: + iter + iterPreOrder + iterPostOrder + getRoot(self) + hasPath(self, path) + getchildren(self, code) + setOwner(self, owner) + setDate(self, date) + setComment(self, comment) + setBudgetType(self, type) + setCertificateOrder(self, order) + setCertificateDate(self, date) + setTitleList(self, title) + getTitleList(self) + getActiveTitle(self) + setDecimals(self, dictionary) + getDecimals(self, decimal="All", N=None) + setPercentages(self, dictionary) + getPercentages(self, percentage="All") + getAllParents(self, code) + getAllchildren(self, code) + getNDecomposition(self, code, N) + getDecomposition(self,code) + getMeasure(self, path) + getStrYield + getStrFactor + setTree(sef, code, child_code, position, factor, yield_, total, + list_lines, label, type) + eval_formula(self, formula, a, b, c, d) + getText(self, code) + setText(self, code, text) + setRecord(self, code, synonyms, hierarchy, unit, sumary, ... + hasRecord(self, code) + getRecord + addPriceToRecord + getStrPriceFromRecord + getCode(self, path) + getAmount + getStrAmount + setSheetSection(self, sheet_code, sheet_title) + hasSheetSection(self, section) + setSheetSections(self,dictionary) + setSheetField(self, field_code, field_title) + hasSheetField(self, field) + getSheetField(self, field) + setSheetFields(self, field_dict) + setSheetParagraph(self, paragraph_code, paragraph_text) + hasSheetParagraph(self, paragraph) + getSheetParagraph(self, paragraph) + setSheetParagraphs(self, paragraph_dict) + setSheetRecord(self, record_code,field, section_dict) + addFile(self, record_code, filename) + setCompany(self, code, summary, name, offices, cif, web, email ) + getCompany + getCompanyKeys + addTecInfo(self, ti_code, text, unit) + hasTecInfo(self, ti_code) + getTecInfo(self, ti_code) + setTecnicalInformation(self, _record_code, _ti_dict) + changeCode(self, record_code, new_rocord_code) + addLabel + setParametricSelectComment + setParametricSummary + setParametricText + """ + + + def __init__(self): + """__init__(self) + + Initialize the budget atributes + """ + self.__title_index = 0 + self.__decimals = [Decimals(), Decimals()] + self.__percentages = { "CI" : "" ,"GG": "", "BI": "", + "BAJA": "", "IVA" : ""} + self.__title_list = [ "", [ ] ] + self.__root = None + self.__file_owner = "" + self.__comment = "" + self.__budgetCerficateOrder = None + self.__budgetCerficateDate = None + self.__date = (0,0,0) + self.__budgetType = 0 + self.__records = { } + self.__synonyms = { } + self.__sheet_sections = { } + self.__sheet_fields = { } + self.__sheet_paragraphs = { } + self.__companys = { } + self.__tec_info = { } + self.__labels = { } + + def __getstate__(self): + return (self.__title_index, self.__decimals, self.__percentages, + self.__title_list, self.__root, self.__file_owner, + self.__records, self.__synonyms, self.__sheet_sections, + self.__sheet_fields, self.__sheet_paragraphs,self.__companys, + self.__tec_info, self.__labels) + + def __setstate__(self, tuple): + self.__title_index = tuple[0] + self.__decimals = tuple[1] + self.__percentages = tuple[3] + self.__title_list = tuple[4] + self.__root = tuple[4] + self.__file_owner = tuple[5] + self.__records = tuple[6] + self.__synonyms = tuple[7] + self.__sheet_sections = tuple[8] + self.__sheet_fields = tuple[9] + self.__sheet_paragraphs = tuple[10] + self.__companys = tuple[11] + self.__tec_info = tuple[12] + self.__labels = tuple[13] + + def iter(self): + for record in self.__records: + yield record + def iterPreOrder(self, recordCode, codes=[]): + _children = self.getchildren(recordCode) + for _child in _children: + if not _child in codes: + codes.append(_child) + self.iterPreOrder(_child, codes) + return codes + def iterPostOrder(self, recordCode, codes=[]): + _children = self.getchildren(recordCode) + for _child in _children: + if not _child in codes: + self.iterPreOrder(_child, codes) + codes.append(_child) + return codes + + def getRoot(self): + """getRoot(self) + + Returns the root record code + """ + return self.__root + + def hasPath(self, path): + """hasPath(self, path) + + path: The path of the record in the budget, It is a tuple. + Tests if the path is valid in the budget + """ + try: + self.getCode(path) + return True + except ValueError: + return False + + def getchildren(self, code): + """getchildren(self, code) + + code: a record code. + Return a list whith the child codes of a record + """ + _record = self.__records[code] + _children = _record.children + _child_code = [ _child.code for _child in _children ] + return _child_code + def setOwner(self, owner): + """setOwner(self, owner) + + owner: data owner + Set the data owner. + """ + if isinstance(owner, basestring): + self.__file_owner = owner + else: + raise TypeError, _("Owner must be a string") + + def setDate(self, date): + """setOwner(self, date) + + date (_y, _m, _d) + Set the date when de file was generated + """ + if isinstance(date, tuple) and len(date) == 3 and \ + isinstance(date[0], int) and isinstance(date[1], int) and \ + isinstance(date[2], int) and date[1] in range(13) and \ + date[2] in range(32): + if date[1] != 0 and date[2] != 0: + datetime.date(*date) + self.__date = date + else: + raise TypeError, _("Invalid Date: %s" % str(date)) + + def setComment(self, comment): + """setOwner(self, comment) + + comment: text to comment the budged + Set the comment. + """ + if isinstance(comment, basestring): + self.__comment = comment + else: + raise TypeError, _("Comment must be a string") + + def setBudgeType(self, budget_type): + """setOwner(self, budget_type) + + budget_type: type of data in budget + 0 -> Undefined + 1 -> Base data. + 2 -> Budget. + 3 -> Budget certificate. + 4 -> Base date update. + Set the budget type. + """ + if budget_type in [1, 2, 3, 4]: + self.__budgetType = budget_type + else: + raise ValueError, _("Budget type must be 1, 2, 3 or 4.") + + def setCertificateOrder(self, certificate_order, certificate_date): + """setOwner(self, budget_type) + + certificate_order: certificate number + certificate_date: certificate date + Set the certificate order and date. + """ + if isinstance(certificate_order, int): + self.__budgetCerficateOrder = certificate_order + else: + raise ValueError, _("Certificate order must be a integer.") + + def setCertificateDater(self, certificate_date): + """setCertidicateDate(self, certificate_date) + + Set the certificate date. + """ + if isinstance(certificate_date, tuple) and \ + len(certificate_date) == 3 and \ + isinstance(certificate_date[0], int) and \ + isinstance(certificate_date[1], int) and \ + isinstance(certificate_date[2], int): + datetime.date(*certificate_date) + self.__budgetCerficateDate = certificate_date + else: + raise ValueError, _("Budget certificate Date must be a valid Date.") + + def setTitleList(self, title_list): + """setTitleList(self, title_list) + + title_list: [ "Header", ["Title1", "Title2", ... ] ] + Set the header and titles for the price groups and decimals. + """ + title_list[0] = str(title_list[0]) + if isinstance(title_list, list) and isinstance(title_list[1], list): + for i in range(len(title_list[1])): + title_list[1][i] = str(title_list[1][i]) + self.__title_list = title_list + else: + raise TypeError, _("Invalid title list format") + + def getTitleList(self): + """ getTitleList(self) + + Returns the header and titles for the price groups and decimals. + """ + return self.__title_list + + def getActiveTitle(self): + """getActiveTitle(self) + + Returns the active Title of price group + """ + return self.__title_index + + def setDecimals(self, dictionary, N): + """setDecimals(self, dictionary, N) + + dictionay: the decimal dictionary + N: the price group + Sets the Decimals for a price group. + """ + if N == -1 or N == len(self.__decimals): + _default_decimals = self.__decimals[0] + self.__decimals.append(_default_decimals) + elif N < len(self.__decimals): + _default_decimals = self.__decimals[N] + else: + raise IndexError, _("Invalid Index Title") + for _decimal in dictionary: + if dictionary[_decimal] == "": + dictionary[_decimal] = eval("_default_decimals." + _decimal) + decimals = Decimals(dictionary["DN"], dictionary["DD"], + dictionary["DSP"], dictionary["DS"], + dictionary["DFC"], + dictionary["DFPU"], dictionary["DFUO"], + dictionary["DFA"], dictionary["DRC"], + dictionary["DRPU"], dictionary["DRUO"], + dictionary["DRA"], dictionary["DP"], + dictionary["DC"], dictionary["DPU"], + dictionary["DUO"], dictionary["DEA"], + dictionary["DES"], dictionary["DIR"], + dictionary["DIRC"], dictionary["DCD"], + dictionary["DIVISA"]) + self.__decimals[N] = decimals + def getDecimals(self, decimal="All", N=None): + """getDecimals(self,decimal="All",N=None) + + decimal: + "All": Return a Decimals objet for a price group + "keys": Return the keys of a Decimal object + key: Return a Decimal value for a price group + N: the price group None,1,2,.. + None: Return the active price group + """ + if N is None: N = self.getActiveTitle() + if decimal == "All": + return self.__decimals[N+1] + elif decimal == "keys": + return self.__decimals[N+1].keys + elif self.__decimals[N+1].haskey(decimal): + return self.__decimals[N+1][decimal] + else: + raise KeyError, _("Decimal Key error") + + def setPercentages(self, dictionary): + """setPercentages(self, dictionary): + + dictionary: the percentage dictionary + Sets the percentage dictionary. + """ + _default_percentages = self.__percentages + for percentage in dictionary: + if dictionary[percentage] == 0: + dictionary[percentage] = "" + elif dictionary[percentage] == "": + dictionary[percentage] = _default_percentages[percentage] + _percentages = { "CI": dictionary["CI"], + "GG": dictionary["GG"], + "BI": dictionary["BI"], + "BAJA": dictionary["BAJA"], + "IVA" : dictionary["IVA"]} + self.__percentages = _percentages + + def getPercentages(self, key="All"): + """getPercentages(self, percentage="All") + + key: + "All": Return the Percentages dictionary + "keys": Return the keys of a Percentages object + key: Return a Percentages value for the key + """ + if key == "All": + return self.__percentages + elif key == "keys": + return self.__percentages.keys + elif key in self.__percentages: + return self.__percentages[key] + else: + raise KeyError, _("Invalid Percentage key") + + def getAllParents(self,code): + """getAllParents(self,code) + + code: a record code. + Returns a list with all the parents of a record + All record which the record is in its descomposition list, + including the parents of the parents + """ + if code in self.__records: + _parents = self.__records[code].parents + if len(_parents) == 0: return [ ] + for _antecesor in _parents[:]: + _parents = _parents + self.getAllParents(_antecesor) + return _parents + else: + return [ ] + + def getAllchildren(self,code): + """getAllchildren(self,code + + code: a record code. + Returns a list with all the children of a record, including + the children of the children + """ + if code in self.__records: + _children = self.__records[code].children + _children = [ _child.code for _child in _children ] + for _child in _children[:]: + _children = _children + self.getAllchildren(_child) + return _children + else: + return [ ] + + def getNDecomposition(self, code, N): + """getDecomposition(self,path) + + path: the path for a record + Returns the Decomposition object of a record + """ + _record = self.getRecord(code) + _decomposition_list = _record.children + _decomposition = _decomposition_list[N] + return _decomposition + + def getDecomposition(self, path): + """getDecomposition(self,path) + + path: the path for a record + Returns the Decomposition object of a record + """ + if path == (0,): + _type = self.getRecord(self.__root).recordType + return Decomposition( 0, self.__root, + [Measure(self.getDecimals(), _type, + 0.0, [], "", 1.0, 1.0)]) + else: + return self.getNDecomposition(self.getCode(path[:-1]), path[-1]) + + def getMeasure(self, path): + """getMeasure(self, path) + + path: the path for a record + Return the measute object of a record + """ + _decomposition = self.getDecomposition(path) + _measure = _decomposition.budgetMeasures[0] + return _measure + + def getStrYield(self, measure, recordType): + #_DR = measure.getDR(self.getDecimals()) + _DR = self.getDecimals().getDR(recordType) + _yield = ("%." + str(_DR) + "f" ) % measure.yield_ + return _yield + + def getStrFactor(self, measure, recorType): + _DF = self.getDecimals().getDF(recordType) + #_DF = measure.getDF(self.getDecimals()) + _factor = ("%." + str(_DF) + "f" ) % measure.factor + return _factor + + def setTree(self, code, child_code, position, factor, yield_, total, + list_lines, label, type): + """setTree(self, code, child_code, position, factor,yield_, total, + list_lines, label, type) + + code: the parent record code + child_code: child record code + position: position of child record in record parent record + decomposition. Position == -1 -> new child + factor: + yield_: + total: total measure (float) + list_lines: list of measure lines + [ [linetype, comment, units, length, width, height], ... ] + linetype: + empty string -> Normal + 1 -> Parcial Subtotal + 2 -> Accumulated Subtotal + 3 -> Formula, the comment is a formula. + comment: Can be a descriptive text or a formula + Valid Operator: '(', ')', '+', '-', '*', '/' and '^' + Valid variable: 'a', 'b', 'c','d'y 'p' (Pi=3.1415926) + units: Number of Units (a) + length: Length (b) + width: Width (c) + height: Height (d) + label: Record Identifiers that are used by some measure programs. + type: type of action + M: Set measure + A: Add measure + Sets the decomposition of a record in a child record + """ + if not utils.is_valid_code(code)[0]: + raise ValueError, utils.mapping(_("Invalid parent code: $1"), + (code,)) + if not utils.is_valid_code(child_code)[0]: + raise ValueError, utils.mapping(_("Invalid child code: $1"), + (code,)) + if not isinstance(position, int): + raise ValueError, utils.mapping(_("Invalid position in measure "\ + "$1, in code $2"), (parent_code, position)) + # Test circular references + _all_parent_list = self.getAllParents(code) + [ code ] + _all_child_list = self.getAllchildren(child_code) + [ child_code ] + for _parent_code in _all_parent_list: + if _parent_code in _all_child_list: + # TODO: change return to except + print utils.mapping(_("Circular Decomposition, parent code: "\ + "$1, child code: $2, repeated code: $3"), + (code, child_code, _parent_code)) + return + # Creating reference to parent code in child record + if child_code in self.__records: + _child_record = self.__records[child_code] + else: + _child_record = self.setRecord(child_code, [], -1, "", "", [], [], + "", "") + if code in self.__records: + code = self.__records[code].code + _child_record.appendParent(code) + child_code = self.__records[child_code].code + if code in self.__records: + # if the code exits retake previous values. + _record = self.__records[code] + _child_number = len(_record.children) + if position == -1: + position = _child_number + if position == _child_number: + # The record do not have the child + if not isinstance(factor, float): factor = 1.0 + if not isinstance(yield_, float): yield_ = 1.0 + if not isinstance(total, float): total = 0.0 + if not isinstance(list_lines, list): list_lines = [] + _child = _record.appendChild(child_code, self.getDecimals(), + factor, yield_, total, list_lines, type, label) + elif position < _child_number: + # The record have the child + _child = _record.children[position] + if child_code != "" and child_code != _child.code: + _child.code = child_code + if factor != "" : + if not isinstance(factor, float): + factor == 1.0 + _child.budgetMeasures[0].setFactor(factor, + self.getDecimals(), _record.recordType) + if yield_ != "": + if not isinstance(yield_, float): + yield_ = 1.0 + _child.budgetMeasures[0].setYield(yield_, + self.getDecimals(), _record.recordType) + _measure = _child.budgetMeasures[0] + if total != "": + if not isinstance(total, float): + yield_ = 0.0 + _measure.setMeasure(total, self.getDecimals()) + if isinstance(list_lines, list) and len(list_lines) > 0: + _measure.buildMeasure(list_lines, type, self.getDecimals(), + _record.recordType) + if isinstance(label, str) and label != "" : + _measure.label = label + else: + # TODO: change return for except + print utils.mapping(_("Error: Invalid child position in " + "decomposition. Parent code: $1 Child code: $2 "\ + "Position: $3"), (code, child_code, position)) + return + else: + if child_code == "" : + print utils.mapping(_("Error: Empty child code. Parent code: "\ + "$1 Position: $2"), (code, position)) + return + if position == -1: + position = 0 + elif position != 0: + print utils.mapping(_("Error: Invalid child position in "\ + "decomposition. Parent code: $1 Child code: $2 "\ + "Position: $3"), (code, child_code, position)) + return + if not isinstance(factor, float): + factor == 1.0 + if not isinstance(yield_, float): + yield_ = 1.0 + _record = self.setRecord(code, [], "", "", "", [], [], + "", "") + _child = _record.appendChild(child_code, self.getDecimals(), + factor, yield_, total, list_lines, type, label) + _child.budgetMeasures[0] = measure + + def eval_formula(self, formula, a, b, c, d): + """eval_formula(self, formula, a, b, c, d) + + formula: + Valid Operator: '(', ')', '+', '-', '*', '/' and '^' + Valid variable: 'a', 'b', 'c','d'y 'p' (Pi=3.1415926) + units: Number of Units (a) + length: Length (b) + width: Width (c) + height: Height (d) + + Evals the formula and return the result + """ + if a == "": a = 0.0 + if b == "": b = 0.0 + if c == "": c = 0.0 + if d == "": d = 0.0 + try: + a = float(a) + except: + raise ValueError, _("'a' value must be a float number") + try: + b = float(b) + except: + raise ValueError, _("'b' value must be a float number") + try: + c = float(c) + except: + raise ValueError, _("'c' value must be a float number") + try: + d = float(d) + except: + raise ValueError, _("'d' value must be a float number") + # spaces are erased + sre.sub("[ ]","",formula) + # operators and varibles are replaced + formula = formula.replace("+", " + ") + formula = formula.replace("-", " - ") + formula = formula.replace("*", " * ") + formula = formula.replace("/", " / ") + formula = formula.replace("^", " ** ") + formula = formula.replace("(", " ( ") + formula = formula.replace(")", " ) ") + formula = formula.replace("a", str(a)) + formula = formula.replace("b", str(b)) + formula = formula.replace("c", str(c)) + formula = formula.replace("d", str(d)) + formula = formula.replace("p", "3.1415926") + _list_formula = formula.split(" ") + _formula2 = "" + for oper in _list_formula: + try: + _float_oper= str(float(oper)) + _formula2 = _formula2 + _float_oper + except ValueError: + _formula2 = _formula2 + oper + _g = {"__builtins__":{}} + try: + return eval(_formula2, _g) + except: + raise ValueError, _("Invalid formula") + + def getText(self,code): + """getText(self,code) + + code: the record code + Returns the description text of a record + """ + if code in self.__records: + return self.__records[code].text + else: + raise IndexError, _("Invalid code") + + def setText(self,code,text): + """setText(self,code,text) + + code: the parent record code + text: the descripion text + Sests the description text of a record + """ + if not utils.is_valid_code(code)[0]: + raise ValueError, utils.mapping(_("Invalid record: $1"), (code,)) + if not code in self.__records: + _record = self.setRecord(code, [], "", "", "", [], [], + "", "") + _record.text = text + else: + _record = self.__records[code] + _record.text = text + + def setRecord(self, code, synonyms, hierarchy, unit, summary, price, date, + type, subtype): + """setRecord(self, code, synonyms, hierarchy, unit, summary, price, + date, type, subtype) + + code: Code string + synonyms: List of synonym codes of the record + hierarchy: + 0 -> root + 1 -> Chapter/Subchapter + 2 -> Other + unit: unit of measure record + summary: Short description of a record + price: List of prices + date: List of dates + "type" and "subtype": + 0 Without classifying + EA Auxiliary element + EU Unitary element + EC Complex element + EF Functional element + OB Construction site + PA Cost overrun + PU Unitary budget + 1 Labourforce + H Labourforce + 2 Machinery and auxiliary equipment + Q Machinery + % Auxiliary equipment + 3 Building materials + MC Cement + MCr Ceramic + MM Wood + MS Iron and steel + ME Energy + MCu Copper + MAl Aluminium + ML Bonding agents + M Others materials + Hierarchy type subtype + 0->root -> 0 -> None,OB + 1->[sub]chapter -> 0 -> None,PU + 2->Other -> 0 -> None,EA,EU,EC,EF,PA + 1 -> None,H + 2 -> None,Q,% + 3 -> None,MC,MCr,MM,MS,ME,MCu,Mal,ML,M + Adds a record in the budget + """ + # hierarchy + if hierarchy == 0 : + # is the root record + if self.__root is None: + self.__root = code + else: + print _("Only can be one root record") + return + # retake previous values. + # TODO: test synonyms + _budget = self + if not code in self.__records: + if code[-1] == "$": + _record = ParametricRecord(_budget.getDecimals(), code, + synonyms, hierarchy, + unit, summary, [], type, subtype, + [], "") + else: + _record = Record(_budget.getDecimals(), code, synonyms, + hierarchy, unit, + summary, [], type, subtype,[], "") + self.__records[code] = _record + _prices = [[price[i], date[i]] for i in range(len(price))] + _record.setPrices(_prices, self.getDecimals()) + else: + _record = self.__records[code] + code = _record.code + if len(synonyms) != 0 and synonyms[0] == "": + synonyms = _record.synonyms + if unit == "": + unit = _record.unit + if summary == "": + summary = _record.summary + #TODO: test empty price list + if len(price) == 0 or price[0] == "": + _prices = _record.prices + else: + _prices = [ [price[i], date[i]] for i in range(len(price))] + if type == "": + type = _record.recordType.type + _record.synonyms = synonyms + _record.unit = unit + _record.summary = summary + _record.setPrices(_prices, self.getDecimals()) + _record.recordType.hierarchy = hierarchy + _record.recordType.type = type + _record.recordType.subtype = subtype + return _record + + def hasRecord(self,code): + """hasRecord(self,code) + + code: Code record + Return True if the budget have this record code. + """ + if code in self.__records: + return True + else: + return False + + def getRecord(self, code): + """getRecord(self, code) + + code: Code record + Return the Record object + """ + return self.__records[code] + + def addPriceToRecord(self, price_date, record): + """addPriceToRecord(self, price, record) + + Add a price to the price list of the record. + price must fulfill: + - must be a list with two items + - the first item: price must be a float + """ + record.addPrice(price_date, self.getDecimals()) + + def getStrPriceFromRecord(self, index_price, record): + _price = record.getPrice(index_price) + _D = self.getDecimals().getD(record.recordType) + _price = ("%." + str(_D) + "f" ) % _price + return _price + + def getCode(self, path): + """getCode(self, path) + + path: path record in the budget. + Return the code record + """ + if isinstance(path, tuple) and len(path)>= 1: + if path[0] == 0: + _code = self.__root + for i in path[1:]: + if isinstance(i, int): + _record = self.__records[_code] + _children_list = _record.children + try: + _child = _children_list[i] + except: + raise ValueError, _("This record does not exits") + _code = _child.code + else: + raise ValueError, _("Path item must be a integer") + return _code + else: + raise ValueError, _("This record does not exits") + else: + raise ValueError, utils.mapping(_("Path must be a not empty "\ + "tuple: $1"), (str(path),)) + + def getAmount(self, path): + """def getAmount(self,path) + + path: record path + Calculate the record amount + """ + if len(path) == 1: + # root: amount is the root price + _root = self.getRecord(self.getRoot()) + _amount = _root.getPrice(self.__title_index) + return _amount + else: + _parent_code = self.getCode(path[:-1]) + _parent_record = self.getRecord(_parent_code) + _child_number = path[-1] + + _decomposition = _parent_record.children[_child_number] + _factor = _decomposition.budgetMeasures[0].factor + _yield = _decomposition.budgetMeasures[0].yield_ + _child_code = _decomposition.code + _child_record = self.getRecord(_child_code) + _price = _child_record.getPrice(self.getActiveTitle()) + _DR = self.getDecimals().getDR(_parent_record.recordType) + _total_yield = round(_factor * _yield, _DR) + _DI = self.getDecimals().getDI(_parent_record.recordType) + _amount = round(_total_yield * _price, _DI) + return _amount + + def getStrAmount(self, path): + """def getStrAmount(self, path) + + path: record path + Calculate the string record amount + """ + if len(path) == 1: #root + _root = self.getRecord(self.getRoot()) + _amount = self.getStrPriceFromRecord(self.__title_index, _root) + return _amount + else: + _parent_code = self.getCode(path[:-1]) + _parent_record = self.getRecord(_parent_code) + _amount = self.getAmount(path) + _DI = self.getDecimals().getDI(_parent_record.recordType) + _amount = ("%." + str(_DI) + "f") % _amount + return _amount + + def setSheetSection(self,sheet_code,sheet_title): + if not isinstance(sheet_code, str): + raise ValueError, _("The sheet code must be a string") + if not isinstance(sheet_title, str): + raise ValueError, _("The sheet title must be a string") + self.__sheet_sections[sheet_code] = sheet_title + def hasSheetSection(self, section): + return section in self.__sheet_sections + def getSheetSection(self, section): + return self.__sheet_sections[section] + def setSheetSections(self,dictionary): + if not isinstance(dictionary, dict): + raise ValueError, _("The sheet sections must be a dictionary") + for sheet_code in dictionary.keys(): + self.setSheetSection(sheet_code, dictionary[sheet_code]) + def setSheetField(self, field_code, field_title): + if not isinstance(field_code, str): + raise ValueError, _("The field code must be a string") + if not isinstance(field_title, str): + raise ValueError, _("The field title must be a string") + self.__sheet_fields[field_code] = field_title + def hasSheetField(self, field): + return field in self.__sheet_fields + def getSheetField(self, field): + return self.__sheet_fields[field] + def setSheetFields(self, field_dict): + if not isinstance(field_dict, dict): + raise ValueError, _("The sheet field must be a dictionary") + for field_code in field_dict.keys(): + self.setSheetField( field_code, field_dict[field_code]) + def setSheetParagraph(self, paragraph_code, paragraph_text): + if not isinstance(paragraph_code, str): + raise ValueError, _("The paragraph code must be a string") + if not isinstance(paragraph_text, str): + raise ValueError, _("The paragraph text must be a string") + self.__sheet_paragraphs[paragraph_code] = paragraph_text + def hasSheetParagraph(self, paragraph): + return paragraph in self.__sheet_paragraphs + def getSheetParagraph(self, paragraph): + return self.__sheet_paragraphs[paragraph] + def setSheetParagraphs(self, paragraph_dict): + if not isinstance(paragraph_dict, dict): + raise ValueError, _("The paragraph dict must be a dictionary") + for paragraph_code in paragraph_dict.keys(): + self.setSheetParagraph( paragraph_code, paragraph_dict[paragraph_code]) + def setSheetRecord(self, record_code, field, section_dict): + if not isinstance(record_code, str): + raise ValueError, _("The record_code code must be a string") + if not isinstance(field, str): + raise ValueError, _("The field must be a string") + if not isinstance(section_dict, dict): + raise ValueError, _("The section dict must be a dictionary") + #-# + # TODO: Add a empty record? + if not self.hasRecord(record_code): + print utils.mapping(_("Error: The budget do not have this record "\ + "code and can not be added the sheet text in the field $1. "\ + "Record Code: $2"), ( field, record_code)) + return + #-# + if not self.hasSheetField(field): + self.setSheetField(field, "") + for section, paragraph in section_dict.iteritems(): + if not self.hasSheetParagraph(paragraph): + self.setSheetParagraph(paragraph,"") + if not self.hasSheetSection(section): + self.setSheetSection(section, "") + _sheet = self.getRecord(record_code).getSheet() + _sheet.addSection(field, section, paragraph) + def addFile(self, record_code, filepath, type, description): + if not isinstance(record_code, str): + raise ValueError, _("The record_code code must be a string") + if not isinstance(filepath, str): + raise ValueError, _("The filename must be a string") + #-# + # TODO: Add a empty record? + if not self.hasRecord(record_code): + print utils.mapping(_("Error: The budget do not have the record "\ + "code $1 and can not be added the file: $2"), + (record_code, filepath)) + return + #-# + _record = self.getRecord(record_code) + _record.addFile(filepath, type, description) + def setCompany(self, company_code, sumamary, name, offices, + cif, web, email): + if not isinstance(company_code, str): + raise ValueError, _("The company code must be a string") + if not isinstance(sumamary, str): + raise ValueError, _("The summary must be a string") + if not isinstance(name, str): + raise ValueError, _("The name must be a string") + if not isinstance(offices, list): + raise ValueError, _("The name must be a list") + _offices = [] + for _office in offices: + if not isinstance(_office, list): + raise ValueError, _("The office must be a list") + if not len(_office) == 10: + raise ValueError, _("The office must be a 10 items list") + for _item in _office[:7] + _office[9:10]: + if not isinstance(_item, str): + raise ValueError, _("This office item must be a "\ + "string") + for _item in _office[7:8]: + if not isinstance(_item, list): + raise ValueError, _("This office item must be a "\ + "list") + _offices.append(Office(_office[0], + _office[1], + _office[2], + _office[3], + _office[4], + _office[5], + _office[6], + _office[7], + _office[8], + _office[9])) + if not isinstance(cif, str): + raise ValueError, _("The name must be a string") + if not isinstance(web, str): + raise ValueError, _("The web must be a string") + if not isinstance(email, str): + raise ValueError, _("The email must be a string") + + self.__companys[company_code] = Company(company_code, sumamary, name, + _offices, cif, web, email) + def getCompany(self, company_code): + return self.__companys[company_code] + def getCompanyKeys(self): + return self.__companys.keys() + def addTecInfo(self, ti_code, text, unit): + if not isinstance(ti_code, str): + raise ValueError, _("The tecnical info code must be a string") + if not isinstance(text, str): + raise ValueError, _("The tecnical info description must be a "\ + "string") + if not isinstance(unit, str): + raise ValueError, _("The tecnical info unit must be a string") + self.__tec_info[ti_code] = [text, unit] + def hasTecInfo(self, ti_code): + return ti_code in self.__tec_info + def getTecInfo(self, ti_code): + return self.__tec_info[ti_code] + def setTecnicalInformation(self, record_code, ti_dict): + """setTecnicalInformation(record_code, ti_dict) + + Sets the tecnical information to a record + record_code: the record code + ti_dict: {ti_code : ti_value} + """ + # TODO: setTecnicalInformation + pass + def changeCode(self, record_code, new_record_code): + """changeCode(self, record_code, new_record_code): + + Change the record code for a new recor code. + """ + if self.hasRecord(record_code) and not self.hasRecord(new_record_code): + _record = self.__records[code] + _record.code = new_record_code + _parents = _record.parents + for _parent in _parents: + _decomposition_list = self.__records[_parent].children + for _decomposition in _decomposition_list: + if _decomposition.code == record_code: + _decomposition.code = new_record_code + break + _children = self.getchildren(record_code) + for _child in _children: + _parents_list = self.__records[_child].parents + for index in range(len(_parents_list)): + if _parents_list[index] == record_code: + _parents_list[index] = new_record_code + break + self.__records[new_record_code] = _record + del self.__records[record_code] + # TODO: attachment files + + def addLabel(self, record_code, label): + """addLabel(self, record_code, label) + + Add a label to a record + """ + if not isinstance(label,str): + raise ValueError, _("The label must be a string") + if self.hasRecord(record_code): + _record = self.__records[record_code] + _record.addLabel(label) + if not label in self.__labels: + self.__labels[label] = [record_code] + else: + _codes = self.__labels[label] + if not record_code in _codes: + _codes.append(record_code) + def setParametricSelectComment(self, record_code, comment): + """setParametricSelectComment(self, record_code, comment) + + Sets Paramtric Record Select Comment + """ + if not isinstance(record_code, str): + raise ValueError, _("The record_code code must be a string") + if not isinstance(comment, str): + raise ValueError, _("The parametric select comment must be a "\ + "string") + if not self.hasRecord(record_code): + print utils.mapping(_("Error: The budget do not have the record "\ + "code $1 and can not be added the Parametric select comment: "\ + "$2"), + (record_code, comment)) + return + _record = self.getRecord(record_code) + if not isinstance(_record, ParametricRecord): + print utils.mapping(_("Error: The Record $1 is not a "\ + "Parametric Record and can not have Parametric comment"), + (record_code,)) + else: + _record.select_comment = comment + + def setParametricSummary(self, record_code, summary): + """setParametricSummary(self, record_code, summary) + + Sets parametric record summary + """ + if not isinstance(record_code, str): + raise ValueError, _("The record_code code must be a string") + if not isinstance(summary, str): + raise ValueError, _("The summary record must be a string") + if not self.hasRecord(record_code): + print utils.mapping(_("Error: The budget do not have the record "\ + "code $1 and can not be seted the summary: $2"), + (record_code, summary)) + return + _record = self.getRecord(record_code) + if not isinstance(_record, ParametricRecord): + print utils.mapping(_("Error: The Record $1 is not a "\ + "Parametric Record and can not have Parametric summary"), + (record_code,)) + else: + self.getRecord(record_code).parametric_summary = summary + + def setParametricText(self, record_code, text): + """setParametricText(self, record_code, text) + + Sets parametric record text + """ + if not isinstance(record_code, str): + raise ValueError, _("The record_code code must be a string") + if not isinstance(text, str): + raise ValueError, _("The text record must be a string") + if not self.hasRecord(record_code): + print utils.mapping(_("Error: The budget do not have the record "\ + "code $1 and can not be seted the text: $2"), + (record_code, text)) + return + _record = self.getRecord(record_code) + if not isinstance(_record, ParametricRecord): + print utils.mapping(_("Error: The Record $1 is not a "\ + "Parametric Record and can not have Parametric text"), + (record_code,)) + else: + self.getRecord(record_code).parametric_text = text + +class Office(object): + """base.Office: + + Description: + Office of a company + Constructor: + base.Office(type, subname, address, postal_code, town, province, + country, phone, fax, contact_person) + Ancestry: + +-- object + +-- Office + Atributes: + "officeType" : type of Office + are defined: + "C" Central. + "D" Local Office. + "R" performer. + "subname" : name of Office or Performer + "address" : + "postal_code" : + "town" : + "province" : + "country" : + "phone" : list of phone numbers + "fax" : list of fax numbers + "contact_person" : name of contact person + "values": + Methods: + __getstate__(self) + __setstate__(self, tuple) + __init__(self, measure, lines, label) + {get/set}OfficeType + {get/set}Subname + {get/set}Address + {get/set}PostalCode + {get/set}Town + {get/set}Province + {get/set}Country + {get/set}Phone + {get/set}Fax + {get/set}ContactPerson + getValues + """ + __slots__ = ["_Office__officeType", + "_Office__subname", + "_Office__address", + "_Office__postal_code", + "_Office__town", + "_Office__province", + "_Office__country", + "_Office__phone", + "_Office__fax", + "_Office__contact_person", + ] + def __getstate__ (self): + return ( self.__officeType, + self.__subname, + self.__address, + self.__postal_code, + self.__town, + self.__province, + self.__country, + self.__phone, + self.__fax, + self.__contact_person) + def __setstate__(self,tuple): + self.__officeType = tuple[0] + self.__subname = tuple[1] + self.__address = tuple[2] + self.__postal_code = tuple[3] + self.__town = tuple[4] + self.__province = tuple[5] + self.__country = tuple[6] + self.__phone = tuple[7] + self.__fax = tuple[8] + self.__contact_person = tuple[9] + + def __init__(self, type, subname, address, postal_code, town, province, + country, phone, fax, contact_person): + self.officeType = type + self.subname = subname + self.address = address + self.postal_code = postal_code + self.town = town + self.province = province + self.country = country + self.phone = phone + self.fax = fax + self.contact_person = contact_person + def getOfficeType(self): + return self.__officeType + def setOfficeType(self, type): + self.__officeType = type + def getSubname(self): + return self.__subname + def setSubname(self, subname): + self.__subname = subname + def getAddress(self): + return self.__address + def setAddress(self, address): + self.__address = address + def getPostalCode(self): + return self.__postal_code + def setPostalCode(self, postal_code): + self.__postal_code = postal_code + def getTown(self): + return self.__town + def setTown(self, town): + self.__town = town + def getProvince(self): + return self.__province + def setProvince(self, province): + self.__province = province + def getCountry(self): + return self.__country + def setCountry(self, country): + self.__country = country + def getPhone(self): + return self.__phone + def setPhone(self, phone): + self.__phone = phone + def getFax(self): + return self.__fax + def setFax(self, fax): + self.__fax = fax + def getContactPerson(self): + return self.__contact_person + def setContactPerson(self, contact_person): + self.__contact_person = contact_person + def getValues(self): + return {"officeType": self.officeType, + "subname": self.subname, + "address": self.address, + "postal code": self.postal_code, + "town": self.town, + "province": self.province, + "country": self.country, + "phone": self.phone, + "fax": self.fax, + "contact person": self.contact_person, + } + officeType = property(getOfficeType, setOfficeType, None, + """Type of office + """) + subname = property(getSubname, setSubname, None, + """Name of office + """) + address = property(getAddress, setAddress, None, + """Adress + """) + postal_code = property(getPostalCode, setPostalCode, None, + """Postal code + """) + town = property(getTown, setTown, None, + """Town + """) + province = property(getProvince, setProvince, None, + """Province + """) + country = property(getCountry, setCountry, None, + """Country + """) + phone = property(getPhone, setPhone, None, + """Phone numbers + """) + fax = property(getFax, setFax, None, + """Fax numbers + """) + contact_person = property(getContactPerson, setContactPerson, None, + """Contact Person + """) + values = property(getValues, None, None, + """Dictionary with comapany values + """) + +class Company(object): + """base.Company: + + Description: + Company object + __slots__ attribute, __getstate__ and __setstate__ method are defined + to use less ram memory. + Constructor: + base.Company(code, summary, name, offices, cif, web, email) + Ancestry: + +-- object + +-- Company + Atributes: + "code": code to indentifie the company in the buget + "summary": short name + "name": long name + "offices": List of Offices + "cif": CIF + "web": web page + "email": email + "values": + Methods: + __getstate__(self) + __setstate__(self, tuple) + __init__(self, measure, lines, label) + {get/set}Code + {get/set}Summary + {get/set}Name + {get/set}Offices + {get/set}Cif + {get/set}Web + {get/set}Email + getValues + """ + __slots__ = ["_Company__code", + "_Company__summary", + "_Company__name", + "_Company__offices", + "_Company__cif", + "_Company__web", + "_Company__email", + ] + def __getstate__ (self): + return ( self.__code, + self.__summary, + self.__name, + self.__offices, + self.__cif, + self.__web, + self.__email) + def __setstate__(self,tuple): + self.__code = tuple[0] + self.__summary = tuple[1] + self.__name = tuple[2] + self.__offices = tuple[3] + self.__cif = tuple[4] + self.__web = tuple[5] + self.__email = tuple[6] + + def __init__(self, code, summary, name, offices, cif, web, email): + self.code = code + self.summary = summary + self.name = name + self.offices = offices + self.cif = cif + self.web = web + self.email = email + def getCode(self): + return self.__code + def setCode(self, code): + self.__code = code + def getSummary(self): + return self.__summary + def setSummary(self, summary): + self.__summary = summary + def getName(self): + return self.__name + def setName(self, name): + self.__name = name + def getOffices(self): + return self.__offices + def setOffices(self, offices): + self.__offices = offices + def getCif(self): + return self.__cif + def setCif(self, cif): + self.__cif = cif + def getWeb(self): + return self.__web + def setWeb(self, web): + self.__web = web + def getEmail(self): + return self.__email + def setEmail(self, email): + self.__email = email + def getValues(self): + return {"code": self.code, + "summary": self.summary, + "name": self.name, + "cif": self.cif, + "web": self.web, + "email": self.email} + code = property(getCode, setCode, None, + """Company code + """) + summary = property(getSummary, setSummary, None, + """Company summary + """) + name = property(getName, setName, None, + """Company name + """) + offices = property(getOffices, setOffices, None, + """List of Offices + """) + cif = property(getCif, setCif, None, + """CIF + """) + web = property(getWeb, setWeb, None, + """Web page + """) + email = property(getEmail, setEmail, None, + """Email + """) + values = property(getValues, None, None, + """Dictionary with comapany values + """) + +class File(object): + """base.Company: + + Description: + File object + Constructor: + base.File(name, type, description) + Ancestry: + +-- object + +-- File + Atributes: + "name": name + "fileType": type of file + "description": description file + Methods: + __getstate__(self) + __setstate__(self, tuple) + __init__(self, path,type, description) + {get/set}Name + {get/set}FileType + {get/set}Description + getValues + """ + __slots__ = ["_File__name", + "_File__fileType", + "_File__description", + + ] + def __getstate__ (self): + return (self.__name, + self.__description, + self.__fileType, + ) + def __setstate__(self,tuple): + self.__name = tuple[0] + self.__fileType = tuple[1] + self.__description = tuple[2] + def __init__(self, name, type, description): + self.name = name + self.fileType = type + self.description = description + def getName(self): + return self.__name + def setName(self, name): + self.__name = name + def getFileType(self): + return self.__fileType + def setFileType(self, type): + self.__fileType = type + def getDescription(self): + return self.__description + def setDescription(self, description): + self.__description = description + def getValues(self): + return {"name": self.name, + "fileType": self.fileType, + "description": self.description, + } + name = property(getName, setName, None, + """File name + """) + fileType = property(getFileType, setFileType, None, + """FileType + """) + description = property(getDescription, setDescription, None, + """File description + """) + values = property(getValues, None, None, + """Dictionary with file values + """) + +class RecordType(object): + """base.RecordType: + + Description: + Record Type object + "hierarchy": + -1 -> temporarily unfixed + 0 -> root + 1 -> Chapter/Subchapter + 2 -> Other + "type" and "subtype": + 0 Without classifying + EA Auxiliary element + EU Unitary element + EC Complex element + EF Functional element + OB Construction site + PA Cost overrun + PU Unitary budget + 1 Labourforce + H Labourforce + 2 Machinery and auxiliary equipment + Q Machinery + % Auxiliary equipment + 3 Building materials + MC Cement + MCr Ceramic + MM Wood + MS Iron and steel + ME Energy + MCu Copper + MAl Aluminium + ML Bonding agents + M Others materials + Hierarchy type subtype + 0->root -> 0 -> None,OB + 1->[sub]chapter -> 0 -> None,PU + 2->Other -> 0 -> None,EA,EU,EC,EF,PA + 1 -> None,H + 2 -> None,Q,% + 3 -> None,MC,MCr,MM,MS,ME,MCu,Mal,ML,M + Constructor: + base.File(hierarchy,type,subtype) + Ancestry: + +-- object + +-- RecordType + Atributes: + "hierarchy": hierarchy + "type": type + "subtype": subtype + Methods: + __getstate__(self) + __setstate__(self, tuple) + __init__(self, hierarchy, type, subtype) + {get/set}Hierarchy + {get/set}Type + {get/set}Subtype + """ + __slots__ = ["_RecordType__hierarchy", + "_RecordType__type", + "_RecordType__subtype", + ] + def __getstate__ (self): + return (self.__hierarchy, + self.__type, + self.__subtype, + ) + def __setstate__(self,tuple): + self.__hierarchy = tuple[0] + self.__type = tuple[1] + self.__subtype = tuple[2] + def __init__(self, hierarchy, type, subtype): + self.hierarchy = hierarchy + self.type = type + self.subtype = subtype + def getHierarchy(self): + return self.__hierarchy + def setHierarchy(self, hierarchy): + if not hierarchy in [-1, 0 , 1 ,2]: + raise ValueError, utils.mapping(_("Invalid Hierarchy ($1) "\ + "The hierarchy must be -1, 0, 1 or 2"), (str(hierarchy))) + self.__hierarchy = hierarchy + def getType(self): + return self.__type + def setType(self, type): + if not type in ["", 0, 1, 2, 3] : + raise ValueError, utils.mapping(_("Invalid type ($1),"\ + "the type must be (empty string,0,1,2,3)"),(str(type))) + self.__type = type + def getSubtype(self): + return self.__subtype + def setSubtype(self, subtype): + if not subtype in ["", "OB", "PU", "EA", "EU", "EC", "EF", "PA", "H", + "Q", "%", "MC", "MCr", "MM", "MS", "ME", "MCu", + "Mal","ML","M"]: + raise ValueError, utils.mapping(_("Invalid subtype ($1), The "\ + "subtype must one in (empty string, EA, "\ + "EU, EC, EF, OB, PA, PU, H, Q, %, MC, MCr, "\ + "MM, MS, ME, MCu, MAl, ML, M)"), (str(subtype))) + self.__subtype = subtype + hierarchy = property(getHierarchy, setHierarchy, None, + """Record Hierarchy + -1 -> temporarily unfixed + 0 -> root + 1 -> Chapter/Subchapter + 2 -> Other + """) + type = property(getType, setType, None, + """Record Type + 0 Without classifying + 1 Labourforce + 2 Machinery and auxiliary equipment + 3 Building materials + """) + subtype = property(getSubtype, setSubtype, None, + """Record Subtype + None + EA Auxiliary element + EU Unitary element + EC Complex element + EF Functional element + OB Construction site + PA Cost overrun + PU Unitary budget + H Labourforce + Q Machinery + % Auxiliary equipment + MC Cement + MCr Ceramic + MM Wood + MS Iron and steel + ME Energy + MCu Copper + MAl Aluminium + ML Bonding agents + M Others materials + """) diff -r a1703c4f2990 -r 2ac1551ad2ab Generic/config.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Generic/config.py Sun Oct 31 20:07:33 2010 +0100 @@ -0,0 +1,46 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +## File config.py +## This file is part of pyArq-Presupuestos. +## +## Copyright (C) 2010 Miguel Ángel Bárcena Rodríguez +## +## +## pyArq-Presupuestos is free software: you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## pyArq-Presupuestos is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program. If not, see . + +"""config module + +Unused module +""" +class Config(object): + """config.Config: + + Description: + Config object + Ancestry: + +-- object + +-- Config + Atributes: + "": + Methods: + + """ + def __init__(self): + self.__path = "" + def _getPath(self): + return self.__path + def _setPath(self, path): + self.__path = path + path = property(_getPath, _setPath, None, + """Base path where find all the program files needed""") diff -r a1703c4f2990 -r 2ac1551ad2ab Generic/durusdatabase.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Generic/durusdatabase.py Sun Oct 31 20:07:33 2010 +0100 @@ -0,0 +1,46 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +## File durus.py +## This file is part of pyArq-Presupuestos. +## +## Copyright (C) 2010 Miguel Ángel Bárcena Rodríguez +## +## +## pyArq-Presupuestos is free software: you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## pyArq-Presupuestos is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program. If not, see . + +# Modules +import os.path +# Durus Modules +from durus.file_storage import FileStorage +from durus.connection import Connection + +class DurusFile(object): + def __init__(self, file, new): + self.__file = file + if new: + if os.path.exists(self.__file): + os.remove(self.__file) + self.__connection = Connection(FileStorage(self.__file)) + self.__root = self.__connection.get_root() + + def close(self): + self.__connection.get_storage().close() + + def getBudget(self): + return self.__root["budget"] + + def setBudget(self, budget): + self.__root["budget"] = budget + self.__connection.commit() + diff -r a1703c4f2990 -r 2ac1551ad2ab Generic/fiebdc.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Generic/fiebdc.py Sun Oct 31 20:07:33 2010 +0100 @@ -0,0 +1,2126 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +## File fiebdc.py +## This file is part of pyArq-Presupuestos. +## +## Copyright (C) 2010 Miguel Ángel Bárcena Rodríguez +## +## +## pyArq-Presupuestos is free software: you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## pyArq-Presupuestos is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program. If not, see . + +# specifications in http://www.fiebdc.org + +# Modules +import time +import re +import calendar +import os.path + +# pyArq-Presupuestos modules +import base +from Generic import utils +from Generic import globals + +class Read(object): + """fiebdc.Read: + + Description: + Reads and parses a fiebdc file + Constructor: + fiebdc.Read(filename=None, budget=None) + Ancestry: + +-- object + +-- Read + Atributes: + "__budget": budget ("base.Budget" object) + "__file_format": File format of the fiebdc file + "__format_list": List of file format that can be readed + "__character_sets_dict": Dictionary with the character sets supported + "__character_set": character_set of the file + "__generator": program which the file is created + "__cancel": Boolean value, True mean that the read process must stop + "__filename": The filename of the fiebdc file that is readed + "__pattern": re compiled pattern dict + Methods: + __init__(self, filename=None, budget=None) + cancel(self) + eraseControlCharacters(self, string) + validateCode(self, code) + parseDate(self, date) + parseRecord(self,record) + _parseV(self, field_list) + _parseC(self, field_list) + _parseDY(self, field_list) + _parseMN(self, field_list) + _parseT(self, field_list) + _parseK(self, field_list) + _parseW(self, field_list) + _parseL(self, field_list) + _parseQ(self, field_list) + _parseJ(self, field_list) + _parseG(self, field_list) + _parseE(self, field_list) + _parseX(self, field_list) + _parseF(self, field_list) + readFile(self, budget=None, filename=None) + """ + def __init__(self, filename=None, budget=None): + """def __init__(self, filename=None, budget=None) + + Sets the instance attributes + """ + self.__budget = budget + self.__filename = filename + if not self.__budget is None: + self.__budget.filename = self.__filename + self.__cancel = False + self.__format_list = ["FIEBDC-3/95", "FIEBDC-3/98", "FIEBDC-3/2002", + "FIEBDC-3/2004", "FIEBDC-3/2007"] + # ANSI->¿"ISO-8859-15" or "latin1 ISO-8859-1" or "cp1252 windows-1252"? + # 850 -> IBM850 -> cp850 + # 437 -> IBM437 -> cp437 + self.__character_sets_dict = {"ANSI" : "cp1252", + "850" : "850", + "437" : "cp437"} + self.__file_format = "FIEBDC-3/2007" + self.__generator = globals.version + self.__character_set = "850" + self.__pattern = { + "control_tilde" : re.compile("((\r\n)| |\t)+~"), + "control_vbar" : re.compile("((\r\n)| |\t)+\|"), + "control_backslash" : re.compile(r"((\r\n)| |\t)+\\"), + "valid_code" : re.compile("[^A-Za-z0-9ñÑ.$#%&_]"), + "special_char": re.compile("[#%&]"), + "no_float": re.compile("[^0-9.]"), + "formula" : re.compile(".*[^0123456789\.()\+\-\*/\^abcdp ].*"), + "comment": re.compile("#.*\r\n"), + "empty_line": re.compile(r"(\r\n) *\r\n"), + "space_before_backslash" : re.compile(r"( )+\\"), + "space_after_backslash" : re.compile(r"\\( )+"), + "start_noend_backslash" : re.compile("(\r\n\\\.*[^\\\])\r\n"), + "end_oper": re.compile("(\+|-|\*|/|/^|@|&|<|>|<=|>=|=|!) *\r\n"), + "matricial_var" : re.compile("(\r\n *[%|\$][A-ZÑ].*=.*,) *\r\n"), + "descomposition" : re.compile("^([^:]+):(.*)$"), + "var" : re.compile("^([$%][A-ZÑ][()0-9, ]*)=(.*)$"), + "after_first_tilde" : re.compile("^[^~]*~"), + "end_control" : re.compile("((\r\n)| |\t)+$"), + } + + def cancel(self): + """def cancel(self) + + Sets the "__cancel" attribute to True, It stops the read process. + """ + self.__cancel = True + + def eraseControlCharacters(self, string): + """eraseControlCharacters(self,string) + + Return a copy of the string with the blank characters (32), + tabs (9) and end of line (13 and 10) before of the separators + '~', '|' erased. + Before separator \ not deleted because it affects the reading of the + record ~P + """ + # "control_tilde" : "((\r\n)| |\t)+~" + string = self.__pattern["control_tilde"].sub("~",string) + # "control_vbar" : "((\r\n)| |\t)+\|" + string = self.__pattern["control_vbar"].sub("|",string) + # "control_backslash" : r"((\r\n)| |\t)+\\" + #string = self.__pattern["control_backslash"].sub(r"\\",string) + return string + + def validateCode(self, code): + """validateCode(self, code) + + Test if the code have invalid characters and try to erase it, + if it is posible return a valid code else return a empty string. + """ + if not isinstance(code, str): + print _("Invalid code, it must be a string") + return "" + # Valid chararcter: A-Z a-z 0-9 ñ Ñ . $ # % & _ + # "valid_code" : "[^A-Za-z0-9ñÑ.$#%&_]" + _code = self.__pattern["valid_code"].sub("", code) + if _code != code: + print utils.mapping(_("The code '$1' have invalid characters."), + (code,)) + code = _code + # the lasts characters can not be <#> or <##> + # <##> -> root record in FIEFDC-3 + # <#> -> chapter record in FIEFDC-3 + if len(code) > 0: + while code[-1] == "#": + code = code[:-1] + if len(code) > 20: + code = code[:20] + # only one charecter # % or & + if sum([code.count(c) for c in '#%&']) > 1: + print utils.mapping(_("The code '$1' contains special "\ + "characters repeated."),(code,)) + _i = min([code.find(c) for c in '#%&']) + code = code[:_i+1] + \ + self.__pattern["special_char"].sub("", code[_i+1:]) + return code + + def parseDate(self, date): + """parseDate(self, date) + + date: in the format: + uneven len: add a Leading 0 + len = 8 DDMMYYYY + len <= 6 DDMMYY “80/20”. >80 -> >1980 <80 -> <2080 + len < 5 MMYY + len < 3 YY + Test date string and return a tuple (YYYY, MM, DD) + or None if the date format is invalid + """ + # All characters must be numbers, len <= 8 and not empty string + if not date.isdigit() or len(date) > 8 or date == "": + return None + else: + if len(date)%2 == 1: # uneven len: add a leading 0 + date = "0" + date + if len(date) == 8: + _d = int(date[:2]) + _m = int(date[2:4]) + _y = int(date[4:8]) + elif len(date) <= 6: + _y = int(date[-2:]) + if _y < 80: _y = 2000 + _y + else: _y = 1900 + _y + if len(date) == 6: + _d = int(date[:2]) + _m = int(date[2:4]) + elif len(date) == 4: + _d = 0 + _m = int(date[:2]) + elif len(date) == 2: + _d = 0 + _m = 0 + if not _d in range(1,31): _d = 0 + if not _m in range(1,12): _m = 0 + if _m == 0: _d = 0 + if _m != 0 and _d != 0: + if calendar.monthrange(_y, _m)[1] < _d: + _d = 0 + return (_y, _m, _d) + + def parseRecord(self,record): + """parseRecord(self,record) + + record: the record line readed from the file whith the format: + type|field|field|subfield\subfield|... + [a] nothing or "a" + {a} zero or more #-#twice#-# "a" + one or more #-#twice#-# "a" + Types: V C D Y M N T K L Q J G E X B F A + V: Property and Version + 1- [File_Owner] + 2- Format_Version[\DDMMYYYY] + 3- [Program_Generator] + 4- [Header]\{Title\} + 5- [Chaters_set] + 6- [Comment] + C: Record: + 1- Code{\Code} + 2- [Unit] + 3- [Summary] + 4- {Price\} + 5- {Date\} + 6- [Type] + D or Y: DECOMPOSITION or ADD DECOMPOSITION + 1- Parent Code + 2- + M or N: MEASURE or ADD MEASURE + 1- [Parent Code\]Child Code + 2- {Path\} + 3- TOTAL MEASURE + 4- {Type\Comment\Unit\Length\Width\Height\} + 5- [Label] + T: Text + 1- Code + 2- Description text + K: Coefficients + 1- { DN \ DD \ DS \ DR \ DI \ DP \ DC \ DM \ DIVISA \ } + 2- CI \ GG \ BI \ BAJA \ IVA + 3- { DRC \ DC \ DRO \ DFS \ DRS \ DFO \ DUO \ DI \ DES \ DN \ + DD \ DS \ DIVISA \ } + 4- [ n ] + L: Sheet of Conditions 1 + A) + 1- Empty + 2- {Section Code\Section Title} + B) + 1- Record Code + 2- {Section Code\Section Text} + 3- {Section Code\RTF file} + 4- {Section Code\HTM file} + Q: Sheet of Conditions 2 + 1- Record Code + 2- {Section Code\Paragraph key\{Field key;}\}| + J: Sheet of Conditions 3 + 1- Paragraph code + 2- [Paragraph text] + 3- [RTF file] + 4- [HTML file] + G: Grafic info + 1- + E: Company + 1- company Code + 2 [ summary ] + 3- [ name ] + 4- { [ type ] \ [ subname ] \ [ address ] \ [ postal_code ] + \ [ town ] \ [ province ] \ [ country ] \ { phone; } + \ { fax; } \ {contact_person; } \ } + 5- [ cif ] \ [ web ] \ [ email ] \ + X: Tecnical information + A) + 1- Empty + 2- < TI_Code \ TI_Descitption \ TI_Unit > + B) + 1- Record_code + 2- < TI_Code \ TI_value > + F: #-#Adjunto#-# File + 1- Record code + 2- { Type \ { Filenames; } \ [Description] } + B: Change code + 1- Record Code + 2- New code + A: Labels + 1- Record Code + 2- + """ + # TODO: ~L ~J RTF and HTML files + # TODO: test ~Q ~J ~G + # TODO: ~P. Registro tipo Descripción Paramétrica. + # TODO: ~O. Registro tipo Relación Comercial. + # TODO: test records + _field_list = record.split("|") + self._record_number = self._record_number +1 + _budget = self.__budget + if _field_list[0] == "V": + self._record_V_number += 1 + self._parseV(_field_list) + elif _field_list[0] == "C": + self._record_C_number += 1 + self._parseC(_field_list) + elif _field_list[0] == "D": + self._record_D_number += 1 + self._parseDY(_field_list) + elif _field_list[0] == "Y": + self._record_Y_number += 1 + self._parseDY(_field_list) + elif _field_list[0] == "M": + self._record_M_number += 1 + self._parseMN(_field_list) + elif _field_list[0] == "N": + self._record_N_number += 1 + self._parseMN(_field_list) + elif _field_list[0] == "T": + self._record_T_number += 1 + self._parseT(_field_list) + elif _field_list[0] == "K": + self._record_K_number += 1 + self._parseK(_field_list) + elif _field_list[0] == "W": + self._record_W_number += 1 + self._parseW(_field_list) + elif _field_list[0] == "L": + self._record_L_number += 1 + self._parseL(_field_list) + elif _field_list[0] == "Q": + self._record_Q_number += 1 + self._parseQ(_field_list) + elif _field_list[0] == "J": + self._record_J_number += 1 + self._parseJ(_field_list) + elif _field_list[0] == "G": + self._record_G_number += 1 + self._parseG(_field_list) + elif _field_list[0] == "E": + self._record_E_number += 1 + self._parseE(_field_list) + elif _field_list[0] == "O": + self._record_O_number += 1 + elif _field_list[0] == "P": + self._record_P_number += 1 + self._parseP(_field_list) + elif _field_list[0] == "X": + self._record_X_number += 1 + self._parseX(_field_list) + elif _field_list[0] == "B": + self._record_B_number += 1 + self._parseB(_field_list) + elif _field_list[0] == "F": + self._record_F_number += 1 + self._parseF(_field_list) + elif _field_list[0] == "A": + self._record_A_number += 1 + self._parseA(_field_list) + else: + self._record_Unknow_number += 1 + + def _parseV(self, field_list): + """_parseV(self, field_list) + + field_list: field list of the record + 0- V :Property and Version + 1- [File_Owner] + 2- Format_Version[\DDMMYYYY] + 3- [Program_Generator] + 4- [Header]\{Title\} + 5- [Chaters_set] + 6- [Comment] + 7- [Data type] + 8- [Number budget certificate] + 9- [Date budget certificate] + """ + if self._record_number != 1: + print utils.mapping(_("The 'V' record (Property and Version) "\ + "must be the first record in the file but it is the "\ + "number: $1"), (self._record_number,)) + print _("The default values were taken and this V record is "\ + "ignored") + return + # _____number of fields_____ + # Any INFORMATION after last field separator is ignored + if len(field_list) > 10: + field_list = field_list[:10] + # If there are no sufficient fields, the fields are added + # with empty value:"" + else: + field_list = field_list + [""]*(10-len(field_list)) + # control character are erased: end of line, tab, space + # only leading and trailing whitespace in owner, generator, comment + # _____Fields_____ + _record_type = self.delete_control_space(field_list[0]) + _owner = field_list[1].strip() + _owner = self.delete_control(_owner) + _version_date = self.delete_control_space(field_list[2]) + _generator = field_list[3].strip() + _generator = self.delete_control(_generator) + _header_title = field_list[4].strip() + _header_title = self.delete_control(_header_title) + _character_set = self.delete_control_space(field_list[5]) + _comment = field_list[6].strip("\t \n\r") + _data_type = self.delete_control_space(field_list[7]) + _number_certificate = self.delete_control_space(field_list[8]) + __date_certificate = self.delete_control_space(field_list[9]) + # _____Owner_____ + self.__budget.setOwner(_owner) + # _____Version-Date_____ + _version_date = _version_date.split("\\") + _file_format = _version_date[0] + if _file_format in self.__format_list: + self.__file_format = _file_format + print _("FIEBDC format: %s" % _file_format) + if len(_version_date) > 1: + _date = _version_date[1] + if _date != "": + _parsed_date = self.parseDate(_date) + if _parsed_date is not None: + self.__budget.setDate(_parsed_date) + # _____Generator_____ + # ignored field + print _("FIEBDC file generated by %s" % _generator) + # _____Header_Title_____ + _header_title = _header_title.split("\\") + _header_title = [_title.strip() for _title in _header_title] + _header = _header_title.pop(0) + _title = [ ] + for _title_index in _header_title: + if _title_index != "": + _title.append(_title_index) + if _header != "": + self.__budget.setTitleList([ _header, _title ]) + # _____Characters_set_____ + # field parsed in readFile method + # _____Comment_____ + if _comment != "": + self.__budget.setComment(_comment) + # _____Data type_____ + # 1 -> Base data. + # 2 -> Budget. + # 3 -> Budget certificate. + # 4 -> Base date update. + try: + _data_type = int(_data_type) + except ValueError: + _data_type = "" + if _data_type == 3: + # _____Number budget certificate_____ + try: + _number_certificate = int(_number_certificate) + except ValueError: + _number_certificate = "" + # _____Date budget certificate_____ + if _date_certificate != "": + _parsed_date_certificate = self.parseDate(_date_certificate) + if _parsed_date_certificate is None: + _date_certificate = "" + else: + _date_certificate = _parsed_date_certificate + self.__budget.setBudgetype(_data_type) + self.__budget.setCertificateOrder(_number_certificate) + self.__budget.setCertificateDate(_parsed_date_cerfificate) + elif _data_type != "": + self.__budget.setBudgeType(_data_type) + self.num_valid_record = self.num_valid_record + 1 + + def _parseK(self, field_list): + """_parseK(self, field_list) + + field_list: field list of the record + 0- K: Coefficients + 1- { DN \ DD \ DS \ DR \ DI \ DP \ DC \ DM \ DIVISA \ } + 2- CI \ GG \ BI \ BAJA \ IVA + 3- + A){ DRC \ DC \ DRO \ DFS \ DRS \ DFO \ DUO \ DI \ DES \ DN \ + DD \ DS \ DIVISA \ } + B){ DRC \ DC \ \ DFS \ DRS \ \ DUO \ DI \ DES \ DN \ + DD \ DS \ DSP\ DEC\ DIVISA \ } + 4- [ n ] + """ + # _____Number of fields_____ + # Any INFORMATION after last field separator is ignored + # The record must have 3 fields + # The last field is ignored, pyArq hate dll's + if len(field_list) > 4: + field_list = field_list[1:4] + # The record must have 3 fields + else: + field_list = field_list[1:] + [""]*(4-len(field_list)) + # control character are erased: end of line, tab, space + # _____Fields_____ + _field0 = self.delete_control_space(field_list[0]) + _field1 = self.delete_control_space(field_list[1]) + _field2 = self.delete_control_space(field_list[2]) + # _____Field 1_____ + if len(_field1) > 0 and _field1[-1] == "\\": + _field1 = _field1[:-1] + # if there are a \ character at the end it must be erased + _percentages = _field1.split("\\") + if len(_percentages) > 5: + _percentages = _percentages[:5] + # If there are no sufficient subfields, the subfields are added + # with empty value:"" + else: + _percentages = _percentages + [""]*(5-len(_percentages)) + _percentage_titles = [ "CI", "GG", "BI", "BAJA", "IVA" ] + _percentage_dict = {} + for _percentage_index in range(len(_percentages)): + try: + _percentage = int(_percentages[_percentage_index]) + except ValueError: + _percentage = "" + _percentage_dict[_percentage_titles[_percentage_index]] = \ + _percentage + self.__budget.setPercentages(_percentage_dict) + # _____Field 0 and 1_____ + # Default number of decimal places + # Number of titles in ~V record + _title_num = len(self.__budget.getTitleList()[1]) + if _title_num == 0: _title_num = 1 + # If the field 2 is empty, the field 0 is readed + if _field2 == "": + # _____Field 0_____ + if _field0[-1] == "\\": + _field0 = _field0[:-1] + # if there are a \ character at the end it must be erased + _decimal_list = _field0.split("\\") + _decimal_index = 0 + if len(_decimal_list)%9 != 0: + # if it is not multiple of 9, empty subfield are added + _decimal_list = _decimal_list + [""]*(9 - \ + len(_decimal_list)%9) + # The number of decimal values is the same as the numbers of + # titles in the V record + if len(_decimal_list)//9 > _title_num: + _decimal_list = _decimal_list[:_title_num*9] + elif len(_decimal_list)//9 < _title_num: + _decimal_list = _decimal_list + _decimal_list[-9:] * \ + (_title_num-(len(_decimal_list)//9)) + while _decimal_index <= len(_decimal_list)-9: + _decimals = _decimal_list[_decimal_index:(_decimal_index + 9)] + _forlist = range(len(_decimals)-1) + for _index in range(len(_decimals)): + try: + #TODO: test this + _decimals[_index] = int(_decimals[_index]) + except ValueError: + _decimals[_index] = "" + _DN = _decimals[0] + _DD = _decimals[1] + _DS = _decimals[2] + _DR = _decimals[3] + _DI = _decimals[4] + _DP = _decimals[5] + _DC = _decimals[6] + _DM = _decimals[7] + _DIVISA = _decimals[8] + _percentage_dict = {"DN" : _DN, + "DD" : _DD, + "DSP" : _DS, + "DS" : _DS, + "DFC" : _DR, + "DFPU" : _DR, + "DFUO" : _DR, + "DFA" : _DR, + "DRC" : _DR, + "DRPU" : _DR, + "DRUO" : _DR, + "DRA" : _DR, + "DP" : _DC, + "DC" : _DC, + "DPU" : _DC, + "DUO" : _DC, + "DEA" : _DC, + "DES" : _DC, + "DIR" : _DI, + "DIRC" : _DI, + "DCD" : _DP, + "DIVISA": _DIVISA } + _decimal_index = _decimal_index + 9 + self.__budget.setDecimals(_percentage_dict, + (_decimal_index//9)) + else: + # _____Field 3_____ + if _field2[-1] == "\\": + _field2 = _field2[:-1] + # if there are a \ character at the end it must be erased + _decimal_list = _field2.split("\\") + # test if the Divisa subfield is 12 or 14 position + # Divisa is the only Alphanumeric subfield + # "no_float": "[^0-9.]" + if len(_decimal_list) >= 13 and \ + self.__pattern["no_float"].search(_decimal_list[12]): + _multiple = 13 + elif len(_decimal_list) >= 15 and \ + self.__pattern["no_float"].search(_decimal_list[14]): + _multiple = 15 + else: + if self.__file_format == "FIEBDC-3/2002": + _multiple = 13 + elif self.__file_format == "FIEBDC-3/2004": + _multiple = 13 + elif self.__file_format == "FIEBDC-3/2007": + _multiple = 15 + else: + _multiple = 15 + _decimal_index = 0 + if len(_decimal_list)%_multiple != 0 : + # if it is not multiple of _multiple, empty subfield are added + _decimal_list = _decimal_list + \ + [""]*(_multiple-len(_decimal_list)%_multiple) + # The number of decimal values is the same as the numbers of + # titles in the V record + if len(_decimal_list)//_multiple > _title_num: + _decimal_list = _decimal_list[:_title_num*_multiple] + elif len(_decimal_list)//_multiple < _title_num: + _decimal_list = _decimal_list + [_decimal_list[-_multiple:]]*\ + (_title_num-(len(_decimal_list)//_multiple)) + while _decimal_index <= len(_decimal_list)-_multiple: + _decimals = _decimal_list[_decimal_index:(_decimal_index +\ + _multiple)] + for _index in range(len(_decimals)-1): + try: + _decimals[_index] = int(_decimals[_index]) + except: + _decimals[_index] = "" + if _multiple == 13: + _DRC = _decimals[0] + _DC = _decimals[1] + _DRO = _decimals[2] + _DFS = _decimals[3] + _DRS = _decimals[4] + _DFO = _decimals[5] + _DUO = _decimals[6] + _DI = _decimals[7] + _DES = _decimals[8] + _DN = _decimals[9] + _DD = _decimals[10] + _DS = _decimals[11] + _DIVISA = _decimals[12] + _percentage_dict = { + "DN" : _DN, + "DD" : _DD, + "DSP" : _DS, + "DS" : _DS, + "DFC" : _DFS, + "DFPU" : _DRC, + "DFUO" : _DFS, + "DFA" : _DFS, + "DRC" : _DRS, + "DRPU" : _DRC, + "DRUO" : _DRS, + "DRA" : _DRS, + "DP" : _DC, + "DC" : _DC, + "DPU" : _DC, + "DUO" : _DUO, + "DEA" : _DES, + "DES" : _DES, + "DIR" : _DI, + "DIRC" : _DC, + "DCD" : _DI, + "DIVISA": _DIVISA, + } + else: # _multiple == 15: + _DRC = _decimals[0] + _DC = _decimals[1] + _DRO = _decimals[2] + _DFS = _decimals[3] + _DRS = _decimals[4] + _DFO = _decimals[5] + _DUO = _decimals[6] + _DI = _decimals[7] + _DES = _decimals[8] + _DN = _decimals[9] + _DD = _decimals[10] + _DS = _decimals[11] + _DSP = _decimals[12] + _DEC = _decimals[13] + _DIVISA = _decimals[14] + _percentage_dict = { + "DN" : _DN, + "DD" : _DD, + "DSP" : _DSP, + "DS" : _DS, + "DFC" : _DFS, + "DFPU" : _DRC, + "DFUO" : _DFS, + "DFA" : _DFS, + "DRC" : _DRS, + "DRPU" : _DRC, + "DRUO" : _DRS, + "DRA" : _DRS, + "DP" : _DC, + "DC" : _DC, + "DPU" : _DC, + "DUO" : _DUO, + "DEA" : _DEC, + "DES" : _DES, + "DIR" : _DI, + "DIRC" : _DC, + "DCD" : _DI, + "DIVISA": _DIVISA} + _decimal_index = _decimal_index + 13 + self.__budget.setDecimals(_percentage_dict, + (_decimal_index//13)) + self.num_valid_record = self.num_valid_record +1 + + def _parseC(self, field_list): + """_parseC(self, field_list) + + field_list: field list of the record + 0- C: Record + 1- Code{\Code} + 2- [Unit] + 3- [Summary] + 4- {Price\} + 5- {Date\} + 6- [Type] + """ + # _____number of fields_____ + # Any INFORMATION after last field separator is ignored + if len(field_list) > 7: + field_list = field_list[:7] + # If there are no sufficient fields, the fields are added + # with empty value:"" + else: + field_list = field_list + [""]*(7-len(field_list)) + # control character are erased: en of line, tab, space + # _____Fields_____ + _record_type = field_list[0] + _codes = self.delete_control_space(field_list[1]) + _unit = self.delete_control_space(field_list[2]) + _summary = self.delete_control(field_list[3]) + _prices = self.delete_control_space(field_list[4]) + _dates = self.delete_control_space(field_list[5]) + _type = self.delete_control_space(field_list[6]) + # _____Code_____ + _codes = _codes.split("\\") + if len(_codes) > 0: + # parse the hierarchy of the first code + # hierarchy: 0->root, 1->Chapter/subchapter, 2->other + if len(_codes[0]) > 2 and _codes[0][-2:] == "##": + _hierarchy = 0 + elif len(_codes[0]) > 1 and _codes[0][-1:] == "#": + _hierarchy = 1 + else: + _hierarchy = 2 + # "#" and "##" characters at the end of the code are erased + # invalid characters are also erased + # maximun len 20 characters + _codes = [self.validateCode(_code) for _code in _codes] + # empty codes are ignored + while "" in _codes: + _codes.remove("") + if len(_codes) > 0: + #TODO: test this + _code = _codes[0] + _synonyms = _codes + else: + print _("Record C without a valid code") + return + # _____Unit_____ + # nothing to do + # _____Summary_____ + # nothing to do + # _____Price_____ and _____Dates_____ + # last \ is erased + if len(_dates) > 0 and _dates[-1] == "\\": + _dates = _dates[:-1] + if len(_prices) > 0 and _prices[-1] == "\\": + _prices = _prices[:-1] + _dates = _dates.split("\\") + _prices = _prices.split("\\") + # number of prices = number of titles in "V" line + # if there are no sufficient prices it takes the last price defined + _title_num = len(self.__budget.getTitleList()[1]) + if _title_num == 0: _title_num = 1 + if len(_prices) > _title_num: _prices = _prices[:_title_num] + elif len(_prices) < _title_num: + _prices = _prices + [_prices[-1]]*(_title_num-len(_prices)) + # number of dates = number of prices + # if there are no sufficient dates it takes the last date defined + if len(_dates) > len(_prices): _dates = _dates[:len(_prices)] + elif len(_dates) < len(_prices): + _dates = _dates + [_dates[-1]]*(len(_prices)-len(_dates)) + for _index in range(len(_prices)): + # TODO: lack to specify the number of decimals of the price + try: + _prices[_index] = float(_prices[_index]) + except: + _prices[_index] = 0.0 + _parsed_date = self.parseDate(_dates[_index]) + if _parsed_date is None: + _dates[_index] = "" + else: + _dates[_index] = _parsed_date + # _____Type_____ + # 0 Without classifying + # EA Auxiliary element + # EU Unitary element + # EC Complex element + # EF Functional element + # OB Construction site + # PA Cost overrun + # PU Unitary budget + # 1 Labourforce + # H Labourforce + # 2 Machinery and auxiliary equipment + # Q Machinery + # % Auxiliary equipment + # 3 Building materials + # MC Cement + # MCr Ceramic + # MM Wood + # MS Iron and steel + # ME Energy + # MCu Copper + # MAl Aluminium + # ML Bonding agents + # M Others materials + # Hierarchy type subtype + # 0->root -> 0 -> None,OB + # 1->[sub]chapter -> 0 -> None,PU + # 2->Other -> 0 -> None,EA,EU,EC,EF,PA + # 1 -> None,H + # 2 -> None,Q,% + # 3 -> None,MC,MCr,MM,MS,ME,MCu,Mal,ML,M + if _hierarchy == 0: + if _type == "OB": + _subtype = _type + _type = 0 + elif _type == "0" or _type == "": + _subtype = "" + _type = 0 + else: + print utils.mapping(_("Incorrect type ($1) in the code $2"), + (str(_type), _code)) + _type = 0 + _subtype = "" + elif _hierarchy == 1: + if _type == "PU": + _subtype = _type + _type = 0 + elif _type == "0" or _type == "": + _subtype = "" + _type = 0 + else: + print utils.mapping(_("Incorrect type ($1) in the code $2"), + (str(_type), _code)) + _type = 0 + _subtype = "" + else: + if _type == "EA" or _type == "EU" or _type == "EC" or \ + _type == "EF" or _type == "PA": + _subtype = _type + _type = 0 + elif _type == "H": + _subtype = _type + _type = 1 + elif _type == "Q" or _type == "%": + _subtype = _type + _type = 2 + elif _type == "MC" or _type == "MCr" or _type == "MM" or \ + _type == "MS" or _type == "ME" or _type == "MCu" or \ + _type == "Mal" or _type == "ML" or _type == "M": + _subtype = _type + _type = 3 + elif _type == "0" or _type == "1" or _type == "2" or \ + _type == "3": + _subtype = "" + _type = int(_type) + elif _type == "": + _subtype = "" + _type = 0 + else: + print utils.mapping(_("Incorrect type ($1) in the code $2"), + (str(_type), _code)) + _type = 0 + _subtype = "" + self.__budget.setRecord(_code, _synonyms, _hierarchy, + _unit, _summary, _prices, _dates, _type, _subtype) + self.num_valid_record = self.num_valid_record + 1 + + def _parseDY(self, field_list): + """_parseDY(self, field_list) + + field_list: field list of the record + 0- D or Y: DECOMPOSITION or ADD DECOMPOSITION + 1- Parent Code + 2- + """ + # _____number of fields_____ + # Any INFORMATION after last field separator is ignored + # The record must have 3 fields + if len(field_list) > 3: + field_list = field_list[:3] + # If there are no sufficient fields, the fields are added + # with empty value:"" + else: + field_list = field_list + [""]*(3-len(field_list)) + # control character are erased: end of line, tab, space + # _____Fields_____ + _record_type = field_list[0] + _code = self.delete_control_space(field_list[1]) + _children = self.delete_control_space(field_list[2]) + # _____Code_____ + # "#" and "##" characters at the end of the code are erased + # invalid characters are also erased + _code = self.validateCode(_code) + # _____children_____ + # TODO: test the number of decimals in factor an yield values + _children = _children.split( "\\" ) + _children_list = [ ] + _child_index = 0 + while _child_index < len(_children)-3: + # _____subfields_____ + _child_code = _children[_child_index] + _factor = _children[_child_index+1] + _yield = _children[_child_index+2] + # _____child_code_____ + _child_code = self.validateCode(_child_code) + # _____factor_____ + if _factor != "": + try: + _factor = float(_factor) + except ValueError: + print utils.mapping(_("ValueError loadig the "\ + "descomposition of the record $1, the factor "\ + "of the child $2 must be a float number and "\ + "can not be $3, seted default value 1.0"), + (_code, _child_code, _factor)) + _factor = 1.0 + #____yield___ + if _yield != "": + try: + _yield = float(_yield) + except ValueError: + print utils.mapping(_("ValueError loading the "\ + "descomposition of the record $1, the yield of "\ + "the child $2, must be a float number and can"\ + "not be $3, seted default value 1.0"), + (_code, _child_code, _factor)) + _yield = 1.0 + if _child_code != "" and _code != "": + _children_list.append([_child_code, _factor, _yield ]) + if _record_type == "D": + _position = _child_index / 3 + else: #_record_type == "Y" + _position = -1 + self.__budget.setTree(_code, _child_code, _position, _factor, + _yield, "", "", "", "") + _child_index = _child_index + 3 + self.num_valid_record = self.num_valid_record +1 + + def _parseT(self, field_list): + """_parseT(self, field_list) + + field_list: field list of the record + 0- T: Text + 1- Record code + 2- Description text + """ + # _____Number of fields_____ + # Any INFORMATION after last field separator is ignored + # The record must have 3 fields + if len(field_list) > 3: + field_list = field_list[0:3] + field_list = field_list[1:] + if len(field_list) != 2: + return + # control character are erased: end of line, tab, space + # _____Fields_____ + _code = self.delete_control_space(field_list[0]) + _text = field_list[1] + # _____Code_____ + # "#" and "##" characters at the end of the code are erased + # invalid characters are also erased + _code = self.validateCode(_code) + # _____Text_____ + self.__budget.setText(_code, _text) + self.num_valid_record = self.num_valid_record + 1 + + def _parseMN(self, field_list): + """_parseMN(self, field_list) + + field_list: field list of the record + 0- M or N: MEASURE or ADD MEASURE + 1- [Parent Code\]Child Code + 2- {Path\} + 3- TOTAL MEASURE + 4- {Type\Comment\Unit\Length\Width\Height\} + 5- [Label] + """ + + # _____Number of fields_____ + # Any INFORMATION after last field separator is ignored + # The record must have 6 fields + if len(field_list) > 6: + field_list = field_list[:6] + # If there are no sufficient fields, the fields are added + # with empty value:"" + else: + field_list = field_list + [""]*(6-len(field_list)) + # control character are erased: end of line, tab, space + # _____Fields_____ + _record_type = field_list[0] + _codes = self.delete_control_space(field_list[1]) + _path = self.delete_control_space(field_list[2]) + _total = self.delete_control_space(field_list[3]) + _lines = self.delete_control(field_list[4]) + _label = self.delete_control_space(field_list[5]) + # _____Codes_____ + _code_list = _codes.split( "\\" ) + # "#" and "##" characters at the end of the code are erased + # invalid characters are also erased + if len(_code_list) == 2: + _parent_code = self.validateCode(_code_list[0]) + if _parent_code == "": + _parent_code = None + _child_code = self.validateCode(_code_list[1]) + elif len(_code_list) == 1: + _child_code = self.validateCode(_code_list[0]) + _parent_code = None + else: + print utils.mapping(_("Invalid codes in $1 record, codes $2"), + (_record_type, _codes)) + return + if _child_code == "": + print utils.mapping(_("Empty child code in $1 record, codes: "\ + "$2"), (_record_type, _codes)) + return + # _____Path_____ + # TODO: path=0, no-estructured measures + _path_list = _path.split( "\\" ) + if len(_path_list) > 0: + while _path_list[-1] == "": + _path_list = _path_list[:-1] + _path = _path_list[-1] + try: + _path = int(_path) + except ValueError: + print utils.mapping(_("Invalid path in $1 record, "\ + "codes $2"), (_record_type, _codes)) + return + if _path > 0: + _path -= 1 + else: + _path = 0 + # _____Total_____ + try: + _total = float(_total) + except ValueError: + print utils.mapping(_("Invalid Total Measure value in $1 "\ + "record, codes $2"), (_record_type, _codes)) + return + # _____Measure lines_____ + _lines = _lines.split( "\\" ) + _line_index = 0 + _line_list = [ ] + while _line_index < len(_lines)-6: + _linetype = _lines[_line_index] + if _linetype == "": + _linetype = 0 + elif _linetype == "1" or _linetype == "2" or \ + _linetype == "3": + _linetype = int(_linetype) + else: + _linetype = 0 + _comment= _lines[_line_index + 1] + if _linetype == 3: + # "formula": ".*[^0123456789\.()\+\-\*/\^abcdp ].*" + if self.__pattern["formula"].match(_comment): + print utils.mapping(_("The comment is not a formula or "\ + "its have invalid characters, in the $1 record, "\ + "codes $2"), (_record_type, _codes)) + return + else: + _formula = _comment + _comment = "" + else: + _formula = "" + _units = _lines[_line_index + 2] + _length = _lines[_line_index + 3] + _width = _lines[_line_index + 4] + _height = _lines[_line_index + 5] + try: + if _units != "": _units = float(_units) + if _length != "": _length = float(_length) + if _width != "": _width = float(_width) + if _height != "": _height = float(_height) + except ValueError: + print utils.mapping("The measure values are not float "\ + "numbers, code $1", (_codes,)) + return + _line_list.append([_linetype, _comment, _units, + _length, _width, _height, _formula]) + _line_index = _line_index + 6 + self.__budget.setTree(_parent_code, _child_code, _path, "", "", + _total, _line_list, _label, _record_type) + self.num_valid_record = self.num_valid_record + 1 + + def _parseW(self, field_list): + """_parseW(self, field_list) + + field_list: field list of the record + 0- W: Geografical field + 1- Field Code + 2- Field + """ + # _____Number of fields_____ + # Any INFORMATION after last field separator is ignored + # The record must have 2 fields + if len(field_list) >= 2: + field_list = field_list[1:2] + else: + return + # control character are erased: end of line, tab, space + # _____Fields_____ + _code_fields = field_list[0] + # last \ is erased + if len(_code_fields) and _code_fields[-1] == "\\": + _code_fields = _code_fields[:-1] + _code_fields = _code_fields.split("\\") + _field_dict = {} + _field_index = 0 + while _field_index < len(_code_fields)-1: + # _____subfields_____ + _field_code = _code_fields[_field_index] + _field_title = _code_fields[_field_index+1] + # control character are erased: end of line, tab, space + # _____section_code_____ + #"control": "[\t \n\r]" + _field_code = self.delete_control_space(_field_code) + # _____section_title_____ + if _field_code != "": + _field_dict[_field_code] = _field_title + _field_index = _field_index + 2 + self.__budget.setSheetFields(_field_dict) + self.num_valid_record = self.num_valid_record +1 + + def _parseL(self, field_list): + """_parseL(self, field_list) + + field_list: field list of the record + 0- L: Sheet of Conditions 1 + A: + 1- Empty + 2- {Section Code\Section Title} + B: + 1- Record Code + 2- {Section Code\Section Text} + 3- {Section Code\RTF file} + 4- {Section Code\HTM file} + """ + # _____Number of fields_____ + # The record must have at least 3 fields + if len(field_list) < 3: + return + _code = field_list[1] + if _code == "": + # A: Section Titles + # Any INFORMATION after last field separator is ignored + # The record must have 3 fields + if len(field_list) > 3: + field_list = field_list[0:3] + field_list = field_list[1:3] + # _____Fields_____ + _section_codes = field_list[1] + # last \ is erased + if len(_section_codes) and _section_codes[-1] == "\\": + _section_codes = _section_codes[:-1] + _section_codes = _section_codes.split("\\") + _section_dict = {} + _section_index = 0 + while _section_index < len(_section_codes)-1: + # _____subfields_____ + _section_code = _section_codes[_section_index] + + _section_title = _section_codes[_section_index+1] + # control character are erased: end of line, tab, space + # _____section_code_____ + _section_code = self.delete_control_space(_section_code) + # _____section_title_____ + _section_title = self.delete_control_space(_section_title) + if _section_code != "": + _section_dict[_section_code] = _section_title + _section_index = _section_index + 2 + self.__budget.setSheetSections(_section_dict) + self.num_valid_record = self.num_valid_record +1 + + else: + # Any INFORMATION after last field separator is ignored + # The record must have 5 fields + if len(field_list) > 5: + field_list = field_list[0:5] + field_list = field_list[1:] + # _____Fields_____ + # _____Record Code_____ + _record_code = self.delete_control_space(field_list[0]) + # "#" and "##" characters at the end of the code are erased + # invalid characters are also erased + _record_code = self.validateCode(_record_code) + _scodes_text = field_list[1] + if _scodes_text == "": + # TODO: rtf and html files + print "Html and rtf files not implemented in ~L record" + else: + # _____Section-code_Section-text_____ + # last \ is erased + if len(_scodes_text) and _scodes_text[-1] == "\\": + _scodes_text = _scodes_text[:-1] + _scodes_text = _scodes_text.split("\\") + _paragraph_dict = {} + _section_dict = {} + _section_index = 0 + while _section_index < len(_scodes_text)-1: + # _____subfields_____ + _section_code = _scodes_text[_section_index] + _section_text = _scodes_text[_section_index+1] + # control character are erased: end of line, tab, space + # _____section_code_____ + _section_code = self.delete_control_space(_section_code) + # _____section_text_____ + if _section_code != "" and _section_text != "": + #-# paragraph #-# + _paragraph_code = _record_code + _section_code + "*" + _paragraph_dict[ _paragraph_code ] = _section_text + _section_dict[_section_code] = _paragraph_code + _section_index = _section_index + 2 + self.__budget.setSheetParagraphs(_paragraph_dict) + self.__budget.setSheetRecord(_record_code, "*", _section_dict) + self.num_valid_record = self.num_valid_record +1 + + def _parseQ(self, field_list): + """_parseQ(self, field_list) + + field_list: field list of the record + 0- Q: Sheet of Conditions 2 + 1- Record Code + 2- {Section Code\Paragraph key\{Field key;}\}| + """ + # _____Number of fields_____ + # The record must have at least 3 fields + if len(field_list) < 3: + return + _code = field_list[1] + # Any INFORMATION after last field separator is ignored + # The record must have 3 fields + if len(field_list) > 3: + field_list = field_list[0:3] + field_list = field_list[1:] + # _____Fields_____ + # _____Record Code_____ + _record_code = self.delete_control_space(field_list[0]) + # "#" and "##" characters at the end of the code are erased + # invalid characters are also erased + _record_code = self.validateCode(_record_code) + _scodes_pkey = field_list[1] + # last \ is erased + if len(_scodes_pkey) and _scodes_pkey[-1] == "\\": + _scodes_pkey = _scodes_pkey[:-1] + _scodes_pkey = _scodes_pkey.split("\\") + _field_dict = {} + _section_index = 0 + while _section_index < len(_scodes_pkey) -1: + # _____subfields_____ + _section_code = _scodes_pkey[_section_index] + _paragraph_key = _scodes_text[_section_index+1] + _field_keys = _scodes_text[_section_index+2] + # control character are erased: end of line, tab, space + # _____section_code_____ + _section_code = self.delete_control_space(_section_code) + # _____section_text_____ + _paragraph_key = self.delete_control_space(_paragraph_key) + # _____Fields keys_____ + _field_keys = self.delete_control_space(_field_keys) + # last ; is erased + if len(_field_keys) and _field_keys[-1] == ";": + _field_keys = _field_keys[:-1] + _field_keys_list = _scodes_pkey.split(";") + for _field_key in _field_keys_list: + if _field_key != "" and _section_code != "" and \ + _paragraph_key != "": + if _field_key in _field_dict: + _section_dict = _field_dict[_field_key] + else: + _section_dict = {} + _field_dict[_field_key] = _section_dict + _section_dict[_section_code] = _paragraph_code + _section_index = _section_index + 3 + for _field, _section_dict in _field_dict.iteritems(): + self.__budget.setSheetRecord(_record_code, _field, _section_dict) + self.num_valid_record = self.num_valid_record +1 + + def _parseJ(self, field_list): + """_parseJ(self, field_list) + + field_list: field list of the record + 0- J: Sheet of Conditions 3 + 1- Paragraph code + 2- [Paragraph text] + 3- [RTF file] + 4- [HTML file] + """ + # _____Number of fields_____ + # The record must have at least 3 fields + if len(field_list) < 3: + return + # Any INFORMATION after last field separator is ignored + # The record must have 5 fields + if len(field_list) > 5: + field_list = field_list[0:5] + field_list = field_list[1:] + # _____Fields_____ + # _____Paragraph code_____ + _paragraph_code = self.delete_control_space(field_list[0]) + # _____Paragraph text_____ + _paragraph_text = field_list[1] + if _paragraph_text == "": + # TODO: rtf and html files + print "Html and rtf files not implemented in ~J record" + else: + self.__budget.setSheetParagraph(paragraph_code, paragraph_text) + self.num_valid_record = self.num_valid_record +1 + + def _parseG(self, field_list): + """_parseG(self, field_list) + + field_list: field list of the record + 0- G: Grafic info + 1- record code + 2- + """ + # _____Number of fields_____ + # The record must have at least 3 fields + if len(field_list) < 3: + return + # Any INFORMATION after last field separator is ignored + # The record must have 3 fields + if len(field_list) > 3: + field_list = field_list[0:3] + field_list = field_list[1:] + # _____Fields_____ + # _____Record Code_____ + _record_code = self.delete_control_space(field_list[0]) + # "#" and "##" characters at the end of the code are erased + # invalid characters are also erased + _record_code = self.validateCode(_record_code) + # _____Grafic files_____ + _grafic_files = self.delete_control(field_list[1]) + # _____subfields_____ + # last \ is erased + if len(_grafic_files) and _grafic_files[-1] == "\\": + _grafic_files = _grafic_files[:-1] + _grafic_file_list = _grafic_files.split("\\") + _tested_grafic_file_list = [] + for _grafic_file in _grafic_file_list: + _path = os.path.dirname(self.__filename) + _grafic_file_path = os.path.join(_path, _grafic_file) + if os.path.exists(_grafic_file_path): + _tested_grafic_file_list.append(_grafic_file_path) + else: + _name_ext = os.path.splitext(_grafic_file) + _grafic_file_name = _name_ext[0] + _grafic_file_ext = _name_ext[1] + _grafic_file_name_u = _grafic_file_name.upper() + _grafic_file_name_l = _grafic_file_name.lower() + _grafic_file_ext_u = _grafic_file_ext.upper() + _grafic_file_ext_l = _grafic_file_ext.lower() + _uu = _grafic_file_name_u + _grafic_file_ext_u + _ul = _grafic_file_name_u + _grafic_file_ext_l + _lu = _grafic_file_name_l + _grafic_file_ext_u + _ll = _grafic_file_name_l + _grafic_file_ext_l + _grafic_file_path_uu = os.path.join(_path, _uu) + _grafic_file_path_ul = os.path.join(_path, _ul) + _grafic_file_path_lu = os.path.join(_path, _lu) + _grafic_file_path_ll = os.path.join(_path, _ll) + if os.path.exists(_grafic_file_path_uu): + _tested_grafic_file_list.append(_grafic_file_path_uu) + elif os.path.exists(_grafic_file_path_ul): + _tested_grafic_file_list.append(_grafic_file_path_ul) + elif os.path.exists(_grafic_file_path_lu): + _tested_grafic_file_list.append(_grafic_file_path_lu) + elif os.path.exists(_grafic_file_path_ll): + _tested_grafic_file_list.append(_grafic_file_path_ll) + else: + print utils.mapping(_("The file $1 do not exist"), + (_grafic_file_path,)) + if len(_grafic_file_list) > 0: + for _grafic_file in _tested_grafic_file_list: + self.__budget.addFile(_record_code, _grafic_file, "img", "") + self.num_valid_record = self.num_valid_record +1 + + def _parseE(self, field_list): + """_parseE(self, field_list) + + field_list: field list of the record + 0- E: Company + 1- company Code + 2 [ summary ] + 3- [ name ] + 4- { [ type ] \ [ subname ] \ [ address ] \ [ postal_code ] + \ [ town ] \ [ province ] \ [ country ] \ { phone; } + \ { fax; } \ {contact_person; } \ } + 5- [ cif ] \ [ web ] \ [ email ] \ + """ + + # _____Number of fields_____ + # Any INFORMATION after last field separator is ignored + # The record must have 6 fields + if len(field_list) > 6: + field_list = field_list[1:6] + # If there are no sufficient fields, the fields are added + # with empty value:"" + else: + field_list = field_list[1:] + [""]*(6-len(field_list)) + # _____Fields_____ + # _____company Code_____ + _company_code = self.delete_control_space(field_list[0]) + if _company_code == "": + return + # _____Summary_____ + + _sumamary = self.delete_control(field_list[1]) + # _____Name_____ + _name = self.delete_control(field_list[2]) + # _____local_offices_____ + _local_offices = self.delete_control(field_list[3]) + # _____subfields of local_offices_____ + # last \ is erased + if len(_local_offices) and _local_offices[-1] == "\\": + _local_offices = _local_offices[:-1] + _local_offices_list = _local_offices.split("\\") + # If there are no sufficent subfields, the subfields are added + # whith empty value + _nsub = len(_local_offices_list) % 10 + if _nsub != 0: + _local_offices_list = _local_offices_list + \ + [""]*(10-len(field_list)) + _local_offices = [] + _local_offices_index = 0 + while _local_offices_index < len(_local_offices_list)-9: + # _____subfields_____ + _type = _local_offices_list[_local_offices_index] + _subname = _local_offices_list[_local_offices_index+1] + _address = _local_offices_list[_local_offices_index+2] + _postal_code = _local_offices_list[_local_offices_index+3] + _town = _local_offices_list[_local_offices_index+4] + _province = _local_offices_list[_local_offices_index+5] + _country = _local_offices_list[_local_offices_index+6] + _phone = _local_offices_list[_local_offices_index+7] + # last ; is erased + if len(_phone) and _phone[-1] == ";": + _phone = _phone[:-1] + _phone_list = _phone.split(";") + _fax = _local_offices_list[_local_offices_index+8] + # last ; is erased + if len(_fax) and _fax[-1] == ";": + _fax = _fax[:-1] + _fax_list = _fax.split(";") + _contact_person = _local_offices_list[_local_offices_index+9] + if _type != "" or _subname != "" or _address != "" or \ + _postal_code != "" or _town != "" or _province != "" or \ + _country != "" or _phone != "" or _fax != "" or \ + _contact_person != "": + _local_offices.append([_type, _subname, _address, + _postal_code, _town, _province, + _country, _phone_list, _fax_list, + _contact_person]) + _local_offices_index = _local_offices_index + 10 + # _____cif web email_____ + _c_w_e = self.delete_control_space(field_list[4]) + # last \ is erased + if len(_c_w_e) and _c_w_e[-1] == "\\": + _c_w_e = _c_w_e[:-1] + _c_w_e_list = _c_w_e.split("\\") + # _____subfields_____ + # If there are no sufficient fields, the fields are added + # with empty value:"" + _c_w_e_list = _c_w_e_list + [""]*(3-len(_c_w_e_list)) + _cif = _c_w_e_list[0] + _web = _c_w_e_list[1] + _email = _c_w_e_list[2] + self.__budget.setCompany(_company_code, _sumamary, _name, + _local_offices, _cif, _web, _email) + self.num_valid_record = self.num_valid_record +1 + + def _parseX(self, field_list): + """_parseX(self, field_list) + + field_list: field list of the record + A) + 0- X: Tecnical information + 1- Empty + 2- < TI_Code \ TI_Descitption \ TI_Unit > + B) + 0- X: Tecnical information + 1- Record_code + 2- < TI_Code \ TI_value > + """ + # Tecnical information + # The record must have at least 3 fields + if len(field_list) < 3: + return + # Any INFORMATION after last field separator is ignored + # The record must have 3 fields + if len(field_list) > 3: + field_list = field_list[0:3] + field_list = field_list[1:] + # _____Fields_____ + # "control": "[\t \n\r]" + _field_1 = self.delete_control_space(field_list[0]) + _field_2 = self.delete_control_space(field_list[1]) + if _field_1 == "": + # A) + _field_2_list = _field_2.split("\\") + _ti_index = 0 + while _ti_index < len(_field_2_list)-3: + _ti_code = _field_2_list[_ti_index] + _ti_description = _field_2_list[_ti_index+1] + _ti_unit = _field_2_list[_ti_index+2] + if _ti_code != "": + self.__budget.addTecInfo(_ti_code, _ti_description, + _ti_unit) + _ti_index = _ti_index + 3 + else: + # B) + # "#" and "##" characters at the end of the code are erased + # invalid characters are also erased + _record_code = self.validateCode(_field_1) + _field_2_list = _field_2.split("\\") + _ti_index = 0 + _ti_dict = {} + while _ti_index < len(_field_2_list)-2: + _ti_code = _field_2_list[_ti_index] + _ti_value = _field_2_list[_ti_index+1] + if _ti_code != "" and _ty_value != "": + _ti_dict[_ti_code] = _ty_value + _ti_index = _ti_index + 2 + self.__budget.setTecnicalInformation(_record_code, _ti_dict) + self.num_valid_record = self.num_valid_record +1 + + def _parseF(self, field_list): + """_parseF(self, field_list) + + field_list: field list of the record + 0- F: Files + 1- Record code + 2- { Type \ { Filenames; } \ [Description] } + """ + + # _____Number of fields_____ + # The record must have at least 3 fields + if len(field_list) < 3: + return + # Any INFORMATION after last field separator is ignored + # The record must have 3 fields + if len(field_list) > 3: + field_list = field_list[0:3] + field_list = field_list[1:] + # _____Fields_____ + # _____Record Code_____ + _record_code = self.delete_control_space(field_list[0]) + # "#" and "##" characters at the end of the code are erased + # invalid characters are also erased + _record_code = self.validateCode(_record_code) + # _____Grafic files_____ + _files = self.delete_control(field_list[1]) + # _____subfields_____ + # last \ is erased + if len(_files) and _files[-1] == "\\": + _files = _files[:-1] + _files_list = _files.split("\\") + # adding empty subfiels if necesary + if len(_files_list)%3 > 0: + _files_list.extend[""]*(3 - len(_files_list)%3) + _file_index = 0 + _tested_files_list = [] + while _file_index < len(_files_list)-3: + _type = _files_list[_file_index].replace(" ","") +## _types = { +## "0": _("others"), +## "1": _("características técnicas y de fabricación"), +## "2": _("manual de colocación, uso y mantenimiento"), +## "3": _("certificado/s de elementos y sistemas"), +## "4": _("normativa y bibliografía"), +## "5": _("tarifa de precios"), +## "6": _("condiciones de venta"), +## "7": _("carta de colores"), +## "8": _("ámbito de aplicación y criterios selección"), +## "9": _("cálculo de elementos y sistemas"), +## "10": _("presentación, datos generales, objetivos, etc. de "\ +## "empresa"), +## "11": _("certificado/s de empresa"), +## "12": _("obras realizadas")} + _types = ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", + "11", "12"] + if not _type in _types: + _type = "0" + _filenames = _files_list[_file_index + 1] + _description = _files_list[_file_index + 2] + _file_index += 3 + if len(_filenames) and _filenames[-1] == ";": + _files = _files[:-1] + _filenames_list = _files.split(";") + _path = os.path.dirname(self.__filename) + for _filename in filenames_list: + _file_path = os.path.join(_path, _filename) + if os.path.exists(_file_path): + _tested_files_list.append([_file_path, _type, + _description]) + else: + _name_ext = os.path.splitext(_filename) + _file_name = _name_ext[0] + _file_ext = _name_ext[1] + _file_name_u = _file_name.upper() + _file_name_l = _file_name.lower() + _file_ext_u = _file_ext.upper() + _file_ext_l = _file_ext.lower() + _uu = _file_name_u + _file_ext_u + _ul = _file_name_u + _file_ext_l + _lu = _file_name_l + _file_ext_u + _ll = _file_name_l + _file_ext_l + _file_path_uu = os.path.join(_path, _uu) + _file_path_ul = os.path.join(_path, _ul) + _file_path_lu = os.path.join(_path, _lu) + _file_path_ll = os.path.join(_path, _ll) + if os.path.exists(_file_path_uu): + _tested_files_list.append([_file_path_uu, _type, + _description]) + elif os.path.exists(_grafic_file_path_ul): + _tested_files_list.append([_file_path_ul, _type, + _description]) + elif os.path.exists(_grafic_file_path_lu): + _tested_files_list.append([_file_path_lu, _type, + _description]) + elif os.path.exists(_grafic_file_path_ll): + _tested_files_list.append([_file_path_ll, _type, + _description]) + else: + print utils.mapping(_("The file $1 do not exist"), + (_file_path,)) + if len(_tested_files_list) > 0: + for _file in _tested_file_list: + self.__budget.addFile(_record_code, _file[0], file[1], file[2]) + self.num_valid_record = self.num_valid_record +1 + + def _parseB(self, field_list): + """_parseB(self, field_list) + + field_list: field list of the record + 0- B: Change code + 1- Record Code + 2- New code + """ + # _____Number of fields_____ + # Any INFORMATION after last field separator is ignored + # The record must have 3 fields + if len(field_list) > 3: + field_list = field_list[0:3] + field_list = field_list[1:] + if len(field_list) != 2: + return + # control character are erased: end of line, tab, space + # _____Fields_____ + _code = self.delete_control_space(field_list[0]) + _new_code = self.delete_control_space(field_list[1]) + # _____Codes_____ + # "#" and "##" characters at the end of the code are erased + # invalid characters are also erased + _code = self.validateCode(_code) + _new_code = self.validateCode(_new_code) + # change code + self.__budget.changeCode(_code, _new_code) + self.num_valid_record = self.num_valid_record + 1 + + def _parseA(self, field_list): + """_parseA(self, field_list) + + field_list: field list of the record + 0- A: Labels + 1- Record Code + 2- + """ + # _____Number of fields_____ + # Any INFORMATION after last field separator is ignored + # The record must have 3 fields + if len(field_list) > 3: + field_list = field_list[0:3] + field_list = field_list[1:] + if len(field_list) != 2: + return + # control character are erased: end of line, tab, space + # _____Fields_____ + # "control": "[\t \n\r]" + _code = self.delete_control_space(field_list[0]) + _labels = self.delete_control_space(field_list[1]) + # _____Codes_____ + # "#" and "##" characters at the end of the code are erased + # invalid characters are also erased + _code = self.validateCode(_code) + # _____Labels_____ + # last \ is erased + # TODO: change the others parsers to this: + while len(_labels) > 0 and _labels[-1] == "\\": + _labels = _labels[:-1] + # replace "_" to " " + _labels = _labels.replace("_"," ") + _label_list = _labels.split("\\") + for _label in _label_list: + self.__budget.addLabel(_code, _label) + self.num_valid_record = self.num_valid_record + 1 + + def _parseP(self, field_list): + """_parseP(self, field_list) + + field_list: Parametric record + A) Global paremetric record + 0- P: Parametric + 1- Empty + 2- [Parametric description] + 3- [library.DLL] + B) Family Parametric record + 0- P: Parametric + 1- Family Code + 2- [Parametric description] + """ + # TODO: Use global parametric record + if len(field_list) > 2: + # delete control caracters and spaces + _family_code = self.delete_control_space(field_list[1]) + if _family_code == "": # A)Global paremetric record + # The record must have 3 or 4 fields + if len(field_list) > 4: + field_list = field_list[0:4] + field_list = field_list[1:] + if len(field_list) == 2: + field_list.append("") + if len(field_list) != 3: + return + else: # B)Family Parametric record + # The record must have 3 fields + if len(field_list) > 3: + field_list = field_list[0:3] + field_list = field_list[1:] + if len(field_list) != 2: + print _("PyArq hates parametric DLLs") + return + else: + return + # _____Description_____ + _description = field_list[1] + if _description == "": + print _("PyArq hates parametric DLLs") + return + # Adding last end of line + _description = _description + "\r\n" + # Delete comments + # "comment" : "#.*\r\n" + _description = self.__pattern["comment"].sub("\r\n",_description) + # Tabs to spaces + _description = _description.replace("\t"," ") + # Delete empty lines + # "empty_line": r"(\r\n) *\r\n" + while self.__pattern["empty_line"].search(_description): + _description = self.__pattern["empty_line"].sub( + lambda x: x.groups()[0], _description) + # Delete spaces before and after / + # "space_before_backslash" : r"( )+\\" + _description = self.__pattern["space_before_backslash"].sub( + r"\\",_description) + # "space_after_backslash" : r"\\( )+" + _description = self.__pattern["space_after_backslash"].sub( + r"\\",_description) + # Join lines that start but not end with / + _description = "\r\n" + _description # add leading end of line + # "start_noend_backslash": "(\r\n\\\.*[^\\\])\r\n" + while self.__pattern["start_noend_backslash"].search(_description): + _description = self.__pattern["start_noend_backslash"].sub( + lambda x: x.groups()[0], _description) + # Join lines that end with a + - * / ^ and @ & < > <= >= = <> ! + # "end_oper" : "(\+|-|\*|/|/^|@|&|<|>|<=|>=|=|!) *\r\n" + _description = self.__pattern["end_oper"].sub( + lambda x: x.groups()[0], _description) + # Join lines for matricial vars + # matricial_var : "(\r\n *[%|\$][A-ZÑ].*=.*,) *\r\n" + while self.__pattern["matricial_var"].search(_description): + _description = self.__pattern["matricial_var"].sub( + lambda x: x.groups()[0], _description) + _description = _description[2:] # remove leading end of line + #_description = re.sub(r"\\( )+",r"\\",_description) + _lines = _description.split("\r\n") + _final_description = "" + _pass_line = 0 + for index in range(len(_lines)): + _line = _lines[index] + # Parse lines + if len(_line) != 0: # Delete empty lines + if _pass_line > 0: + _pass_line = _pass_line -1 + _line = "" + elif _line.isspace(): + _line = "" + elif _line[0] != "\\": + # Delete spaces out "" delimiter + _list = _line.split('"') + _final_line = "" + for index1 in range(len(_list)): + if index1 % 2 != 0: + _parcial_line = '"' + _list[index1] + else: + _parcial_line = '"' + _list[index1].replace(" ","") + _final_line = _final_line + _parcial_line + _line = _final_line[1:] + _lines[index] = _line + # parse data + if len(_line) > 2 and _line[:2] == "::": + # Delete spaces out " delimiter + #print "__PRECIO__" + _line[2:] + pass + elif len(_line) > 2 and _line[:2] == "%:": + # Delete spaces out " delimiter + #print "__%AUX__" + _line[2:] + pass + elif len(_line) > 3 and _line[:2] == "%%:": + # Delete spaces out " delimiter + #print "__%%AUX__" + _line[2:] + pass + elif self.__pattern["var"].search(_line): + # Delete spaces out " delimiter + #print "line =", _line + while _line.count('"') % 2 == 1 and \ + index + _pass_line + 1 < len(_lines) -1: + _line = _line + _lines[index + _pass_line + 1] + _pass_line = _pass_line + 1 + _search = self.__pattern["var"].search(_line) + if _search is not None: + _var = _search.groups()[0] + " = " + _search.groups()[1] + #print "__VAR__" + str(_var) + pass + else: + #print "no __VAR__", _line + pass + elif self.__pattern["descomposition"].search(_line): + # Delete spaces out " delimiter + #_patern = "(^[^:]*):(.*)$" + _search = self.__pattern["descomposition"].search(_line) + if _search is not None: + _var = _search.groups()[0] + ":" + _search.groups()[1] + #print "__Descomposición__" + str(_var) + pass + else: + #print "no __Descomposición__", _line + pass + else: + print "Parametric: code: " + _family_code + print "******* Desconocido *** : " + _line + if index-10 > 0: print "-11 :", _lines[index-11] + if index-10 > 0: print "-10 :", _lines[index-10] + if index-9 > 0: print "-9 :", _lines[index-9] + if index-8 > 0: print "-8 :", _lines[index-8] + if index-7 > 0: print "-7 :", _lines[index-7] + if index-6 > 0: print "-6 :", _lines[index-6] + if index-5 > 0: print "-5 :", _lines[index-5] + if index-4 > 0: print "-4 :", _lines[index-4] + if index-3 > 0: print "-3 :", _lines[index-3] + if index-2 > 0: print "-2 :", _lines[index-2] + if index-1 > 0: print "-1 :", _lines[index-1] + print "-0 :", _lines[index-0] + pass + else: + _parameter_list = _line.split("\\")[1:-1] + if len(_parameter_list) >= 2: + if _parameter_list[0] == "C" or \ + _parameter_list[0] == "COMENTARIO": + #print "__COMENTARIO__" + _parameter_list[1] + self.__budget.setParametricSelectComment( + _family_code, _parameter_list[1]) + elif _parameter_list[0] == "R" or \ + _parameter_list[0] == "RESUMEN": + #print "__RESUMEN__" + _parameter_list[1] + self.__budget.setParametricSummary(_family_code, + _parameter_list[1]) + elif _parameter_list[0] == "T" or \ + _parameter_list[0] == "TEXTO": + #print "__TEXTO__" + _parameter_list[1] + self.__budget.setParametricText(_family_code, + _parameter_list[1]) + elif _parameter_list[0] == "P" or \ + _parameter_list[0] == "PLIEGO": + #print "__PLIEGO__" + str(_parameter_list[1:]) + pass + elif _parameter_list[0] == "K" or \ + _parameter_list[0] == "CLAVES": + #print "__CLAVES__" + str(_parameter_list[1:]) + pass + elif _parameter_list[0] == "F" or \ + _parameter_list[0] == "COMERCIAL": + #print "__COMERCIAL__" + str(_parameter_list[1:]) + pass + else: + #print "==PARAMETRO==" + str(_parameter_list[:]) + pass + _final_description = _final_description + _line + "\r\n" + + #print _line + # Delete last empty line + _description = _final_description[:-2] + _lines = _description.split("\r\n") + for _line in _lines: + pass + #print _line + self.num_valid_record = self.num_valid_record + 1 + + def readFile(self, budget=None, filename=None, interface=None): + """readFile(self, budget=None, filename=None) + + filename: the filename of the fiebdc file + budget: base.obra object + interface: a object to send messages + must have printf(message) progress(percent) + recordStatistics(...) + Return the budget objetc or None if the file can be readed + """ + if filename != None and budget != None: + self.__filename = filename + self.__budget = budget + self.__budget.filename = self.__filename + if self.__filename is None or self.__budget is None or \ + self.__cancel == True: + return None + if not os.path.exists(self.__filename): + return None + _time = time.time() + try: + _file = open(self.__filename, 'r') + except IOError: + print utils.mapping("IOError: $1", (self.__filename,)) + return None + self.__budget.filename = self.__filename + self._record_number = 0 + self.num_valid_record = 0 + self._record_V_number = 0 + self._record_C_number = 0 + self._record_D_number = 0 + self._record_Y_number = 0 + self._record_M_number = 0 + self._record_N_number = 0 + self._record_T_number = 0 + self._record_K_number = 0 + self._record_W_number = 0 + self._record_L_number = 0 + self._record_Q_number = 0 + self._record_J_number = 0 + self._record_G_number = 0 + self._record_E_number = 0 + self._record_O_number = 0 + self._record_P_number = 0 + self._record_X_number = 0 + self._record_B_number = 0 + self._record_F_number = 0 + self._record_A_number = 0 + self._record_Unknow_number = 0 + print utils.mapping(_("Loading file $1"), (self.__filename,)) + _filesize = float(os.path.getsize(self.__filename)) + interface.progress(_file.tell() / _filesize) + _buffer = _file.read(1000) + # set codepage from V record + _record_list = _buffer.split("~") + registro_V = _record_list[1] + # ~V|[PROPIEDAD_ARCHIVO]|VERSION_FORMATO[\DDMMAAAA]|[PROGRAMA_EMISION]| + # [CABECERA]\{ ROTULO_IDENTIFICACION \}|[JUEGO_CARACTERES]| + # [COMENTARIO]|[TIPO INFORMACIÓN]|[NÚMERO CERTIFICACIÓN]| + # [FECHA CERTIFICACIÓN ] | + registro_V = registro_V.split("|") + if registro_V[0] == "V": + #_codepage = registro_V[5] + if len(registro_V) > 5: + _version = registro_V[5].strip() + # remove leading spaces + if _version in self.__character_sets_dict: + self.__character_set = self.__character_sets_dict[_version] + else: + print utils.mapping(_("This codepage do not exist in "\ + "FIEBDC3! Default codepage: $1"), + (self.__character_set,)) + else: + print utils.mapping(_("This V record dot have a codepage! "\ + "Default codepage: $1"), + (self.__character_set,)) + else: + print utils.mapping(_("Not 'V' record in File! Default codepage: "\ + "$1"), (self.__character_set,)) + if self.__character_set != "utf8": + _buffer = unicode(_buffer, self.__character_set) + _buffer = _buffer.encode("utf8") + # Any INFORMATION between the beginning of the file and the + # beginning of the first registry “~” is ignored + #"after_first_tilde" : "^[^~]*~" + _buffer = self.__pattern["after_first_tilde"].sub("",_buffer) + while _buffer != "" and self.__cancel != True: + #-# the blank characters (32), tabs (9) and end of line (13 and 10) + # before the separators '~', '|' are erased. + # Before separator \ not deleted because it affects the reading of + # the record ~P + _buffer = self.eraseControlCharacters(_buffer) + _record_list = _buffer.split("~") + # The last record can be incomplete unless it is the last one of + # the file + if len(_record_list) > 1: + # not the end + _last_record = _record_list.pop() + else: + # the end record + # The blank characters (32), tabs (9) and end of line + # (13 and 10) at the end of the file are ignored. + #"end_control" : "((\r\n)| |\t)+$" + _record_list[-1] = self.__pattern["end_control"].sub("", + _record_list[-1]) + _last_record = "" + for record in _record_list: + if self.__cancel == True: + break + self.parseRecord(record) + interface.progress(_file.tell() / _filesize) + _buffer2 = _file.read(100000) + if self.__character_set != "utf8": + _buffer2 = unicode(_buffer2, self.__character_set) + _buffer2 = _buffer2.encode("utf8") + _buffer = _last_record + _buffer2 + _file.close() + if self.__cancel == True: + print _("Cancelled process") + return None + else: + print utils.mapping(_("Time to load: $1 seconds"), + (("%.2f" %(time.time()-_time)),)) + print utils.mapping(_("Records/Valid Records: $1/$2"), + (self._record_number, self.num_valid_record)) + if self._record_O_number > 0: + print utils.mapping(_("$1 unsuported record type O: "\ + "Comercial Relationship"), (self._record_O_number,)) + if self.num_valid_record == 0: + print _("This file is not a valid FIBDC3 file") + return None + _str = "" + for type in \ + [("V", self._record_V_number), + ("C", self._record_C_number), + ("D", self._record_D_number), + ("Y", self._record_Y_number), + ("M", self._record_M_number), + ("N", self._record_N_number), + ("T", self._record_T_number), + ("K", self._record_K_number), + ("W", self._record_W_number), + ("L", self._record_L_number), + ("Q", self._record_Q_number), + ("J", self._record_J_number), + ("G", self._record_G_number), + ("E", self._record_E_number), + ("O", self._record_O_number), + ("P", self._record_P_number), + ("X", self._record_X_number), + ("B", self._record_B_number), + ("F", self._record_F_number), + ("A", self._record_A_number), + ("?", self._record_Unknow_number)]: + _str = _str + "%s: %s\n" %(type[0], type[1]) + print _str + self._testBudget(self.__budget) + return self.__budget + + def _testBudget(self, budget): + """testBudget(self,budget) + + budget: base.obra object + Test and repair budget object after read it from bc3 file + """ + # TODO: more to do here + print _("Testing budget ...") + # Add price to records without price + _iter = budget.iter() + _titlelist = budget.getTitleList()[1] + if len(_titlelist) == 0: + _titlenum = 1 + else: + _titlenum = len(_titlelist) + for _code in _iter: + _record = budget.getRecord(_code) + _prices = _record.getPrices() + _len_prices = len(_prices) + if _titlenum > _len_prices: + _leftprices = _titlenum - _len_prices + for _index in range(0,_leftprices): + _root = budget.getRecord(budget.getRoot()) + _price = [0.0, _root.getDate(_len_prices + _index)] + budget.addPriceToRecord(_price,_record) + print _("End Test") + + def delete_control_space(self, text): + text = self.delete_control(text) + text = text.replace(" ", "") + return text + + def delete_control(self, text): + text = text.replace("\t", "") + text = text.replace("\r", "") + text = text.replace("\n", "") + return text diff -r a1703c4f2990 -r 2ac1551ad2ab Generic/globals.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Generic/globals.py Sun Oct 31 20:07:33 2010 +0100 @@ -0,0 +1,94 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +## File globals.py +## This file is part of pyArq-Presupuestos. +## +## Copyright (C) 2010 Miguel Ángel Bárcena Rodríguez +## +## +## pyArq-Presupuestos is free software: you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## pyArq-Presupuestos is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program. If not, see . + +# module for global variables +import os +# path: Paths where find the program files needed + +version = "pyArq-Presupuestos v0.0.0" +path = { + "HOME" : "", + "APPDATA" : "", + "ICON" : "/images/pyArq-Presupuestos.svg", + "CHAPTER-ICON" : "/images/chapter.svg", + "UNIT-ICON" : "/images/unit.svg", + "MATERIAL-ICON" : "/images/material.svg", + "MACHINERY-ICON" : "/images/machinery.svg", + "LABOURFORCE-ICON": "/images/labourforce.svg", + "MENU-ICON": "/images/menu.svg", + "CONNECTED-ICON": "/images/connected.svg", + "DISCONNECTED-ICON": "/images/disconnected.svg", + "CLOSE-ICON": "/images/close.svg", + "DESCRIPTION-ICON": "/images/description.svg", + "SHEET-ICON": "/images/sheet.svg", + "DECOMPOSITION-ICON" : "/images/decomposition.svg", + "MEASURE-ICON" : "/images/measure.svg", + "ACUMULATEDLINE-ICON" : "/images/acumulatedline.svg", + "PARCIALLINE-ICON" : "/images/parcialline.svg", + "NORMALLINE-ICON" : "/images/normalline.svg", + "CALCULATEDLINE-ICON" : "/images/calculatedline.svg", + "ARROW-ICON": "/images/arrow.svg", + "IMAGE-ICON": "/images/image.svg", + "DXF-ICON": "/images/dxf.svg", + "DURUS-DATABASE": "/pyArq-Presupuestos/durus/", + "BUDGET": "/pyArq-Presupuestos/budget/", + "THROBBER-ICON": "/images/throbber.png", + "THROBBER-GIF": "/images/throbber.gif", + "BUDGET-ICON": "/images/budget.svg", + "PYARQ-ICON": "/images/pyArq.png", + } + +color = { + "ACTIVE" : "#CDD7FF", # blue + "INDEX-EVEN" : "#C4C4C4", # dark grey + "INDEX-UNEVEN" : "#DDDDDD", # grey + "EVEN" : "#E6E6E6", # dark white + "UNEVEN": "#FFFFFF", # white + "CHAPTER-EVEN": "#D8E6E6", # dark cian + "CHAPTER-UNEVEN": "#F0FFFF", # cian + "TEXT": "#000000", # black + "CALCULATED-TEXT": "#FF00FF", # + "SUBTOTAL": "#FAC8C8", + "SUBTOTAL-PARCIAL": "#ADD8E6", + } +desktop = { + "autodetect" : True, + "desktop" : "", + "browser" : "firefox", + "mailapp" : "evolution", + "imageapp" : "gthumb", + "cadapp" : "qcad", + } + +def getAppPath(key): + return path["APPDATA"] + path[key] +def getHomePath(key): + return path["HOME"] + path[key] +if os.name == 'posix': + path["HOME"] = os.environ.get('HOME') +elif sys.platform == 'win32': + path["HOME"] = os.environ.get('HOMEPATH') + # TODO: Mac Os, + # TODO: Test in diferents os + +#-# +path["BUDGET"] = "/pyArq-Presupuestos/" +#-# diff -r a1703c4f2990 -r 2ac1551ad2ab Generic/openwith.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Generic/openwith.py Sun Oct 31 20:07:33 2010 +0100 @@ -0,0 +1,137 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +## File openwith.py +## This file is part of pyArq-Presupuestos. +## +## Copyright (C) 2010 Miguel Ángel Bárcena Rodríguez +## +## +## This file is based in gtkgui_helpers.py and common/helpers.py from gajim +## +## Copyright (C) 2003-2008 Yann Leboulanger +## Copyright (C) 2005-2006 Dimitur Kirov +## Nikos Kouremenos +## Copyright (C) 2006 Alex Mauer +## Copyright (C) 2006-2007 Travis Shirk +## Copyright (C) 2006-2008 Jean-Marie Traissard +## Copyright (C) 2007 Lukas Petrovicky +## James Newton +## Julien Pivotto +## Copyright (C) 2007-2008 Stephan Erb +## Copyright (C) 2008 Brendan Taylor +## Jonathan Schleifer +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published +## by the Free Software Foundation; version 3 only. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with Gajim. If not, see . +## +# Modules +import subprocess +import os + +# pyArq-Presupuestos modules +import globals + +# from gtkgui_helpers.py +def autodetect_desktop(): + # recognize the environment and sets it in globals + if os.name == 'nt': + globals.desktop["desktop"] = "windows" + else: + _processes = get_running_processes() + if 'gnome-session' in _processes: + globals.desktop["desktop"] = "gnome" + elif 'startkde' in _processes: + globals.desktop["desktop"] = "kde" + elif 'startxfce4' in _processes or 'xfce4-session' in _processes: + globals.desktop["desktop"] = "xfce" + else: + globals.desktop["desktop"] = "" + +def get_running_processes(): + '''returns running processes or None (if not /proc exists)''' + if os.path.isdir('/proc'): + # under Linux: checking if 'gnome-session' or + # 'startkde' programs were run before gajim, by + # checking /proc (if it exists) + # + # if something is unclear, read `man proc`; + # if /proc exists, directories that have only numbers + # in their names contain data about processes. + # /proc/[xxx]/exe is a symlink to executable started + # as process number [xxx]. + # filter out everything that we are not interested in: + files = os.listdir('/proc') + + # files that doesn't have only digits in names... + files = filter(str.isdigit, files) + + # files that aren't directories... + files = [f for f in files if os.path.isdir('/proc/' + f)] + + # processes owned by somebody not running gajim... + # (we check if we have access to that file) + files = [f for f in files if os.access('/proc/' + f +'/exe', os.F_OK)] + + # be sure that /proc/[number]/exe is really a symlink + # to avoid TBs in incorrectly configured systems + files = [f for f in files if os.path.islink('/proc/' + f + '/exe')] + + # list of processes + processes = [os.path.basename(os.readlink('/proc/' + f +'/exe')) for f in files] + + return processes + return [] + +# from common/helpers.py + +def exec_command(command): + subprocess.Popen('%s &' % command, shell=True).wait() + +def build_command(executable, parameter): + # we add to the parameter (can hold path with spaces) + # "" so we have good parsing from shell + parameter = parameter.replace('"', '\\"') # but first escape " + command = '%s "%s"' % (executable, parameter) + return command + +def launch_file(kind, uri): + # kind = "url" ,"mail", "image", "dxf" + _desktop = globals.desktop["desktop"] + if _desktop == "windows": + try: + os.startfile(uri) # if pywin32 is installed we open + except Exception: + pass + else: + if kind == 'mail' and not uri.startswith('mailto:'): + uri = 'mailto:' + uri + if _desktop == "gnome": + command = 'gnome-open' + elif _desktop == "kde": + command = 'kfmclient exec' + elif _desktop == "xfce": + command = 'exo-open' + else: + if kind == 'url': + command = globals.desktop["browser"] + elif kind == 'mail': + command = globals.desktop["mailapp"] + elif kind == 'image': + command = globals.desktop["imageapp"] + elif kind == 'dxf': + command = globals.desktop["cadapp"] + else: # if no app is configured + return + command = build_command(command, uri) + try: + exec_command(command) + except Exception: + pass diff -r a1703c4f2990 -r 2ac1551ad2ab Generic/utils.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Generic/utils.py Sun Oct 31 20:07:33 2010 +0100 @@ -0,0 +1,165 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +## File utils.py +## This file is part of pyArq-Presupuestos. +## +## Copyright (C) 2010 Miguel Ángel Bárcena Rodríguez +## +## +## pyArq-Presupuestos is free software: you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## pyArq-Presupuestos is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program. If not, see . + +# Modules +import re +import imghdr + +# add wmf to imghdr +def test_wmf(h, f): + """wmf image library""" + if h[:6] == "\xd7\xcd\xc6\x9a\x00\x00": + return 'wmf' +imghdr.tests.append(test_wmf) + +# add dxf to imghdr +def test_dxf(h, f): + """AutoCAD DXF: Drawing Interchange Format""" + if isinstance(f,file): + _pos = f.tell() + f.seek(0) + _h = f.read(128) + f.seek(-32, 2) + _l = f.read(32) + f.seek(_pos) + else: + _h = h + _l = h[-32:] + _h = _h.replace("\r","") + _l = _l.replace("\r","") + if (" 0\nSECTION\n 2\nHEADER\n" in _h or\ + " 0\nSECTION\n 2\nCLASSES\n" in _h or\ + " 0\nSECTION\n 2\nTABLES\n" in _h or\ + " 0\nSECTION\n 2\nBLOCKS\n" in _h or\ + " 0\nSECTION\n 2\nENTITIES\n" in _h or\ + " 0\nSECTION\n 2\nOBJECTS\n" in _h or\ + " 0\nSECTION\n 2\nTHUMBNAILIMAGE\n" in _h) and \ + _l[-19:] == " 0\nENDSEC\n 0\nEOF\n": + return 'dxf' +imghdr.tests.append(test_dxf) + + +def mapping(string, tuple): + """mapping(string, tuple) + + string: a message string + tuple: a truple with string items + Return the string replacing the $[n] words whith its corresponding value + from the tuple. + It is used because the gettext module can not #-#supotr#-# strings as: + "Invalid type (%s) in record: %s" %(type, record) + """ + for _index in range(len(tuple)): + string = string.replace("$" + str(_index+1), str(tuple[_index])) + return string + +def eliminate_duplicates(list): + """eliminate_duplicates(list) + + Return a copy of the list without duplicate values + """ + _result = [ ] + for item in list: + if item not in _result: + _result.append(item) + return _result + +def is_valid_code(code): + """is_valid_code(code) + + code: a string code + Funtion to test if a record code is valid + A valid code must fulfill: + - Be a not empty string + - The valid characters are the defined in MSdos 6.0 including .$#%&_ + What it means? I am not sure, so I test if all the character + are in cp850 + - Cannot contain the following characters + <~> separator of records if FIEBDC-3 + <|> separator of fields if FIEBDC-3 + <\> separator of subfield in FIEBDC-3 + <\t> tab -> control character + < > space -> control character + <\n> end of line -> control character + <\r> end of line -> control character + - Cannot end with <#> or <##>, root and chapter code record + It return a tuple (is_valid, code) + is_valid (True/False) + True: the code is valid + False: the code is not valid + code(False/code) + False: the code is not valid and can not be corrected + code: the code or the corrected code + """ + _is_valid = True + if not isinstance(code, str): + return False, False + if code == "": + return False, False + try: + _unicode_code = unicode(code, "utf8") + _code_cp850 = _unicode_code.encode("cp850") + _unicode_code = unicode(_code_cp850, "cp850") + _code_utf8 = _unicode_code.encode("utf8") + except UnicodeError: + return False, False + if _code_utf8 != code: + _is_valid = False + if _code_utf8 == "": + return False, False + code = _code_utf8 + _code2 = re.sub("[\t \n\r~|\\\]","",code) + if _code2 != code: + if _code2 == "": + return False, False + _is_valid = False + code = _code2 + if code[-1] == "#": + _is_valid = False + while code[-1] == "#": + code = code[:-1] + if code == "": + return False, False + return _is_valid, code + +def getFiletype(filename, h=None): + """getFiletype(filename, h=None): + + filename: the filename to test + h: raw string, if h is not None the filename is ignored and h is assumed + to contain the byte stream to test + """ + _type = imghdr.what(filename, h) + _image_types = ["rgb", "gif", "pbm", "pgm", "ppm", "tiff", "rast", "xbm", + "jpeg", "bmp", "png", "wmf"] + if _type in _image_types: + return "image" + elif _type == "dxf": + return "dxf" +## _video_types = ["avi", "mpg", "mkv", "ogm"] +## elif _type in _video_types: +## return "video" +## elif _type == "pdf": +## return "pdf" +## elif _type == "ppt" or _type == "odp": +## return "presentation" + else: + return None diff -r a1703c4f2990 -r 2ac1551ad2ab Gtk/__init__.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Gtk/__init__.py Sun Oct 31 20:07:33 2010 +0100 @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +## +## This file is part of pyArq-Presupuestos. +## +## Copyright (C) 2010 Miguel Ángel Bárcena Rodríguez +## +## +## pyArq-Presupuestos is free software: you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## pyArq-Presupuestos is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program. If not, see . +# +# define __all__ so 'from Gtk import *' works +# +__all__ = [ + 'gui', + 'importFiebdc', + ] diff -r a1703c4f2990 -r 2ac1551ad2ab Gtk/gui.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Gtk/gui.py Sun Oct 31 20:07:33 2010 +0100 @@ -0,0 +1,4509 @@ +# -*- coding: utf-8 -*- +## File gui.py +## This file is part of pyArq-Presupuestos. +## +## Copyright (C) 2010 Miguel Ángel Bárcena Rodríguez +## +## +## pyArq-Presupuestos is free software: you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## pyArq-Presupuestos is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program. If not, see . +## + +"""Gui module + +The MainWindow class contain the toplevel WINDOW, +this window have a notebook with a page for each budget. +Each budget or notebook page is showed by the Page class, this class contain +the main widget showed in a page notebook. +The main widget can show the budget information in several panes. +This panes are ordened in gtk.Paned represented for the class Paned which can +have 2 viewes represented for the View class or other gtk.Paned that have other +viewes or more gtk.Paned. +The view can have diferente type of widgets to show the budget information. +The DecompositionList class show the decompositon list information of a record +The Measure class show de measure information of a record +The TextWindow class show the long description of a record +The Sheet class class show the sheet of condition information of a record + +The views can send signal to the others. +All the viewes ordered in panes can be or not be connected to the others, +if there are connecteded to the others when the user change the active code in +one of the panes the active code change in the others. + +""" +# TODO: Config file + +# Standar Modules +import os +import time +import pygtk +pygtk.require('2.0') +import gtk +import gobject +import weakref + +# pyArq-Presupuestos Modules +from Gtk import importFiebdc +from Generic import base +from Generic import fiebdc +from Generic import durusdatabase +from Generic import utils +from Generic import globals +from Generic import openwith + +# Load default icon +if os.path.exists(globals.getAppPath("ICON")): + icon = gtk.gdk.pixbuf_new_from_file(globals.getAppPath("ICON")) + gtk.window_set_default_icon_list(icon) +else: + print utils.mapping(_("The icon file does not exist. '$1'"), + (globals.getAppPath("ICON"),)) + +# Autodetect desktop +if globals.desktop["autodetect"] is True: + openwith.autodetect_desktop() + print utils.mapping(_("pyArq-Presupuestos running on $1"), + (globals.desktop["desktop"],)) + +class MainWindow(object): + """gui.MainWindow: + + Description: + Creates and shows the main window. + This is the interface base class. + Constructor: + gui.MainWindow(): Returns the newly created main window instance + Ancestry: + +-- object + +-- MainWindow + Atributes: + "window": Main window widget ("gtk.Window" object) + "__budget_temp_list": Temporal list of budgets + "__budget_list": List of budgets ("base.Budget" objects) + "__page_list": List of pages ("Page" object) + "__notebook": Notebook widget ("gtk.Notebook" object) + "__general_action_group": the "General" action group + Methods: + __init__(self) + _main(self) + _addBudget(self, budget) + _appendPage(self) + _testBudgetList(self) + _menuitemImportFiebdc(self, widget) + _menuitemImportPriceDatabase(self, widget) + _menuitemOpenPriceDatabase(self, widget) + _menuitemOpen + _menuitemClose(self, widget) + _menuitemText(self, widget) + _delete_event(self, widget, event) + _destroy(self, widget) + """ + # TODO:* Can choose open budget in new window + # TODO:* gtk.Action for menu and toolbar + # TODO:* Can choose show more than one notebook in the same window or + # TODO: can show basedata notebook in a side pane + __ui = ''' + + + + + + + + + + + + + + + + + + + + ''' + + def __init__(self): + """def __init__(self) + + Initialize the atributes "__budget_list" and "__page_list" without data. + Creates the widgets "window" and "__notebook". + """ + self.__budget_temp_list = [] + self.__budget_list = [] + self.__page_list = [] + # Main window + self.window = gtk.Window(gtk.WINDOW_TOPLEVEL) + self.window.set_default_size(771, 570) + self.window.set_title("Presupuestos") + self.window.set_border_width(0) + self.window.connect("destroy", self._destroy) + self.window.connect("delete_event", self._delete_event) + # Vertical box + _vbox1 = gtk.VBox(False, 0) + self.window.add(_vbox1) + _vbox1.show() + #Uimanager + _uimanager = gtk.UIManager() + _accelgroup = _uimanager.get_accel_group() + self.window.add_accel_group(_accelgroup) + _general_action_group = gtk.ActionGroup("General") + self.__general_action_group = _general_action_group + _general_action_group.add_actions( + [("File", None, _("_File"), None), + ("ImportFiebdc", gtk.STOCK_OPEN, _('_Import Fiebdc'), "", 'NPI', + self._menuitemImportFiebdc), + ("Close", gtk.STOCK_CLOSE, _("_Close"), None, 'NPI', + self._menuitemClose), + ("View", None, _("_View")), + ("Text", None, _("_Text"), None, 'NPI', + self._menuitemText), + ("Test", None, _("_Test")), + ('ImportFiebdcPriceDatabase', gtk.STOCK_OPEN, + _("Import Fiebdc _price database"), "", "NPI", + self._menuitemImportPriceDatabase ), + ("OpenPriceDatabase", gtk.STOCK_OPEN, _('_Open price database'), + "", 'NPI', self._menuitemOpenPriceDatabase), + ]) + _uimanager.insert_action_group(_general_action_group, 0) + _uimanager.add_ui_from_string(self.__ui) + _menu_bar = _uimanager.get_widget("/MenuBar") + _vbox1.pack_start(_menu_bar, False, False, 0) + _toolbar = _uimanager.get_widget("/ToolBar") + _toolbar.get_settings().set_long_property("gtk-toolbar-icon-size", + gtk.ICON_SIZE_SMALL_TOOLBAR, "pyArq-Presupuestos:toolbar") + _vbox1.pack_start(_toolbar, False, False, 0) + # Notebook + self.__notebook = gtk.Notebook() + _vbox1.pack_start(self.__notebook, True, True, 0) + self.__notebook.set_tab_pos(gtk.POS_TOP) + self.__notebook.set_show_tabs(True) + self.__notebook.set_show_border(True) + self.__notebook.set_scrollable(True) + self.__notebook.show() + self._main() + + def _main(self): + """def main(self) + + Shows window and starts the GTK+ event processing loop. + """ + self.window.show() + gtk.main() + + def _addBudget(self, budget): + """def _addBudget(self, budget) + + budget: "base.Budget" object + + Appends a budget in the "__budget_list" + """ + if budget != None: + _budget = budget + if _budget in self.__budget_temp_list: + self.__budget_temp_list.remove(_budget) + self.__budget_list.append(_budget) + + def _appendPage(self): + """def _appendPage(self) + + Creates a new page (instance of "Page class") from the last budget in + __budget_list, appends this page in the "__page_list" and shows the + page widget in the notebook widget. + """ + _last_budget = self.__budget_list[-1] + _page = Page(_last_budget) + self.__notebook.append_page(_page.widget, _page.title) + self.__page_list.append(_page) + + def _testBudgetList2(self): + """def _testBudgetList2(self) + + Test if the number of budgets ("__budget_list") is greater + than the number of pages ("__page_list"), if it is greater + appendPage method is called to create a page to show the new budget + and save the budget in a durus file. + """ + if len(self.__budget_list) > len(self.__page_list): + self._appendPage() + #-# in test mode + # TODO: It must be in a thread + _last_budget = self.__budget_list[-1] + _path = globals.getHomePath("DURUS-DATABASE") + _file_whit_path_bc3 = _last_budget.filename + _filename_bc3 = _file_whit_path_bc3.split("/")[-1] + _filename = _filename_bc3.split(".")[-2] + _file = _path + _filename + ".durus" + print utils.mapping(_("Saving file: $1"), (_file,)) + _time = time.time() + _durus_file = durusdatabase.DurusFile(_file,True) + _durus_file.setBudget(_last_budget) + _durus_file.close() + print utils.mapping(_("Saving time: $1 seconds"), + (("%.2f" %(time.time()-_time) ),)) + #-# + return False + return True + + def _menuitemImportFiebdc(self, widget): + """def _menuitemImportFiebdc(self, widget) + + widget: the widget where the event is emitted from + Callback to open a budget file. + Inits a timeout to test if a budgets is appended to "__budget_list" + calling _testBudgetList method if it is true. + + Creates and shows a window to open a budget file. + """ + _budget = base.Budget() + self.__budget_temp_list.append(_budget) + _budget_file = fiebdc.Read() + _read_method = _budget_file.readFile + _filename = "file" + _exit_method = _budget_file.cancel + _file_window = importFiebdc.FileSelectionWindow(self, + _read_method, _budget, _filename, _exit_method) + + def _menuitemImportPriceDatabase(self, widget): + """def _menuitemImportPriceDatabase(self, widget) + + widget: the widget where the event is emitted from + Callback to open a budget file. + Inits a timeout to test if a budgets is appended to "__budget_list" + calling _testBudgetList method if it is true. + + Creates and shows a window to open a budget file. + """ + #TODO: change timeout, it is deprecated + #TODO: the .durus file must be direted saved + gobject.timeout_add(100, self._testBudgetList2) + _budget = base.Budget() + self.__budget_temp_list.append(_budget) + _budget_file = fiebdc.Read() + _read_method = _budget_file.readFile + _filename = "file" + _exit_method = _budget_file.cancel + _file_window = importFiebdc.FileSelectionWindow(self, + _read_method, _budget, _filename, _exit_method) + + def _menuitemOpenPriceDatabase(self, widget): + """def _menuitemImportPriceDatabase(self, widget) + + widget: the widget where the event is emitted from + Callback to open a budget file. + Inits a timeout to test if a budgets is appended to "__budget_list" + calling _testBudgetList method if it is true. + + Creates and shows a window to open a budget file. + """ + _openDialog = OpenDurusDatabase(self._OpenDurusDatabase) + _openDialog.main() + + def _OpenDurusDatabase(self, file): + """def _menuitemImportFiebdc(self, widget) + + widget: the widget where the event is emitted from + + Callback to open a budget file from a durus file. + """ + _file = file + print utils.mapping(_("Loading file: $1:"), (_file,)) + _time = time.time() + _durus_file = durusdatabase.DurusFile(_file,False) + _budget = _durus_file.getBudget() + _durus_file.close() + print utils.mapping(("Loadig time: $1 seconds"), + (("%.2f" %(time.time()-_time)),)) + self.__budget_list.append(_budget) + _page = Page(_budget) + self.__notebook.append_page(_page.widget, _page.title) + self.__page_list.append(_page) + + def _menuitemClose(self, widget): + """def _menuitemClose(self, widget) + + widget: the widget where the event is emitted from + + Callback to close a budget file. + """ + _page_num = self.__notebook.get_current_page() + if _page_num == -1: + return + _page = self.__page_list.pop(_page_num) + if isinstance(_page, Page): + #not loading budget + self.__budget_list.pop(_page_num) + _page.clear() + self.__notebook.remove_page(_page_num) + + def _menuitemText(self, widget): + """_menuitemText(self, widget) + + widget: the widget where the event is emitted from + + Creates and shows a window showing text description + using a instance of TextWindow class. + """ + _page_num = self.__notebook.get_current_page() + if _page_num == -1: + return + _budget = self.__budget_list[_page_num] + _page = self.__page_list[_page_num] + _code = _page.getActiveCode() + _concepto = _budget.getRecord(_code) + _text = _concepto.text + _window = TextWindow(_code, _text) + _window.main() + + def _delete_event(self, widget, event): + """_delete_event(self, widget, event) + + widget: the widget where the event is emitted from + event: the "gtk.gdk.Event" + + Method connected to "delete_event" signal of main window widget + This signal is emitted when a user press the close titlebar button. + It Returns True so the signal "destroy" is emitted. + """ + for _page in self.__page_list: + _page.clear() + return False # -> destroy + + def _destroy(self, widget): + """_destroy(self, widget) + + widget: the widget where the event is emitted from + Method connected to "destroy" signal of main window widget + + This signal is emited when the method connected to "delete_event" + signal returns True or when the program call the destroy() method of + the gtk.Window widget. + The window is closed and the GTK+ event processing loop is ended. + """ + gtk.main_quit() + + def getNotebook(self): + return self.__notebook + def getPageList(self): + return self.__page_list + def getBudgetList(self): + return self.__budget_list + +class EmptyPage(object): + """ + """ + def __init__(self, mainWindow, readFileMethod, budget, filename, + cancelMethod): + """def __init__(self, mainWindow, readFileMethod, budget, filename, + cancelMethod) + + """ + self.__mainWindow = mainWindow + self.__readFileMethod = readFileMethod + self.__budget = budget + self.__filename = filename + self.__cancelMethod = cancelMethod + self.__children = None + self.__cancel = [False, False] + self.__progress = 0.0 + self.__widget = gtk.VBox() + self.__main_item = None + self.__widget.show() + self.__throbber = gtk.Image() + self.__throbber.set_from_file(globals.getAppPath("THROBBER-ICON")) + self.__throbber.show() + self.__animationThobber = gtk.gdk.PixbufAnimation( + globals.getAppPath("THROBBER-GIF")) + self.__quietThobber = self.__throbber.get_pixbuf() + self.__budget_icon = gtk.gdk.pixbuf_new_from_file_at_size( + globals.getAppPath("BUDGET-ICON"), 16, 16) + _filename = os.path.basename(filename) + _rootfilename = os.path.splitext(_filename)[0] + if not _rootfilename == "": + _filename = _rootfilename + _titleLabel = gtk.Label(_filename) + _titleLabel.show() + self.__title = gtk.HBox() + self.__title.add(self.__throbber) + self.__title.add(_titleLabel) + self.__statusbar = gtk.Statusbar() + self.__statuscontext = self.__statusbar.get_context_id("Statusbar") + self.__statusbar.show() + _align = gtk.Alignment(0.5, 0.5, 0, 0) + _iconVbox = gtk.VBox() + _pyArqIcon = gtk.Image() + _pyArqIcon.set_from_file(globals.getAppPath("PYARQ-ICON")) + _pyArqIcon.show() + _iconVbox.pack_start(_pyArqIcon, True, True, 0) + _link = gtk.LinkButton("http://pyarq.obraencurso.es", + "http://pyarq.obraencurso.es") + _iconVbox.pack_start(_link, True, True, 0) + _link.show() + _iconVbox.show() + _align.add(_iconVbox) + _align.show() + self.__widget.pack_start(_align, True, True, 0) + _progressframe = gtk.Frame() + _progressframe.set_shadow_type(gtk.SHADOW_IN) + _progressframe.show() + self.__progress_bar = gtk.ProgressBar() + self.__progress_bar.show() + _progressframe.add(self.__progress_bar) + self.__statusbar.pack_start(_progressframe, False, False, 0) + self.__widget.pack_end(self.__statusbar, False, True, 0) + self.__main_item = None + + def run(self): + self.__statusbar.push(self.__statuscontext, _("Time: 0s")) + self.__throbber.set_from_animation(self.__animationThobber) + self._launchChildren() + self._launchTimeout() + + def progress(self, percent): + _progress = str(int(round(100 * percent,0))) + self.__progress = percent + + def stopLoading(self): + self.__throbber.set_from_pixbuf(self.__budget_icon) + self.__progress_bar.hide() + self.__statusbar.pop(self.__statuscontext) + + def _launchChildren(self): + """_launchChildren(self) + + Launch the thread to read the file + """ + if self.__children is None: + self.__children = importFiebdc.Thread(self, self.__mainWindow, + self.__readFileMethod, self.__budget, self.__filename, + self.__cancelMethod) + self.__children.start() + + def _launchTimeout(self): + """def _launchTimeout(self) + + Launch the timeouts: + 1- update progress bar + 2- update time label + 3- If the other timetouts are stoped the window is closed + """ + gobject.timeout_add(500, self._updateProgressBar) + gobject.timeout_add(1000, self._updateLabel, time.time()) + self.__cancel = [False, False] + gobject.timeout_add(1000, self._autoClose) + + def _updateProgressBar(self): + """def _updateProgressBar(self) + + update progress bar in a timeout + If the thread end or is canceled the timeout is stoped + """ + if self.__children is None or self.__children.isCanceled() == True: + self.__cancel[0] = True + return False + else: + self.__progress_bar.set_fraction(self.__progress) + _text = "%s%%" %str(int(round(100 * self.__progress,0))) + self.__progress_bar.set_text(_text) + return True + + def _updateLabel(self, _time): + """def _updateProgressBar(self) + + update time label in a timeout + If the thread end or is canceled the timeout is stoped + """ + if self.__children is None or self.__children.isCanceled() == True: + self.__cancel[1] = True + return False + else: + _time = time.time() - _time + _text = utils.mapping(_("Time: $1"), ("%.0f" %_time,)) + self.__statusbar.pop(self.__statuscontext) + self.__statusbar.push(self.__statuscontext, _text) + return True + + def _autoClose(self): + """def _updateProgressBar(self) + + If the time label and progress bar timeouts are stoped the window is + closed and ist tiemeout is stoped + """ + if self.__cancel == [ True, True ]: + return False + else: + return True + + def closeWindow(self): + """def closeWindow(self) + + Sets the __children atribute to None + This causes that the timeouts is ended. + This method is called from thread when it is finished + TODO: it must called threadFinished or somethig + """ + self.__children = None + self.stopLoading() + _page = Page(self.__budget) + _children = self.__widget.get_children() + for _child in _children: + self.__widget.remove(_child) + self.__widget.pack_start(_page.widget, True, True, 0) + _noteBook = self.__mainWindow.getNotebook() + _pageIndex = _noteBook.page_num(self.__widget) + self.__mainWindow.getPageList()[_pageIndex] = _page + def threadCanceled(self): + """def threadCanceled(self) + + Sets the __children atribute to None + This causes that the timeouts is ended. + This method is called from thread when is canceled + TODO: it must called threadFinished or somethig + """ + self.__children = None + self.stopLoading() + + def clear(self): + """def clear(self) + + Cancel thread + """ + self.__children.cancel() + + def getWidget(self): + """def getWidget(self) + + Return de main widget to show in the page + """ + return self.__widget + + def getTitle(self): + """def getTtle(self) + + Return the title of the page, a gtk.Label objetc + """ + return self.__title + + widget = property(getWidget, None, None, + "Main widget showed in the pane") + title = property(getTitle, None, None, + "Page Title") + +class Page(object): + """gui.Page: + + Description: + It creates and shows a page in the notebook from a budget object. + The page can show the budget information in several panes ordered + according to "panes_list" information. + Constructor: + gui.Page(budget, active_code=None): + budget: budget to be showed in this page (base.Budget object) + active_code: the code of the active record + Returns the newly created Page instance + Ancestry: + +-- object + +-- Page + Atributes: + "budget": Read-Write. Budget to show in the page. (base.obra object) + "panes_list": Read. info list for create the panes + ej: [ "v", pane1, pane2 ] , [ "h", pane1, pane2 ] + [ "v", [ "h", pane1, pane2 ], [ "h", pane1, pane2 ] ] + pane types: + * "DecompositionList": its creates a "DecompositionList" object + * "RecordDescription" : its creates a "Description" objetc + * "Measure": its creates a "Measure" objetc + * "FileView": its creates a "FileView" objet + * "CompanyView": its creates a "CompanyView" object + "widget": Read. Notebook page Widget. (a gtk.VBox instance) + "title": Read. Notebook page title (gtk.Label object) + "__active_path_record": The active path record + "__main_item": main item in the page, can be a View object or a Paned + object + Methods: + __init__(self, budget=None, active_code=None) + propagateMessageFrom(self, message, path, arg=None) + sendMessageTo(self, pane, message, path, arg=None) + clear(self) + getItem(self,path) + setMainItem(self, item) + itemsFactory(self, list_paned, path=(0,)) + setActivePathRecord(self, path_record) + getTitle(self) + getWidget(self) + setBudget(self, budget) + getBudget(self) + getPanesList(self) + """ + # TODO: * The panes can be ordered as the user wishes + # TODO: * Panes in windows + # TODO: * pane types + # TODO: * General budget properties (is better a dialog?) + + def __init__(self, budget, path_record=(0,)): + """def __init__(self, budget=None, active_code=None) + + budget: "base.Budget" object + active_code: the code of the active record + Sets the atributes + * __panes_list: info to create the panes + * budget (base.Budget object) + * active_code + """ + #TODO: __panes_list should come from config file... + self.__widget = gtk.VBox() + self.__panes_list = [ "v", "DecompositionList", [ "v", "Measure", + "RecordDescription" ]] + self.__main_item = None + self.setBudget(budget) + self.setActivePathRecord(path_record) + self.__widget.show() + + def propagateMessageFrom(self, message, path, arg=None): + """def propagateMessageFrom(self, message, path, arg=None) + + message: string message + path: tuple that represents the pane path which emits the message + arg: arguments for the message + if message is "change_active" arg is the path record + + The panes are connectted to this method to send messages to other panes + """ + _budget = self.__budget + if message == "change_active" and _budget.hasPath(arg): + self.sendMessageTo(self.__main_item, message, path, arg) + elif message == "autoclose": + self._closeItem(path) + elif message == "split h": + self._splitItem(path, "h") + elif message == "split v": + self._splitItem(path, "v") + + def sendMessageTo(self, pane, message, path, arg=None): + """def sendMessageTo(self, pane,message, path, arg=None) + pane: the receiver pane + message: string message + path: tuple that represents the pane path which emits the message + arg: arguments for the message + + Sends a message to a pane + """ + if not pane.path == path: + pane.runMessage(message, path, arg) + + def clear(self): + """def clear(self) + + Clear atributes + """ + self.propagateMessageFrom("clear", (0,)) + + del self.__budget + del self.__panes_list + del self.__widget + del self.__title + del self.__active_path_record + del self.__main_item + + def getItem(self,path): + """def getItem(self, path + + Return the item whith the path "path", it can return a Paned instance + or a View instance + """ + _item = self.__main_item + if len(path) == 1: + return _item + else: + return _item.getItem(path[1:]) + + def setMainItem(self, item): + """setMainItem(self,item) + + Sets a new main item in the page + """ + if not self.__main_item is None: + _old_main_widget = self.__main_item.widget + self.__widget.remove(_old_main_widget) + self.__main_item = item + _main_widget = self.__main_item.widget + _main_widget.show() + self.__widget.pack_start(_main_widget, True, True, 0) + + def _splitItem(self, path, orientation): + """_splitItem(self, path, orientation) + + Splits the item that is identifies by the path and the orientation + """ + _item = self.getItem(path) + _parent = self.getItem(path[:-1]) + _item.setPath(path+ (0,)) + _item_clone0 = _item.getClone(path + (0,)) + _item_clone1 = _item.getClone(path + (1,)) + _paned = Paned(orientation, path, _item_clone0, _item_clone1) + if len(path) > 1: + _parent.setItem(path[-1], [_paned]) + else: + self.setMainItem(_paned) + + def _closeItem(self, path): + """_closeItem(self, path) + + Closes the item that is identifies by the path + """ + _item = self.getItem(path) + if len(path) > 1: + # There are more than one item + _parent = self.getItem(path[:-1]) + _brothers = [ _brother for _brother in _parent] + _brothers.remove(_item) + _brother = _brothers[0] + + _parent.widget.remove(_brother.widget) + _brother.path = path[:-1] + if len(path) > 2: + _grandparent = self.getItem(path[:-2]) + _grandparent.setItem(path[-2], [_brother]) + _parent.widget.destroy() + _parent.clear() + _item.clear() + else: + _grandparent = self + _grandparent.setMainItem(_brother) + _parent.widget.destroy() + _parent.clear() + _item.clear() + else: + # Thre is only one item in the page, it can not be closed + pass + + def itemsFactory(self, list_paned, path=(0,)): + """def itemsFactory(self, list_paned, path(0,)) + + list_paned: list in "__panes_list" format + [ "v" or "h", panel1_type, panel2_type] + which contains the info for create the widgets. + panel types: + * "DecompositionList" + * "RecordDescription" + * "Measure" + * "Sheet of Conditions" + * "FileView" + * "CompanyView" + path: tuple that represents the item path in the page + + Creates the items and widgets and returns the main item + """ + if not isinstance(list_paned , list): + raise ValueError, _("The value must be a list") + if list_paned[0] == "v" or list_paned[0] == "h": + if len(list_paned) != 3: + raise ValueError, _("Incorrect len") + if not isinstance(list_paned[1],list): + list_paned[1] = [list_paned[1]] + if not isinstance(list_paned[2],list): + list_paned[2] = [list_paned[2]] + _item1 = self.itemsFactory(list_paned[1],path + (0,)) + _item2 = self.itemsFactory(list_paned[2],path + (1,)) + _item = Paned(list_paned[0], path, _item1, _item2) + elif list_paned[0] == "DecompositionList": + _item = View( "DecompositionList", self.__budget, + weakref.ref(self), path, self.__active_path_record) + elif list_paned[0] == "RecordDescription": + _item = View( "RecordDescription", self.__budget,weakref.ref(self), + path, self.__active_path_record) + elif list_paned[0] == "Measure": + _item = View( "Measure", self.__budget, weakref.ref(self), path, + self.__active_path_record) + elif list_paned[0] == "Sheet of Conditions": + _item = Sheet(sef.__budget, weakref.ref(self), path, + self.__active_path_record) + elif list_paned[0] == "FileView": + _item = FileView(sef.__budget, weakref.ref(self), path, + self.__active_path_record) + elif list_paned[0] == "CompanyView": + _item = CompanyView(sef.__budget, weakref.ref(self), path, + self.__active_path_record) + else: + _item = None + raise ValueError, utils.mapping(_("Incorrect item $1"), + (str(list_paned[0]),)) + return _item + + def setActivePathRecord(self, path_record): + """def setActivePathRecord(self, path_record) + + path_record: the active record path + + Sets the active record path + """ + if self.__budget.hasPath(path_record): + self.__active_path_record = path_record + else: + raise ValueError, utils.mapping(_("The budget does not have the "\ + "path record: $1"), (str(path_record),)) + + def getTitle(self): + """def getTtle(self) + + Return the title of the page, a gtk.Label objetc + """ + return self.__title + + def getWidget(self): + """def getWidget(self) + + Return de main widget to show in the pane + """ + return self.__widget + + def setBudget(self, budget): + """def setBudget(self, budget) + + budget: a base.Budget object + + Sets the budget and the active code atributes, + creates the page title and the widgets in the pane and + shows the main widget. + """ + if budget is None: + self.clear() + return + self.__budget = budget + self.setActivePathRecord((0,)) + ## Todo: change page title + self.__title = gtk.Label(self.__budget.getCode( + self.__active_path_record)) + _panes_list = self.__panes_list + self.__main_item = self.itemsFactory(_panes_list) + _main_widget = self.__main_item.getWidget() + _main_widget.show() + self.__widget.pack_start(_main_widget, True, True, 0) + + def getBudget(self): + """def getBudget(self) + + Return de budget, a "base.Budget" object. + """ + return self.__budget + + def getPanesList(self): + """def getPanesList(self) + + Return the panes list, info list for create the panes. + """ + return self.__panes_list + + budget = property(getBudget, setBudget, None, + "Budget to show, base.Budget object") + widget = property(getWidget, None, None, + "Main widget showed in the pane") + title = property(getTitle, None, None, + "Page Title") + panes_list = property(getPanesList, None, None, + "Info list for create the panes") + +class View(object): + """gui.View: + + Description: + It creates a view to show the budget info + Constructor: + View(view_type, budget, wr_page, path, active_path_record) + Ancestry: + +-- object + +-- Paned + Atributes: + "path": the tuple that identifies the view in the main notebook page + "widget": the main gtk widget to show in a view object, + a gtk.VBox object + "__view_type": the object type to show + * DecompositionList + * Description + * Measure + * Sheet of conditions + * FileView + * CompanyView + "__wr_page": weak reference to the page where the view must be showed + "__budget": the budget to show + "__view ": the object to show: + * DecompositionList object + * Description object + * Measure object + * Sheet object + * FileView object + * Comapany View + "__connected": boolean value, True means that the View object sends and + receives signals from/to others views + "__connected_button": a button to switch __connected True or False + Methods: + __init__(self) + getItem(self, path) + _closeItem(self, close_button) + _change_combo(self, combobox) + propagateMessgeFrom(self, message, path, arg=None) + runMessage(self, message, path, arg=None) + getWidget(self) + getPath(self) + setPath(self) + getClone(self, newpath) + clear(self) + """ + def __init__(self, view_type, budget, wr_page, path, active_path_record): + + """def __init__(self, view_type, budget, wr_page, path, + active_path_record) + view_type: the object type to show + * DecompositionList + * Description + * Measure + * Sheet + * FileView + * CompanyView + budget: the budget to show + wr_page: weak reference to the page where the view must be showed + path: the position or path of the view in the page notebook + active_path_record: the record path that must be showed + + Creates and shows a new view + """ + self.__active_path_record = active_path_record + self.__view_type = view_type + self.__wr_page = wr_page + self.__budget = budget + self.__path = path + self.__connected = True + # view_type liststore + _liststore = gtk.ListStore(str) + _liststore.append([_("Decomposition")]) #0 + _liststore.append([_("Description")]) #1 + _liststore.append([_("Measure")]) #2 + _liststore.append([_("Sheet of Conditions")]) #3 + _liststore.append([_("Files")]) #4 + _liststore.append([_("Companies")]) #5 + _combobox = gtk.ComboBox(_liststore) + _cell = gtk.CellRendererText() + _combobox.pack_start(_cell, True) + _combobox.add_attribute(_cell, 'text', 0) + _vbox = gtk.VBox() + _vbox.show() + _toolitem = gtk.ToolItem() + _toolitem.set_expand(True) + _toolitem.add(_vbox) + _toolitem.show() + self.__widget = gtk.Toolbar() + self.__widget.insert(_toolitem, 0) + _hbox = gtk.HBox() + if view_type == "DecompositionList": + self.__view = DecompositionList(budget, weakref.ref(self), + path, active_path_record) + _combobox.set_active(0) + _view_icon = gtk.Image() + _view_icon.set_from_file(globals.getAppPath("DECOMPOSITION-ICON")) + elif view_type == "RecordDescription": + self.__view = Description(budget, weakref.ref(self), + path, active_path_record) + _combobox.set_active(1) + _view_icon = gtk.Image() + _view_icon.set_from_file(globals.getAppPath("DESCRIPTION-ICON")) + elif view_type == "Measure": + self.__view = Measure(budget, weakref.ref(self), + path, active_path_record) + _combobox.set_active(2) + _view_icon = gtk.Image() + _view_icon.set_from_file(globals.getAppPath("MEASURE-ICON")) + elif view_type == "Sheet of Conditions": + self.__view = Sheet(budget, weakref.ref(self), + path, active_path_record) + _combobox.set_active(3) + _view_icon = gtk.Image() + _view_icon.set_from_file(globals.getAppPath("SHEET-ICON")) + elif view_type == "FileView": + self.__view = FileView(budget, weakref.ref(self), + path, active_path_record) + _combobox.set_active(4) + _view_icon = gtk.Image() + _view_icon.set_from_file(globals.getAppPath("SHEET-ICON")) + elif view_type == "CompanyView": + self.__view = CompanyView(budget, weakref.ref(self), path, + active_path_record) + _combobox.set_active(5) + _view_icon = gtk.Image() + _view_icon.set_from_file(globals.getAppPath("SHEET-ICON")) + + else: + raise ValueError, _(utils.mapping("Invalid type of View: $1", + view_type)) + _view_icon.show() + _combobox.connect("changed", self._change_combo) + _combobox.show() + _vbox.pack_start(_hbox,False) + _vbox.pack_start(self.__view.widget, True, True) + _hbox.pack_start(_view_icon, False, False,0) + _hbox.pack_start(_combobox, False, False,0) + _invisible = gtk.HBox() + _invisible.show() + _hbox.pack_start(_invisible, True, False,0) + _icon_menu = gtk.Image() + _icon_menu.set_from_file(globals.getAppPath("MENU-ICON")) + _icon_menu.show() + _menu_button = gtk.ToolButton() + _menu_button.set_icon_widget(_icon_menu) + _menu_button.connect("clicked", self._menu_view) + _menu_button.show() + _icon_connected = gtk.Image() + _icon_connected.set_from_file(globals.getAppPath("CONNECTED-ICON")) + _icon_connected.show() + _hbox.pack_start(_menu_button, False, False, 0) + self.__connected_button = gtk.ToolButton() + self.__connected_button.set_icon_widget(_icon_connected) + self.__connected_button.connect("clicked", self._connected) + self.__connected_button.show() + _hbox.pack_start(self.__connected_button, False, False, 0) + _icon_close = gtk.Image() + _icon_close.set_from_file(globals.getAppPath("CLOSE-ICON")) + _icon_close.show() + _close_button = gtk.ToolButton() + _close_button.set_icon_widget(_icon_close) + _close_button.connect("clicked", self._closeItem) + _close_button.show() + _hbox.pack_start(_close_button, False, False, 0) + _hbox.show() + self.__widget.show() + + def getItem(self, path): + """def getItem(self, path) + + Return itself. + """ + return self + + def _closeItem(self, close_button): + """_closeItem(self, widget) + + Method connected to the "clicked" signal of the _close_button widget + Send the "autoclose" message to the page to close this view + """ + self.propagateMessageFrom( "autoclose", self.__path) + + def _change_combo(self, combobox): + """_change_combo(self, combobox) + + Method connected to the "changed" signal of the _combobox widget + It changes the view type to the type selected in the combobox + """ + _index = combobox.get_active() + _budget = self.__view.budget + _wr_page = self.__view.page + _path = self.__view.path + _path_record = self.__view.active_path_record + _toolitem = self.__widget.get_nth_item(0) + _vbox= _toolitem.get_children()[0] + _hbox = _vbox.get_children()[0] + _combobox = _hbox.get_children()[1] + _hbox.remove(_combobox) + _invisible = _hbox.get_children()[1] + _hbox.remove(_invisible) + _menu_button = _hbox.get_children()[1] + _hbox.remove(_menu_button) + _connected_button = _hbox.get_children()[1] + _hbox.remove(_connected_button) + _close_button = _hbox.get_children()[1] + _hbox.remove(_close_button) + _vbox.remove(self.__view.widget) + _vbox.remove(_hbox) + _hbox.destroy() + _view_icon = gtk.Image() + if _index == 0: + self.__view = DecompositionList(_budget, _wr_page, _path, + _path_record) + + _view_icon.set_from_file(globals.getAppPath("DECOMPOSITION-ICON")) + self.__view_type = "DecompositionList" + elif _index == 1: + self.__view = Description(_budget, _wr_page, _path, + _path_record) + _view_icon.set_from_file(globals.getAppPath("DESCRIPTION-ICON")) + self.__view_type = "RecordDescription" + elif _index == 2: + self.__view = Measure(_budget, _wr_page, _path, + _path_record) + _view_icon.set_from_file(globals.getAppPath("MEASURE-ICON")) + self.__view_type = "Measure" + elif _index == 3: + self.__view = Sheet(_budget, _wr_page, _path, + _path_record) + _view_icon.set_from_file(globals.getAppPath("SHEET-ICON")) + self.__view_type = "Sheet of Conditions" + elif _index == 4: + self.__view = FileView(_budget, _wr_page, _path, + _path_record) + _view_icon.set_from_file(globals.getAppPath("SHEET-ICON")) + self.__view_type = "FileView" + elif _index == 5: + self.__view = CompanyView(_budget, _wr_page, _path, + _path_record) + _view_icon.set_from_file(globals.getAppPath("SHEET-ICON")) + self.__view_type = "CompanyView" + _view_icon.show() + _hbox = gtk.HBox() + _hbox.pack_start(_view_icon, False, False,0) + _hbox.pack_start(_combobox, False, False,0) + _hbox.pack_start(_invisible, True, False,0) + _hbox.pack_start(_menu_button, False, False, 0) + _hbox.pack_start(_connected_button, False, False, 0) + _hbox.pack_start(_close_button, False, False, 0) + _hbox.show() + _vbox.pack_start(_hbox, False, False, 0) + _vbox.pack_start(self.__view.widget, True, True, 0) + + def _menu_view(self, widget): + """_menu_view(self, widget) + + Method connected to the "clicked" signal of the __connected_button + It shows a popup menu with some options + """ + _menu_view = gtk.Menu() + _item_leftright = gtk.MenuItem("Split View Left/Right") + _menu_view.append(_item_leftright) + _item_leftright.connect_object("activate", self._split_view, "h") + _item_leftright.show() + _item_topbottom = gtk.MenuItem("Split View Top/Bottom") + _menu_view.append(_item_topbottom) + _item_topbottom.connect_object("activate", self._split_view, "v") + _item_topbottom.show() + _item_close = gtk.MenuItem("close view") + _menu_view.append(_item_close) + _item_close.connect_object("activate", self._closeItem, None) + _item_close.show() + _menu_view.popup(None, None, None, 0, 0) + + def _split_view(self, orientation): + """_menu_view(self, orientation) + + orientation: orientation split, "h" or "v" + + Method connected to the "activate" signal of the _item_leftright and + _item_topbottom menu items. + It sends the "split" message to the page to splits the view in the + specified orientation + """ + self.propagateMessageFrom( "split " + orientation, self.__path) + + def _connected(self, widget): + """_connected(self, widget) + + Method connected to the "clicked" signal of the _menu_button + It changes the __connected atribute to True or False, if the + _connected atribute is False the view do not send and receive messages + to/from others views + """ + if self.__connected: + _icon = gtk.Image() + _icon.set_from_file(globals.getAppPath("DISCONNECTED-ICON")) + _icon.show() + self.__connected_button.set_icon_widget(_icon) + self.__connected = False + else: + _icon = gtk.Image() + _icon.set_from_file(globals.getAppPath("CONNECTED-ICON")) + _icon.show() + self.__connected_button.set_icon_widget(_icon) + self.__connected = True + + def propagateMessageFrom(self, message, path, arg=None): + """def propagateMessageFrom(self, message, path, arg=None) + + message: string message + path: tuple that represents the pane path which emits the message + arg: arguments for the message + The panes are connectted to this method to send messages to other panes + """ + if self.__connected or message == "autoclose" or \ + message == "split h" or message == "split v": + self.__wr_page().propagateMessageFrom(message, path, arg) + + def runMessage(self, message, path, arg=None): + """def runMessage(self, message, path, arg=None) + + message: the message type + "change_active": change the active record + "clear": clear instance + path: tuple that identifies the pane in the notebook page + arg: tuple whit two items: + 0: record path in the budget + 1: record code + This method receives a message and executes its corresponding action + """ + if self.__connected: + self.__view.runMessage(message, path, arg) + if message == "change_active": + if self.__budget.hasPath(arg): + _path_record = arg + self.__active_path_record = _path_record + + def getWidget(self): + """def getWidget(self) + + Return de pane widget + """ + return self.__widget + + def getPath(self): + """def getPath(self) + + return the tuple that identifies the pane in the notebook page + """ + return self.__view.path + + def setPath(self, path): + """def setPath(self) + + set the tuple that identifies the pane in the notebook page + """ + self.__path = path + self.__view.path = path + + def getClone(self, new_path): + """getClone(self, new_path) + + new_path: the path that identifies the clone view in the page + + return a clone of itself + """ + return View(self.__view_type, self.__budget, self.__wr_page, + new_path, self.__active_path_record) + + def clear(self): + """clear(self) + + Clear the intance atributes + """ + del self.__wr_page + del self.__budget + del self.__path + del self.__widget + del self.__view + del self.__connected + del self.__connected_button + + path = property(getPath, setPath, None, + "path that identifies the item in the notebook page") + widget = property(getWidget, None, None, "View widget") + +class Paned(object): + """gui.Paned: + + Description: + It creates and shows gtk.Hpaned or gtk.Vpaned to show in page budget + Constructor: + Paned(orientation, widget1, widget2) + orientation: The orientation of the pane separator, can be "v" or "h" + widget1: the top or left pane widget + widget2: the botton or right pane widget + Returns the newly created Paned instance + Ancestry: + +-- object + +-- Paned + Atributes: + "widget": Pane widget("gtk.VPaned" or "gtk.HPaned" object) + "__orientation": The orientation of de gtk.Paned, can be "v" or "h" + "__items": list of items showed in the paned, its can be View or Paned + instances + "__path": the paned path in the page + Methods: + __init__(self) + __getitem__(self, item) + getClone(self, new_path) + getItem(self, path) + runMessage(self, messge, path, arg=None) + getWidget(self) + {get/set}Path + clear(self) + """ + # TODO: *control the position paned separator. Now is always 200 pixels + # TODO: can be with a float(0.0-1.0) aspect ratio + # TODO: 0.0 no space for widget1 + # TODO: 1.0 all the space for widget1 + # TODO: *control the position pane separator when the size of the window + # TODO: change with the same ascpect ratio + + def __init__(self, orientation, path, item1, item2): + """def __init__(self, oritentation, path, item1, item2) + + orientation: The orientation of de gtk.Paned, can be "v" or "h" + path: the paned path in the page + item1: the top or left pane object + item2: the bottom or right pane object + + Creates and shows a new gtk.Paned + """ + self.__orientation = orientation + if not isinstance(item1.widget, gtk.Widget) or \ + not isinstance(item2.widget, gtk.Widget): + raise ValueError, _("The item must be a widget object.") + if orientation == "v": + self.__widget = gtk.VPaned() + elif orientation == "h": + self.__widget = gtk.HPaned() + else: + raise ValueError, _("Invalid orientation.") + self.__widget.pack1(item1.widget,True,False) + self.__widget.pack2(item2.widget,True,False) + self.__widget.set_position(200) + self.__widget.show() + self.__items = [item1, item2] + self.__path = path + + def __getitem__(self, item): + """__getitem__(self, item) + + Called to implement evaluation of self[key]. + The accepted keys should be integers 0 or 1. + """ + return self.__items[item] + + def getClone(self, new_path): + """getClone(self, new_path) + + Return a clone Paned instance with the path new_path + """ + return Paned(self.__orientation, new_path, + self.__items[0].getClone(new_path + (0,)), + self.__items[1].getClone(new_path + (1,))) + + def getItem(self,path): + """def getItem(self, path) + + Return the item whith the specified path. + """ + _item = self.__items[path[0]] + if len(path) == 1: + return _item + else: + return _item.getItem(path[1:]) + + def setItem(self, path, item_list): + """def setItem(self, path, item_list) + + Sets the first item in the item_list whith the especified path and + remove the old item in this position. + """ + item = item_list[0] + if path == 0 or path == 1: + _old_item = self.__items[path] + self.__widget.remove(_old_item.widget) + self.__items[path] = item + if path == 0: + self.__widget.pack1(item.widget,True,False) + else: + self.__widget.pack2(item.widget,True,False) + return True + return False + + def runMessage(self, message, path, arg=None): + """def runMessage(self, message, page_path, arg=None) + + message: the message type + "change_active": change the active record + "clear": clear instance + page_path: tuple that identifies the pane in the notebook page + arg: arguments + + This method receives a message and send this to the items of the paned + """ + for _item in self.__items: + if not _item.path == path: + _item.runMessage(message, path, arg) + + def getWidget(self): + """def getWidget(self) + + Return de gtk.Paned widget + """ + return self.__widget + + def getPath(self): + """def getPath(self) + + Return de Paned path in the notebook page + """ + return self.__path + + def setPath(self, path): + """def setPath(self) + + sets the tuple that identifies the pane in the notebook page + """ + self.__path = path + self.__items[0].path = path + (0,) + self.__items[1].path = path + (1,) + + def clear(self): + del self.__widget + del self.__orientation + del self.__items + del self.__path + + widget = property(getWidget, None, None, "gtk.Paned widget") + path = property(getPath, setPath, None, "Pane path in the notebook page") + +class TreeView(object): + """gui.Treeviev: + + Description: + It creates the columns in a treeview, is the base class for + DescompositionList and Measure classes + Constructor: + TreView(args) + args: list of tuples, the tuple items are: + 0.type: + * index column + * float column + * text column + * calculated column + * calculated text + * type column + 1. clicked method + 2. width + 3. text color + 4. backgruound colors + 5. model column index + Ancestry: + +-- object + +-- TreeView + Atributes: + "columns": list of columns (gtk.TreeViewColumn isntances) + Methods: + __init__(self) + __getitem__(self, item) + createColumn(self, args) + createTextBaseColumn(self,args) + createBaseColumn(self,args) + """ + + def __init__(self, args): + """__init__(self, args) + + args: list of tuples, the tuple items are: + 0.type: + * index column + * float column + * text column + * calculated column + * Calculated text + * type column + 1. clicked method + 2. width + 3. text color + 4. backgruound colors + 5. model column index + + Create the columns form the args info calling creatheColumn to create + each column + """ + self.columns = [ self.createColumn(arg) for arg in args ] + self.columns.append(self.createColumn(("END",))) + + def createColumn(self, args): + """createColumn(self, args) + + args: tuple with the args + 0.type: + * index column + * float column + * text column + * calculated column + * calculated text + * type column + 1. clicked method + 2. width + 3. text color + 4. backgruound colors + 5. model column index + + Return a column created whith the arg info + """ + if args[0] == "INDEX": + _index_column = self.createBaseColumn(args) + _text_index_cell = gtk.CellRendererText() + _text_index_cell.set_property('foreground-gdk', + gtk.gdk.color_parse(globals.color["TEXT"])) + _pixbuf_index_cell = gtk.CellRendererPixbuf() + _arrow_icon = gtk.gdk.pixbuf_new_from_file( + globals.getAppPath("ARROW-ICON")) + _pixbuf_index_cell.set_property("pixbuf", _arrow_icon) + _index_column.pack_start(_text_index_cell, True) + _index_column.pack_start(_pixbuf_index_cell, True) + _index_column.set_cell_data_func(_text_index_cell, + self.colorCell, + [gtk.gdk.color_parse(globals.color["INDEX-UNEVEN"]), + gtk.gdk.color_parse(globals.color["INDEX-EVEN"])]) + return _index_column + elif args[0] == "TEXT": + _column, _cell = self.createTextBaseColumn(args) + _column.add_attribute(_cell, 'text', args[5]) + return _column + elif args[0] == "FLOAT": + _column, _cell = self.createTextBaseColumn(args) + _column.add_attribute(_cell, 'text', args[5]) + _column.get_cell_renderers()[0].set_property('xalign', 1.0) + return _column + elif args[0] == "CALCULATED": + _column, cell = self.createTextBaseColumn(args) + _column.get_cell_renderers()[0].set_property('xalign', 1.0) + return _column + elif args[0] == "CALCULATEDTEXT": + _column, cell = self.createTextBaseColumn(args) + return _column + elif args[0] == "TYPE": + _column = self.createBaseColumn(args) + _type_cell1 = gtk.CellRendererPixbuf() + _type_cell2 = gtk.CellRendererText() + _type_cell2.set_property('foreground-gdk', args[3]) + _column.pack_start(_type_cell1, True) + _column.pack_start(_type_cell2, True) + _column.add_attribute(_type_cell2, 'text', args[5]) + _column.set_cell_data_func(_type_cell1, + self.colorCell, args[4]) + _column.set_cell_data_func(_type_cell2, + self.colorCell, args[4]) + return _column + elif args[0] == "PIXBUF": + _column = self.createBaseColumn(args) + _type_cell1 = gtk.CellRendererPixbuf() + _column.pack_start(_type_cell1, True) + _column.set_cell_data_func(_type_cell1, + self.colorCell, args[4]) + return _column + elif args[0] == "END": + _end_column = gtk.TreeViewColumn() + _end_column.set_clickable(False) + _end_cell = gtk.CellRendererText() + _end_cell.set_property('cell-background-gdk', + gtk.gdk.color_parse(globals.color["UNEVEN"])) + _end_column.pack_start(_end_cell, True) + return _end_column + return None + + def createTextBaseColumn(self,args): + """createTextBaseColumn(self,args) + + args: tuple with the args + 0.type: + * float column + * text column + * calculated column + * calculated text + 1. clicked method + 2. width + 3. text color + 4. backgruound colors + 5. model column index + + Return a column and its CellREndererText + """ + _column = self.createBaseColumn(args) + _cell = gtk.CellRendererText() + _cell.set_property('foreground-gdk', args[3]) + _column.pack_start(_cell, True) + _column.set_cell_data_func(_cell, self.colorCell, args[4]) + return _column, _cell + + def createBaseColumn(self,args): + """createBaseColumn(self,args) + + args: tuple with the args + 0.type: + * index column + * float column + * text column + * calculated column + * calculated text column + * type column + 1. clicked method + 2. width + 3. text color + 4. backgruound colors + 5. model column index + + Return a column + """ + _column = gtk.TreeViewColumn() + _column.set_clickable(True) + _column.connect("clicked", args[1]) + _column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) + _column.set_fixed_width(args[2]) + _column.set_resizable(True) + return _column + +class DecompositionList(TreeView): + """gui.DecompositionList: + + Description: + Class to show a budget Decomposition List + Constructor: + DecompositionList(budget, page, path) + budget: budget showed ("base.Budget" object) + page: weak reference from Page instance which creates this class + path: tuple that represents the view path in the Page + Returns the newly created DecompositionList instance + Ancestry: + +-- object + +-- TreeView + +-- DecompositionList + Atributes: + "budget": Budget to show, base.obra instance. + "widget or __scrolled_window": Window that contains the table, + (gtk.ScrolledWindow) + "path": Pane page identifier + "page": weak reference from Page instance which creates this class + "__active_color": background color of the active cell, a + gtk.gdk.Color object + "__chapter_background_colors": background colors of the Code + column cells when there is a chapter record, + list of gtk.gdk.Color objects [even cell, uneven cell] + "__methond_message": Method to send messages to the page + "__liststore": list model which store the list data + (gtk.ListStore object) + "__treeview": widget for displaying decomposition lists (gtk.TreeView) + "__index_column": Index column (gtk.TreeViewColumn object) + "__code_column": Record code column (gtk.TreeViewColumn) + "__unit_column": Unit of measure column (gtk.TreeViewColumn) + "__description_column": record's short description column + (gtk.TreeViewColumn) + "__measure_column": Measure column (gtk.TreeViewColumn) + "__price_column": Price column (gtk.TreeViewColumn) + "__amount_column": Amount column(gtk.TreeViewColumn) + "__end_column": End empty column (gtk.TreeViewColumn) + "__treeselection": active selection + "__selection_control": state of the selection control (True/False) + "__cursor": cursor position in the table + Methods: + __init__(self, budget) + treeviewCursorChanged(self, treeview) + treeviewClickedEvent(self, widget, event) + treeviewKeyPressEvent(self, widget, event) + moveCursor(self, treeview, step, count) + controlSelection(self, selection) + selectAll(self, column) + setColumnsHeaders(self) + setListstoreValues(self, puntero, treeiter=None) + colorCell(self, column, cell_renderer, tree_model, iter, lcolor) + _claculateAmount(self, row_path, tree_model) + showParentRecord(self, column) + showMessageRecord(self, camino,_code): + showRowRecord(self, treeview, path, column) + runMessage(self, messagem path, arg=None) + _clear(self) + getWidget(self) + {get/set}Path + {get/seg}Page + getBudget(self) + getActivePathRecord(self) + """ + + def __init__(self, budget, page, path, path_record=(0,)): + """def __init__(self, budget, page, path) + + budget: budget showed ("base.Budget" object) + page: weak reference from Page instance which creates this class + path: tuple that represents the path of the List in the Page + + Sets the init atributes + Creates the init list values in self.__liststore from the budget + showing the top record descomposition + Creates the list in self.__treeview + * Creates the columns and cell + * Sets te the column headers values + * Sets the selection properties + * Connects the events + """ + # TODO: to group all columns in a dicctionary + # Budget + if not isinstance(budget, base.Budget): + raise ValueError, _("Argument must be a Budget object") + self.__budget = budget + self.__page = page + self.__path = path + # ListStore + self.__liststore = gtk.ListStore(object + #, int, int, str, str, str, str, str,str + ) + if path_record is None: + print _("DecompositionList.__init__: Record path can not be None") + path_record = (0,) + self.__active_path_record = path_record + self.setListstoreValues(self.__active_path_record) + # Treeview + self.__treeview = gtk.TreeView(self.__liststore) + self.__treeview.set_enable_search(False) + self.__treeview.set_reorderable(False) + self.__treeview.set_headers_clickable(True) + self.__treeview.show() + # Scrolled_window + self.__scrolled_window = gtk.ScrolledWindow() + self.__scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, + gtk.POLICY_AUTOMATIC) + self.__scrolled_window.add(self.__treeview) + # colors + _text_color = gtk.gdk.color_parse(globals.color["TEXT"]) + _background_color = [ + gtk.gdk.color_parse(globals.color["UNEVEN"]), + gtk.gdk.color_parse(globals.color["EVEN"])] + self.__chapter_background_colors = [ + gtk.gdk.color_parse(globals.color["CHAPTER-UNEVEN"]), + gtk.gdk.color_parse(globals.color["CHAPTER-EVEN"])] + super(DecompositionList,self).__init__( + [("INDEX",self.selectAll,42), + ("CALCULATEDTEXT", self.showParentRecord, + gtk.Label("A"*10).size_request()[0] +10, + _text_color, _background_color), + ("PIXBUF", self.showParentRecord, 26, _text_color, + _background_color), + ("CALCULATEDTEXT", self.showParentRecord, + gtk.Label(_("a"*4)).size_request()[0] +10, + _text_color, _background_color), + ("CALCULATEDTEXT", self.showParentRecord, + gtk.Label("a"*30).size_request()[0] +10, + _text_color, _background_color), + ("CALCULATED", self.showParentRecord, + gtk.Label("a"*10).size_request()[0] +10, + _text_color, _background_color), + ("CALCULATED", self.showParentRecord, + gtk.Label("a"*10).size_request()[0] +10, + _text_color, _background_color), + ("CALCULATED", self.showParentRecord, + gtk.Label("a"*10).size_request()[0] +10, + gtk.gdk.color_parse(globals.color["CALCULATED-TEXT"]), + _background_color), + ]) + self.__index_column = self.columns[0] + self.__code_column = self.columns[1] + self.__type_column = self.columns[2] + self.__unit_column = self.columns[3] + self.__description_column = self.columns[4] + self.__measure_column = self.columns[5] + self.__price_column = self.columns[6] + self.__amount_column = self.columns[7] + self.__end_column = self.columns[8] + # Index column + self.__treeview.append_column(self.__index_column) + # Code column + self.__treeview.append_column(self.__code_column) + # Type column + self.__treeview.append_column(self.__type_column) + self.chapter_icon = gtk.gdk.pixbuf_new_from_file( + globals.getAppPath("CHAPTER-ICON")) + self.unit_icon = gtk.gdk.pixbuf_new_from_file( + globals.getAppPath("UNIT-ICON") ) + self.material_icon = gtk.gdk.pixbuf_new_from_file( + globals.getAppPath("MATERIAL-ICON") ) + self.machinery_icon = gtk.gdk.pixbuf_new_from_file( + globals.getAppPath("MACHINERY-ICON")) + self.labourforce_icon = gtk.gdk.pixbuf_new_from_file( + globals.getAppPath("LABOURFORCE-ICON")) + self.__type_column.get_cell_renderers()[0].set_property("pixbuf", + self.labourforce_icon) + + # Unit column + self.__treeview.append_column(self.__unit_column) + # Description column + self.__treeview.append_column(self.__description_column) + # Measure Column + self.__treeview.append_column(self.__measure_column) + # Price column + self.__treeview.append_column(self.__price_column) + # Amount column + self.__treeview.append_column(self.__amount_column) + # End Column + self.__treeview.append_column(self.__end_column) + # Connect + self.__treeview.connect("row-activated", self.showRowRecord) + self.__treeview.connect("move-cursor", self.moveCursor) + self.__treeview.connect("key-press-event", self.treeviewKeyPressEvent) + self.__treeview.connect("button-press-event", self.treeviewClickedEvent) + self.__treeview.connect("cursor-changed", self.treeviewCursorChanged) + # control selection + self.__treeselection = self.__treeview.get_selection() + self.__treeselection.set_mode(gtk.SELECTION_MULTIPLE) + self.__treeselection.set_select_function(self.controlSelection) + self.__selection_control = True + if len(self.__liststore) > 0: + self.__treeview.set_cursor_on_cell((0,),self.__unit_column, + self.__unit_column.get_cell_renderers()[0],True) + self.__treeview.grab_focus() + self.__cursor = self.__treeview.get_cursor() + # Show + self.setColumnsHeaders() + self.__scrolled_window.show() + + def treeviewCursorChanged(self, treeview): + """def treeviewCursorChanged(self, treeview) + + treeview: treewiew widget + Method connected to "cursor-changed" signal + The "cursor-changed" signal is emitted when the cursor moves or is set + Sets the new cursor position in self.__cursor, it is used to avoid + unnecessary changes in cursor position. + """ + event = gtk.get_current_event() + (_cursor_path, _column) = treeview.get_cursor() + if event is None or event.type != gtk.gdk.BUTTON_RELEASE: + if not _column is self.__index_column: + self.__cursor = treeview.get_cursor() + + def treeviewClickedEvent(self, widget, event): + """def treeviewClickedEvent(self, widget, event) + + widget: treewiew widget + event: clicked event + Method connected to "button-press-event" signal + The "button-press-event" signal is emitted when a mouse button is + pressed. + Returns TRUE to stop other handlers from being invoked for the event. + Returns FALSE to propagate the event further. + + The events in end column are ignored. + If the user click in a row of the index column the cursor is moved to + this row but not to the index column + """ + if event.button == 1: + path_at_pos = self.__treeview.get_path_at_pos(int(event.x), + int(event.y)) + if not path_at_pos is None: + _path_cursor, _column, _x, _y = path_at_pos + if _column == self.columns[-1]: + return True + if _column is self.columns[0]: + self.__cursor[0] == _path_cursor + return False + + def treeviewKeyPressEvent(self, widget, event): + """def treeviewKeyPressEvent(self, widget, event) + + widget: treewiew widget + event: Key Press event + Method connected to "key-press-event" signal + The "key-press-event" signal is emitted when the user presses a key + on the keyboard. + Returns :TRUE to stop other handlers from being invoked for the event. + Returns :FALSE to propagate the event further. + + If the user press the right cursor button and the cursor is in the + amount column or pres the left cursor button and the cursor is + in the code column the event is estoped, else the event is propagated. + """ + (_cursor_path, _column) = self.__treeview.get_cursor() + if (event.keyval == gtk.keysyms.Right \ + and _column == self.columns[-2]) \ + or (event.keyval == gtk.keysyms.Left \ + and _column == self.columns[1]): + return True + return False + + def moveCursor(self, treeview, step, count): + """def treeviewKeyPressEvent(self, widget, event) + + treeview: the treeview that received the signal + step: the movement step size + count: the number of steps to take + + Method connected to "move-cursor" signal + The "move-cursor" signal is emitted when the user moves the cursor + using the Right, Left, Up or Down arrow keys or the Page Up, + Page Down, Home and End keys. + + Returns :TRUE if the signal was handled. + """ + return False + + def controlSelection(self, selection): + """def controlSelection(self, selection) + + selection: treeselection + + Method connected to set_selection_function() + This method is called before any node is selected or unselected, + giving some control over which nodes are selected. + The selection function should return TRUE if the state + of the node may be toggled, and FALSE if the state of the node should + be left unchanged. + + The selection only run if the user click in the index column, else + the previous selection is erased. + """ + _column = self.__treeview.get_cursor()[1] + if _column is self.columns[0] \ + or self.__selection_control == False: + return True + else: + self.__selection_control = False + self.__treeselection.unselect_all() + self.__selection_control = True + return False + + def selectAll(self, column): + """def selectAll(self, column) + + column: index column + Method connected to "clicked" event in the index column + If the user clickes in the index column header selecs or deselects + all rows + """ + (_model, _pathlist) = self.__treeselection.get_selected_rows() + # it avoid to set cursor in the index column + self.__treeview.set_cursor(self.__cursor[0], self.__cursor[1]) + self.__selection_control = False + if len(_pathlist) == 0: + # select all + self.__treeselection.select_all() + else: + # unselect all + self.__treeselection.unselect_all() + self.__selection_control = True + + def setColumnsHeaders(self): + """def setColumnsHeaders(self) + + Sets the headers column values + """ + _path_record = self.__active_path_record + _number = _path_record[-1] + _budget = self.__budget + _code = _budget.getCode(_path_record) + _decomposition = _budget.getDecomposition(_path_record) + _stryield = _budget.getStrYield(_decomposition.budgetMeasures[0], + _budget.getRecord(_code).recordType) + _record = _budget.getRecord(_code) + _unit = _record.unit + _description = _record.summary + _price = _budget.getStrPriceFromRecord(self.budget.getActiveTitle(), + _record) + # TODO: round to decimal places in amount + _amount = float(_stryield) * float(_price) + if len(_path_record) == 1: # root record + _amount = _price + else: + _parent_code = self.budget.getCode(self.__active_path_record[:-1]) + _parent_record = self.__budget.getRecord(_parent_code) + _amount = _budget.getStrAmount(self.__active_path_record) + + self.__code_column.set_title(_("Code") + chr(10) + "[" + _code + "]") + self.__unit_column.set_title(_("Unit") + chr(10) + "[" + _unit + "]") + self.__description_column.set_title( + _("Description") + chr(10) + "[" + _description + "]") + self.__measure_column.set_title( + _("Measure") + chr(10) + "[" + _stryield + "]") + self.__price_column.set_title( + _("Price") + chr(10) + "[" + _price + "]") + self.__amount_column.set_title( + _("Amount") + chr(10) + "[" + str(_amount) + "]") + + + def setListstoreValues(self, path_record): + """def setListstoreValues(self, path_record) + + path_record: Record path in the budget + Sets the liststore record values from a path record + """ + self.__liststore.clear() + _budget = self.__budget + if not _budget.hasPath(path_record): + raise ValueError, _("Invalid path") + else: + _parent_code = _budget.getCode(path_record) + for N,_code in enumerate(_budget.getchildren(_parent_code)): + _decomposition = _budget.getNDecomposition(_parent_code, N) + _record = _budget.getRecord(_code) + _values = [_record, + #_record.hierarchy, + #_record.type, + #_record.subtype, + #_code, + #_record.unit, + #_record.summary, + #_decomposition.yield_, + #_decomposition.budget[0].yield_, + #_record.prices[_budget.getActiveTitle()].prices] + #_record.getPrice(_budget.getActiveTitle()) + ] + _treeiter = self.__liststore.append(_values) + + def colorCell(self, column, cell_renderer, tree_model, iter, lcolor): + """def colorCell(self, column, cell_renderer, tree_model, iter, lcolor) + + column: the gtk.TreeViewColumn in the treeview + cell_renderer: a gtk.CellRenderer + tree_model: the gtk.TreeModel + iter: gtk.TreeIter pointing at the row + lcolor: list with 2 gtk colors for even and uneven record + + Method connected to "set_cell_data_func" of many column + The set_cell_data_func() method sets the data function (or method) + to use for the column gtk.CellRenderer specified by cell_renderer. + This function (or method) is used instead of the standard attribute + mappings for setting the column values, and should set the attributes + of the cell renderer as appropriate. func may be None to remove the + current data function. The signature of func is: + -def celldatafunction(column, cell, model, iter, user_data) + -def celldatamethod(self, column, cell, model, iter, user_data) + where column is the gtk.TreeViewColumn in the treeview, cell is the + gtk.CellRenderer for column, model is the gtk.TreeModel for the + treeview and iter is the gtk.TreeIter pointing at the row. + + The method sets cell background color and text for all columns. + """ + _row_path = tree_model.get_path(iter) + _number = _row_path[-1] + _record = tree_model[_row_path][0] + if column is self.__index_column: + cell_renderer.set_property('text', str(_number + 1)) + self.__index_column.get_cell_renderers()[1].set_property( + 'cell-background-gdk', lcolor[_number % 2]) + elif column is self.__code_column: + # if the record is a chapter + if tree_model.get_value(iter, 0).recordType.hierarchy == 1: + lcolor = self.__chapter_background_colors + _code = _record.code + cell_renderer.set_property('text', _code) + elif column is self.__unit_column: + _unit = _record.unit + cell_renderer.set_property('text', _unit) + elif column is self.__description_column: + _summary = _record.summary + cell_renderer.set_property('text', _summary) + elif column is self.__measure_column: + _parent_code = self.budget.getCode(self.__active_path_record) + _parent_record = self.__budget.getRecord(_parent_code) + _decomposition = _parent_record.children[_number] + _stryield = self.__budget.getStrYield( + _decomposition.budgetMeasures[0], _parent_record.recordType) + cell_renderer.set_property('text', _stryield) + elif column is self.__price_column: + _price = self.budget.getStrPriceFromRecord( + self.budget.getActiveTitle(), _record) + cell_renderer.set_property('text', _price) + elif column is self.__amount_column: + _parent_code = self.budget.getCode(self.__active_path_record) + _parent_record = self.__budget.getRecord(_parent_code) + _amount = self.budget.getStrAmount( + self.__active_path_record + (_number,)) + cell_renderer.set_property('text', str(_amount)) + elif column is self.__type_column: + _hierarchy = tree_model[_row_path][0].recordType.hierarchy + _type = tree_model[_row_path][0].recordType.type + _subtype = tree_model[_row_path][0].recordType.subtype + if _hierarchy == 1: + cell_renderer.set_property("pixbuf",self.chapter_icon) + else: + if _type == 0: + cell_renderer.set_property("pixbuf",self.unit_icon) + elif _type == 1: + cell_renderer.set_property("pixbuf", + self.labourforce_icon) + elif _type == 2: + cell_renderer.set_property("pixbuf", + self.machinery_icon) + else: + cell_renderer.set_property("pixbuf",self.material_icon) + if self.__treeview.get_cursor() == (_row_path,column): + cell_renderer.set_property('cell-background-gdk', + gtk.gdk.color_parse(globals.color["ACTIVE"])) + else: + cell_renderer.set_property('cell-background-gdk', + lcolor[_number % 2]) + + def showParentRecord(self, column): + """def showParentRecord(self, column) + + column: the column that is clicked + Method connected to "clicked" event of many columns + Show the parent record + """ + _budget = self.__budget + if len(self.__active_path_record) == 1: + # The active record is the root record + # This avoid to move the cursor to the clicked column + self.__treeview.set_cursor(self.__cursor[0], self.__cursor[1]) + else: + _path_record = self.__active_path_record[:-1] + _parent = self.__active_path_record[-1] + self.__active_path_record = _path_record + self.setColumnsHeaders() + self.setListstoreValues(self.__active_path_record) + arg = ( _path_record ) + _page = self.__page() + _page.propagateMessageFrom("change_active", self.__path, arg) + self.__treeview.set_cursor(_parent, self.__cursor[1]) + self.__cursor = self.__treeview.get_cursor() + + def showMessageRecord(self, record_path): + """def showMessageRecord(self, record_path) + + record_path: the path of the record to show + Method connected to "change_active" message + Show the record especified in the "change_active" message + """ + _budget = self.__budget + self.__active_path_record = record_path + self.setColumnsHeaders() + self.setListstoreValues(self.__active_path_record) + self.__treeview.set_cursor((0,)) + + def showRowRecord(self, treeview, treeview_path, column): + """def showRowRecord(self, treeview, treeview_path, column) + + treeview: treview to show + treeview_path: the path of the record to show + code: the code of the record to show + + Method connected to "row-activated" event + The "row-activated" signal is emitted when the row_activated() method + is called or the user double clicks a treeview row. + "row-activated" is also emitted when a non-editable row is selected + and one of the keys: Space, Shift+Space, Return or Enter is pressed. + Show the especified record + """ + if not (column is self.__end_column) and \ + not (column is self.__index_column): + _budget = self.__budget + _model = treeview.get_model() + _iter = _model.get_iter(treeview_path) + _code = _model.get_value(_iter, 0).code + #_code = _model.get_value(_iter, 4) + _path_record = self.__active_path_record + treeview_path + if self.__budget.hasPath(_path_record): + # if this record path is valid + self.__active_path_record = _path_record + self.setColumnsHeaders() + self.setListstoreValues(self.__active_path_record) + self.__treeview.set_cursor((0,)) + _arg = ( _path_record ) + _page = self.__page() + _page.propagateMessageFrom("change_active", self.__path, + _arg ) + + def runMessage(self, message, path, arg=None): + """def runMessage(self, message, path, arg=None) + + message: the message type + "change_active": change the active record + "clear": clear instance + path: tuple that identifies the pane in the notebook page + arg: tuple whit two items: + 0: record path in the budget + 1: record code + This method receives a message and executes its corresponding action + """ + _budget = self.__budget + if message == "change_active": + if _budget.hasPath(arg): + _path_record = arg + self.showMessageRecord( _path_record) + elif message == "clear": + self._clear() + + def _clear(self): + """def _clear(self) + + it deletes the __budget value + this would not be necessary if there were not circular references, + which are pending to fix + """ + del self.__budget + + def getWidget(self): + """def getWidget(self) + + return the main widget (gtk.ScrolledWindow) + """ + return self.__scrolled_window + + def getPath(self): + """def getPath(self) + + return the tuple that identifies the pane in the notebook page + """ + return self.__path + + def setPath(self, path): + """def setPath(self) + + sets the tuple that identifies the pane in the notebook page + """ + self.__path = path + + def getPage(self): + """def getPage(self) + + return the Page + """ + return self.__page + + def setPage(self,page): + """def setPage(self) + + set the Page + """ + self.__page = page + + def getBudget(self): + """def getBudget(self) + + return the Budget objet + """ + return self.__budget + + def getActivePathRecord(self): + """def getActivePathRecord(self) + + return the Active Path Record + """ + return self.__active_path_record + + widget = property(getWidget, None, None, + "Pane configuration list") + path = property(getPath, setPath, None, + "path that identifie the item in the page notebook") + page = property(getPage, setPage, None, + "weak reference from Page instance which creates this class") + budget = property(getBudget, None, None, + "Budget object") + active_path_record = property(getActivePathRecord, None, None, + "Active path record") + +class Measure(TreeView): + """gui.Measure: + + Description: + Class to show a Measure List + Constructor: + Measure(budget, page, path) + budget: budget showed ("base.Budget" object) + page: weak reference from Page instance which creates this class + path: tuple that represents the path of the List in the Page + Returns the newly created DecompositionList instance + Ancestry: + +-- object + +-- TreeView + +-- DecompositionList + Atributes: + "budget": Budget to show, base.obra instance. + "__active_path_record": path of the active record in the budget + "widget or __scrolled_window": Window that contains the table, + (gtk.ScrolledWindow) + "path": Pane page identifier + "page": weak reference from Page instance which creates this class + "__active_color": The background color of the active cell as a + gtk.gdk.Color object + "__chapter_background_colors": The background colors of the Code + column cells when there is a chapter record + as a list of gtk.gdk.Color objects [even cell, uneven cell] + "__methond_message": Method to send messages to the page + "__liststore": list model which store the list data + (gtk.ListStore object) + "__treeview": widget for displaying decomposition lists (gtk.TreeView) + "__index_column": Index column (gtk.TreeViewColumn object) + "__code_column": Record code column (gtk.TreeViewColumn) + "__unit_column": Unit of measure column (gtk.TreeViewColumn) + "__description_column": record's short description column + (gtk.TreeViewColumn) + "__measure_column": Measure column (gtk.TreeViewColumn) + "__price_column": Price column (gtk.TreeViewColumn) + "__amount_column": Amount column(gtk.TreeViewColumn) + "__end_column": End empty column (gtk.TreeViewColumn) + "__treeselection": active selection + "__selection_control": state of the selection control (True/False) + "__cursor": Situation of the cursor in the table + Methods: + __init__(self, budget, page, path, path_record=(0,)) + setListstoreValues(self, path_record) + setColumnsHeaders(self) + controlSelection(self, selection) + showMessageRecord(self, record_path) + treeviewCursorChanged(self, treeview) + moveCursor(self, treeview, step, count) + treeviewClickedEvent(self, widget, event) + treeviewKeyPressEvent(self, widget, event) + runMessage(self, message, path, arg=None) + selectAll(self, column) + colorCell(self, column, cell_renderer, tree_model, iter, lcolor) + _clear(self) + getWidget(self) + {get/set}Path + {get/set}Page + getBudget(self) + getActivePathRecord(self) + """ + + def __init__(self, budget, page, path, path_record=(0,)): + """def __init__(self, budget, page, path, path_record=(0,)) + + budget: budget: budget showed ("base.Budget" object) + page: weak reference from Page instance which creates this class + path: tuple that represents the path of the List in the Page + path_record: path of the active record in the budget + + Sets the init atributes + Creates the init list values in self.__liststore from the budget + showing the top record from the record with path path_record + Creates the list in self.__treeview + * Creates the columns and cell + * Sets te the column headers values + * Sets the selection properties + * Connects the events + """ + # Seting init args + if not isinstance(budget, base.Budget): + raise ValueError, _("Argument must be a Budget object") + self.__budget = budget + self.__page = page + self.__path = path + if not isinstance(path_record, tuple): + print _("Record path must be a tuple") + path_record = (0,) + self.__active_path_record = path_record + # ListStore + self.__liststore = gtk.ListStore(object) + self.setListstoreValues(self.__active_path_record) + # Treeview + self.__treeview = gtk.TreeView(self.__liststore) + self.__treeview.set_enable_search(False) + self.__treeview.set_reorderable(False) + self.__treeview.set_headers_clickable(True) + self.__treeview.show() + # Scrolled_window + self.__scrolled_window = gtk.ScrolledWindow() + self.__scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, + gtk.POLICY_AUTOMATIC) + self.__scrolled_window.add(self.__treeview) + # colors + _text_color = gtk.gdk.color_parse(globals.color["TEXT"]) + _calculated_text =gtk.gdk.color_parse(globals.color["CALCULATED-TEXT"]) + _background_color = [ + gtk.gdk.color_parse(globals.color["UNEVEN"]), + gtk.gdk.color_parse(globals.color["EVEN"])] + self.__chapter_background_colors = [ + gtk.gdk.color_parse(globals.color["CHAPTER-UNEVEN"]), + gtk.gdk.color_parse(globals.color["CHAPTER-EVEN"])] + super(Measure,self).__init__( + [("INDEX",self.selectAll,42), + ("PIXBUF", self.passMethod, + gtk.Label("A"*4).size_request()[0] +10, + _text_color, _background_color), + ("CALCULATEDTEXT", self.passMethod, + gtk.Label("A"*12).size_request()[0] +10, + _text_color, _background_color), + ("CALCULATED", self.passMethod, + gtk.Label("A"*5).size_request()[0] +10, + _text_color, _background_color), + ("CALCULATED", self.passMethod, + gtk.Label("A"*7).size_request()[0] +10, + _text_color, _background_color), + ("CALCULATED", self.passMethod, + gtk.Label("A"*7).size_request()[0] +10, + _text_color, _background_color), + ("CALCULATED", self.passMethod, + gtk.Label("A"*7).size_request()[0] +10, + _text_color, _background_color), + ("CALCULATEDTEXT", self.passMethod, + gtk.Label("A"*12).size_request()[0] +10, + _text_color, _background_color), + ("CALCULATED", self.passMethod, + gtk.Label("A"*7).size_request()[0] +10, + _calculated_text, _background_color), + ("CALCULATED", self.passMethod, + gtk.Label("A"*7).size_request()[0] +10, + _calculated_text, _background_color), + ]) + self.__index_column = self.columns[0] + self.__linetype_column = self.columns[1] + self.__comment_column = self.columns[2] + self.__units_column = self.columns[3] + self.__length_column = self.columns[4] + self.__width_column = self.columns[5] + self.__height_column = self.columns[6] + self.__formula_column = self.columns[7] + self.__parcial_column = self.columns[8] + self.__subtotal_column = self.columns[9] + self.__end_column = self.columns[10] + # Index column + self.__treeview.append_column(self.__index_column) + # Linetype column + self.__treeview.append_column(self.__linetype_column) + self.calculatedline_icon = gtk.gdk.pixbuf_new_from_file( + globals.getAppPath("CALCULATEDLINE-ICON")) + self.normalline_icon = gtk.gdk.pixbuf_new_from_file( + globals.getAppPath("NORMALLINE-ICON") ) + self.parcialline_icon = gtk.gdk.pixbuf_new_from_file( + globals.getAppPath("PARCIALLINE-ICON") ) + self.acumulatedline_icon = gtk.gdk.pixbuf_new_from_file( + globals.getAppPath("ACUMULATEDLINE-ICON")) + # Comment column + self.__treeview.append_column(self.__comment_column) + # Units column + self.__treeview.append_column(self.__units_column) + # Length column + self.__treeview.append_column(self.__length_column) + # Width_column + self.__treeview.append_column(self.__width_column) + # Height column + self.__treeview.append_column(self.__height_column) + # Formula column + self.__treeview.append_column(self.__formula_column) + # Parcial column + self.__treeview.append_column(self.__parcial_column) + # Subtotal column + self.__treeview.append_column(self.__subtotal_column) + # End Column + self.__treeview.append_column(self.__end_column) + # Connect + self.__treeview.connect("move-cursor", self.moveCursor) + self.__treeview.connect("key-press-event", self.treeviewKeyPressEvent) + self.__treeview.connect("button-press-event", self.treeviewClickedEvent) + self.__treeview.connect("cursor-changed", self.treeviewCursorChanged) + # control selection + self.__treeselection = self.__treeview.get_selection() + self.__treeselection.set_mode(gtk.SELECTION_MULTIPLE) + self.__treeselection.set_select_function(self.controlSelection) + self.__selection_control = True + self.__treeview.set_cursor_on_cell((1,), self.columns[1], + self.columns[1].get_cell_renderers()[0],True) + self.__treeview.grab_focus() + self.__cursor = self.__treeview.get_cursor() + # Show + self.setColumnsHeaders() + self.__scrolled_window.show() + + def passMethod(self, args): + pass + + def setListstoreValues(self, path_record): + """def setListstoreValues(self, path_record) + + path_record: Record path in the budget + Sets the liststore record values from a path record + """ + self.__liststore.clear() + _budget = self.__budget + if not _budget.hasPath(path_record): + raise ValueError, _("Invalid path") + else: + _measure = _budget.getMeasure(path_record) + if isinstance(_measure, base.Measure): + _lines = _measure.lines + for _line in _lines: + _values = [ + _line, +## _line.type, +## _line.comment, +## _line.units, +## _line.length, +## _line.width, +## _line.height + ] + _treeiter = self.__liststore.append(_values) + else: + raise ValueError, utils.mapping(_("measure must be a Measure "\ + "object. Type: $1"), (type(_measure),)) + def setColumnsHeaders(self): + """def setColumnsHeaders(self) + + Sets the headers column values + """ + _measure = self.__budget.getMeasure(self.__active_path_record) + _DS = self.__budget.getDecimals("DS") + _total = _measure.measure + _total_str = ("%." + str(_DS) + "f" ) % _total + self.columns[1].set_title(_("Type")) # Σ parcial Σ total + self.columns[2].set_title(_("Comment")) + self.columns[3].set_title(_("N\n(a)")) + self.columns[4].set_title(_("Length\n(b)")) + self.columns[5].set_title(_("Width\n(c)")) + self.columns[6].set_title(_("Height\n(d)")) + self.columns[7].set_title(_("Formula")) + self.columns[8].set_title(_("Parcial\n[%s]" % _total_str)) + self.columns[9].set_title(_("Subtotal")) + def controlSelection(self, selection): + """def controlSelection(self, selection) + + selection: treeselection + + Method connected to set_selection_function() + This method is called before any node is selected or unselected, + giving some control over which nodes are selected. + The selection function should return TRUE if the state + of the node may be toggled, and FALSE if the state of the node should + be left unchanged. + + The selection only run if the user click in the index column, else + the previous selection is erased. + """ + _column = self.__treeview.get_cursor()[1] + if _column is self.columns[0] \ + or self.__selection_control == False: + return True + else: + self.__selection_control = False + self.__treeselection.unselect_all() + self.__selection_control = True + return False + + def showMessageRecord(self, record_path): + """def showMessageRecord(self, record_path) + + record_path: the path of the record to show + Method connected to "change_active" message + Show the record especified in the "change_active" message + """ + _budget = self.__budget + self.__active_path_record = record_path + self.setColumnsHeaders() + self.setListstoreValues(self.__active_path_record) + self.__treeview.set_cursor((0,)) + + def treeviewCursorChanged(self, treeview): + """def treeviewCursorChanged(self, treeview) + + treeview: treewiew widget + Method connected to "cursor-changed" signal + The "cursor-changed" signal is emitted when the cursor moves or is set + Sets the new cursor position in self.__cursor, it is used to avoid + unnecessary changes in cursor position. + """ + event = gtk.get_current_event() + (_cursor_path, _column) = treeview.get_cursor() + if event is None or event.type != gtk.gdk.BUTTON_RELEASE: + if not _column is self.__index_column: + self.__cursor = treeview.get_cursor() + + def moveCursor(self, treeview, step, count): + """def treeviewKeyPressEvent(self, widget, event) + + treeview: the treeview that received the signal + step: the movement step size + count: the number of steps to take + + Method connected to "move-cursor" signal + The "move-cursor" signal is emitted when the user moves the cursor + using the Right, Left, Up or Down arrow keys or the Page Up, + Page Down, Home and End keys. + + Returns :TRUE if the signal was handled. + """ + return False + + def treeviewClickedEvent(self, widget, event): + """def treeviewClickedEvent(self, widget, event) + + widget: treewiew widget + event: clicked event + Method connected to "button-press-event" signal + The "button-press-event" signal is emitted when a mouse button is + pressed. + Returns TRUE to stop other handlers from being invoked for the event. + Returns FALSE to propagate the event further. + + The events in end column are ignored. + If the user click in a row of the index column the cursor is moved to + this row but not to the index column + """ + if event.button == 1: + path_at_pos = self.__treeview.get_path_at_pos(int(event.x), + int(event.y)) + if not path_at_pos is None: + _path_cursor, _column, _x, _y = path_at_pos + if _column == self.columns[-1]: + return True + if _column is self.columns[0]: + self.__cursor[0] == _path_cursor + return False + + def treeviewKeyPressEvent(self, widget, event): + """def treeviewKeyPressEvent(self, widget, event) + + widget: treewiew widget + event: Key Press event + Method connected to "key-press-event" signal + The "key-press-event" signal is emitted when the user presses a key + on the keyboard. + Returns :TRUE to stop other handlers from being invoked for the event. + Returns :FALSE to propagate the event further. + + If the user press the right cursor button and the cursor is in the + amount column or pres the left cursor button and the cursor is + in the code column the event is estoped, else the event is propagated. + """ + (_cursor_path, _column) = self.__treeview.get_cursor() + if (event.keyval == gtk.keysyms.Right \ + and _column == self.columns[-2]) \ + or (event.keyval == gtk.keysyms.Left \ + and _column == self.columns[1]): + return True + return False + + def runMessage(self, message, path, arg=None): + """def runMessage(self, message, path, arg=None) + + message: the message type + "change_active": change the active record + "clear": clear instance + path: tuple that identifies the pane in the notebook page + arg: tuple whit two items: + 0: record path in the budget + 1: record code + This method receives a message and executes its corresponding action + """ + _budget = self.__budget + if message == "change_active": + if _budget.hasPath(arg): + _path_record = arg + self.showMessageRecord( _path_record) + elif message == "clear": + self._clear() + + def selectAll(self, column): + """def selectAll(self, column) + + column: index column + Method connected to "clicked" event in the index column + If the user clickes in the index column header selecs or deselects + all rows + """ + (_model, _pathlist) = self.__treeselection.get_selected_rows() + # it avoid to set cursor in the index column + self.__treeview.set_cursor(self.__cursor[0], self.__cursor[1]) + self.__selection_control = False + if len(_pathlist) == 0: + # select all + self.__treeselection.select_all() + else: + # unselect all + self.__treeselection.unselect_all() + self.__selection_control = True + + def colorCell(self, column, cell_renderer, tree_model, iter, lcolor): + """def colorCell(self, column, cell_renderer, tree_model, iter, lcolor) + + column: the gtk.TreeViewColumn in the treeview + cell_renderer: a gtk.CellRenderer + tree_model: the gtk.TreeModel + iter: gtk.TreeIter pointing at the row + lcolor: list with 2 gtk colors for even and uneven record + + Method connected to "set_cell_data_func" of many column + The set_cell_data_func() method sets the data function (or method) + to use for the column gtk.CellRenderer specified by cell_renderer. + This function (or method) is used instead of the standard attribute + mappings for setting the column values, and should set the attributes + of the cell renderer as appropriate. func may be None to remove the + current data function. The signature of func is: + -def celldatafunction(column, cell, model, iter, user_data) + -def celldatamethod(self, column, cell, model, iter, user_data) + where column is the gtk.TreeViewColumn in the treeview, cell is the + gtk.CellRenderer for column, model is the gtk.TreeModel for the + treeview and iter is the gtk.TreeIter pointing at the row. + + The method sets cell background color for all columns + and text for index and amount columns. + """ + _row_path = tree_model.get_path(iter) + _number = _row_path[-1] + if column is self.__index_column: + cell_renderer.set_property('text', str(_number + 1)) + self.__index_column.get_cell_renderers()[1].set_property( + 'cell-background-gdk', lcolor[_number % 2]) + elif column is self.__linetype_column: + _measure = tree_model[_row_path][0] + _type = _measure.lineType + if _type == 0: + cell_renderer.set_property("pixbuf",self.normalline_icon) + elif _type == 1: + cell_renderer.set_property("pixbuf",self.parcialline_icon) + elif _type == 2: + cell_renderer.set_property("pixbuf", + self.acumulatedline_icon) + else: #elif _type == 3: + cell_renderer.set_property("pixbuf", + self.calculatedline_icon) + + elif column is self.__comment_column: + _measure = tree_model[_row_path][0] + _comment = str(_measure.comment) + cell_renderer.set_property('text', _comment) + elif column is self.__units_column: + _measure = tree_model[_row_path][0] + _units = _measure.units + if isinstance(_units, float): + _DN = self.__budget.getDecimals("DN") + _units = ("%." + str(_DN) + "f" ) % _units + cell_renderer.set_property('text', _units) + elif column is self.__length_column: + _measure = tree_model[_row_path][0] + _length = _measure.length + if isinstance(_length, float): + _DD = self.__budget.getDecimals("DD") + _length = ("%." + str(_DD) + "f" ) % _length + cell_renderer.set_property('text', _length) + elif column is self.__width_column: + _measure = tree_model[_row_path][0] + _width = _measure.width + if isinstance(_width, float): + _DD = self.__budget.getDecimals("DD") + _width = ("%." + str(_DD) + "f" ) % _width + cell_renderer.set_property('text', _width) + elif column is self.__height_column: + _measure = tree_model[_row_path][0] + _height = _measure.height + if isinstance(_height, float): + _DD = self.__budget.getDecimals("DD") + _height = ("%." + str(_DD) + "f" ) % _height + cell_renderer.set_property('text', _height) + elif column is self.__formula_column: + _measure = tree_model[_row_path][0] + _formula = _measure.formula + cell_renderer.set_property('text', _formula) + elif column is self.__parcial_column: + _measure_line = tree_model[_row_path][0] + _parcial = _measure_line.parcial + _type = _measure_line.lineType + if _type == 1 or _type == 2: + _parcial = "" + else: + if isinstance(_parcial, float): + _DS = self.__budget.getDecimals("DS") + _parcial = ("%." + str(_DS) + "f" ) % _parcial + cell_renderer.set_property('text', _parcial) + elif column is self.__subtotal_column: + _measure_line = tree_model[_row_path][0] + _type = _measure_line.lineType + if _type == 1 or _type == 2: + if _type == 1: + _color = gtk.gdk.color_parse( + globals.color["SUBTOTAL-PARCIAL"]) + _subtotal = _measure_line.parcial_subtotal + else: #elif _type == 2: + _color = gtk.gdk.color_parse(globals.color["SUBTOTAL"]) + _subtotal = _measure_line.acumulated_subtotal + lcolor = [_color, _color] + if isinstance(_subtotal, float): + _DS = self.__budget.getDecimals("DS") + _subtotal= ("%." + str(_DS) + "f" ) % _subtotal + cell_renderer.set_property('text', _subtotal) + else: + cell_renderer.set_property('text', "") + + if self.__treeview.get_cursor() == (_row_path,column): + cell_renderer.set_property('cell-background-gdk', + gtk.gdk.color_parse(globals.color["ACTIVE"])) + else: + cell_renderer.set_property('cell-background-gdk', + lcolor[_number % 2]) + + def _clear(self): + """def _clear(self) + + it deletes the __budget value + this would not be necessary if there were not circular references, + which are pending to fix + """ + del self.__budget + + def getWidget(self): + """def getWidget(self) + + return the main widget (gtk.ScrolledWindow) + """ + return self.__scrolled_window + + def getPath(self): + """def getPath(self) + + return the tuple that identifies the pane in the notebook page + """ + return self.__path + + def setPath(self, path): + """def setPath(self) + + sets the tuple that identifies the pane in the notebook page + """ + self.__path = path + def getPage(self): + """def getPage(self) + + return the Page + """ + return self.__page + + def setPage(self,page): + """def setPage(self) + + set the Page + """ + self.__page = page + + def getBudget(self): + """def getBudget(self) + + return the Budget objet + """ + return self.__budget + + def getActivePathRecord(self): + """def getActivePathRecord(self) + + return the Active Path Record + """ + return self.__active_path_record + + widget = property(getWidget, None, None, + "Pane configuration list") + path = property(getPath, setPath, None, + "Path that identifies the item in the page notebook") + page = property(getPage, setPage, None, + "Weak reference from Page instance which creates this class") + budget = property(getBudget, None, None, + "Budget object") + active_path_record = property(getActivePathRecord, None, None, + "Active Code") + +class Description(object): + """gui.Description + + Description: + Class to show a description text of a record in a pane + Constructor: + Description(budget, code) + budget: budget + code: code record + Ancestry: + +-- object + +-- Description + Atributes: + "widget": the main widget (gtk.ScrolledWindow object) + "path": the tuple that identifies the pane in the notebook page + TODO + "budget": The budget (base.obra objetc) + "active_code": The active code of the record + "__textbuffer": The textbuffer of the textview that contain + the record text. + "__label": The gtk.label with the title of the pane + Methods: + __init__(self, budget, code) + setActiveCode(self, code) + runMessage(self, message, nt, arg=None) + _clear(self) + getWidget(self) + {get/set}Path + {get/seg}Page + getBudget(self) + getActviCode(self) + """ + # TODO: make standar: "DecompositonList and Description" + def __init__(self, budget, page, path, path_record=(0,)): + """def __init__(self, budget, page, path, path_record=(0,)) + + budget: the budget (base.obra object) + page: weak reference from Page instance which creates this class + path: the path position of the description in the page + path_record: the path of the active record + + Creates an shows the scroledwindow that contain the description text + of the record to be showed in a pane. + """ + self.__budget = budget + self.__page = page + self.__path = path + self.__active_path_record = path_record + _budget = budget + _text = _budget.getRecord(self.__budget.getCode( + self.__active_path_record)).text + _scrollwindow = gtk.ScrolledWindow() + _scrollwindow.set_policy(gtk.POLICY_AUTOMATIC, + gtk.POLICY_AUTOMATIC) + _textview = gtk.TextView() + _textview.set_wrap_mode(gtk.WRAP_WORD) + self.__textbuffer = _textview.get_buffer() + self.__textbuffer.set_text(_text) + _textview.show() + _hbox = gtk.HBox() + _hbox.pack_start(_textview, True, True, 5) + _hbox.show() + _vbox = gtk.VBox() + self.__label = gtk.Label(utils.mapping(_("Description text of the "\ + "record $1"), (self.__budget.getCode( + self.__active_path_record),))) + self.__label.set_alignment(0, 0) + self.__label.show() + _vbox.pack_start(self.__label, False, False, 5) + _vbox.pack_start(_hbox, True, True, 5) + _vbox.show() + _scrollwindow.add_with_viewport(_vbox) + _scrollwindow.show() + self.__widget = _scrollwindow + + + def setActivePathRecord(self, path_record): + """def setActivePathRecord(self, path_record)) + + path_record: active path record + Set the new path code to show its description text. + """ + _budget = self.__budget + self.__active_path_record = path_record + _code = _budget.getCode(self.__active_path_record) + self.__label.set_text(utils.mapping(_("Description text of the record "\ + "$1"), (_code,))) + _text = _budget.getRecord(_code).text + self.__textbuffer.set_text(_text) + + def runMessage(self, message, path, arg=None): + """def runMessage(self, message, path, arg=None) + + message: the message type + "change_active": change the active record + "clear": clear instance + path: tuple that identifies the pane in the notebook page + arg: tuple whit two items: + 0: record path in the budget + 1: record code + This method receives a message and executes its corresponding action + """ + _budget = self.__budget + if message == "change_active": + if _budget.hasPath(arg): + self.setActivePathRecord(arg) + elif message == "clear": + self._clear() + + def _clear(self): + """def _clear(self) + + Delete all instance atributes + """ + del self.__widget + del self.__path + del self.__budget + del self.__active_code + del self.__textbuffer + del self.__label + + def getWidget(self): + """def getWidget(self) + + return the main widget (gtk.ScrolledWindow) + """ + return self.__widget + + def getPath(self): + """def getPath(self) + + return the tuple that identifies the pane in the notebook page + """ + return self.__path + + def setPath(self, path): + """def setPath(self) + + sets the tuple that identifies the pane in the notebook page + """ + self.__path = path + + def getPage(self): + """def getPage(self) + + return the weak reference from Page instance + """ + return self.__page + + def setPage(self, page): + """def setPage(self) + + set the weak reference from Page instance + """ + self.__page = page + + def getBudget(self): + """def getBudget(self) + + return the budget object + """ + return self.__budget + + def getActivePathRecord(self): + """def getActivePathRecord(self) + + return the Active Path Record + """ + return self.__active_path_record + + path = property(getPath, setPath, None, + "Path that identifie the item in the page notebook") + widget = property(getWidget, None, None, + "The main widget (gtk.ScrolledWindow)") + page = property(getPage, setPage, None, + "Weak reference from Page instance which creates this class") + budget = property(getBudget, None, None, + "Budget object") + active_path_record = property(getActivePathRecord, None, None, + "Active Path Record") + +class Sheet(object): + """gui.Sheet + + Description: + Class to show a sheeet of conditions text of a record in a pane + Constructor: + Sheet(budget, code) + budget: budget object + code: code record + Ancestry: + +-- object + +-- Sheet + Atributes: + "widget": the main widget (gtk.ScrolledWindow object) + "path": the tuple that identifies the pane in the notebook page + "budget": The budget (base.obra objetc) + "active_path_record": The active path record + "page": weak reference from Page instance which creates this class + + "__textbuffer": The textbuffer of the textview that contain + the record text. + "__label": The gtk.label with the title of the pane + "__field_liststore": the field liststore + "__field_treeview": the field treeview + "__field_selection": the field selected in field treview + "__section_liststore": the section liststore + "__section_treeview": the section treeview + "__section_selection": the section selected in the section treeview + Methods: + __init__(self, budget, code) + setFields(self) + setSection(self) + setText(self) + field_controlSelection(self, selection) + section_controlSelection(self, selection) + runMessage(self, message, nt, arg=None) + _clear(self) + getWidget(self) + {get/set}Path + {get/set}Page + getBudget(self) + getActviPathRecord(self) + """ + def __init__(self, budget, page, path, path_record=(0,)): + """def __init__(self, budget, page, path, path_record=(0,)) + + budget: the budget (base.obra object) + page: weak reference from Page instance which creates this class + path: the path position of the description in the page + path_record: the path of the active record + Creates an shows the scroledwindow that contain the description text + of the record to be showed in a pane. + """ + self.__budget = budget + self.__page = page + self.__path = path + self.__active_path_record = path_record + _budget = budget + + _main_box = gtk.VBox() + + self.__label = gtk.Label(utils.mapping(_("Sheet of Conditions of the "\ + "record $1"), (self.__budget.getCode( + self.__active_path_record),))) + self.__label.set_alignment(0, 0) + self.__label.show() + + _frame = gtk.Frame() + _frame.set_shadow_type(gtk.SHADOW_IN) + _frame_box = gtk.VBox() + _list_box = gtk.HBox() + + self.__field_liststore = gtk.ListStore(str, str) + self.__field_treeview = gtk.TreeView(self.__field_liststore) + _field_treeselection = self.__field_treeview.get_selection() + _field_treeselection.set_mode(gtk.SELECTION_SINGLE) + self.__field_selection = None + _field_treeselection.set_select_function( + self.field_controlSelection) + self.__field_treeview.show() + _fieldcode_cell = gtk.CellRendererText() + _field_column = gtk.TreeViewColumn(_("Field")) + _field_column.pack_start(_fieldcode_cell, False) + _field_cell = gtk.CellRendererText() + _field_column.pack_end(_field_cell, True) + _field_column.add_attribute(_fieldcode_cell, "text", 0) + _field_column.add_attribute(_field_cell, "text", 1) + self.__field_treeview.append_column(_field_column) + _field_scrollwindow = gtk.ScrolledWindow() + _field_scrollwindow.set_policy(gtk.POLICY_AUTOMATIC, + gtk.POLICY_AUTOMATIC) + _field_scrollwindow.add(self.__field_treeview) + _field_scrollwindow.show() + + self.__section_liststore = gtk.ListStore(str, str) + self.__section_treeview = gtk.TreeView(self.__section_liststore) + _section_treeselection = self.__section_treeview.get_selection() + _section_treeselection.set_mode(gtk.SELECTION_SINGLE) + self.__section_selection = None + _section_treeselection.set_select_function( + self.section_controlSelection) + self.__section_treeview.show() + _sectioncode_cell = gtk.CellRendererText() + _section_column = gtk.TreeViewColumn(_("Section")) + _section_column.pack_start(_sectioncode_cell, False) + _section_column.add_attribute(_sectioncode_cell, "text", 0) + _section_cell = gtk.CellRendererText() + _section_column.pack_end(_section_cell, True) + _section_column.add_attribute(_section_cell, "text", 1) + self.__section_treeview.append_column(_section_column) + _section_scrollwindow = gtk.ScrolledWindow() + _section_scrollwindow.set_policy(gtk.POLICY_AUTOMATIC, + gtk.POLICY_AUTOMATIC) + _section_scrollwindow.add(self.__section_treeview) + _section_scrollwindow.show() + + _list_box.pack_start(_field_scrollwindow, True, True, 5) + _list_box.pack_start(_section_scrollwindow, True, True, 5) + _list_box.show() + + _scrollwindow = gtk.ScrolledWindow() + _scrollwindow.set_policy(gtk.POLICY_AUTOMATIC, + gtk.POLICY_AUTOMATIC) + _textview = gtk.TextView() + _textview.set_wrap_mode(gtk.WRAP_WORD) + self.__textbuffer = _textview.get_buffer() + _textview.show() + _hbox = gtk.HBox() + _hbox.pack_start(_textview, True, True, 5) + _hbox.show() + + _frame_box.pack_start(self.__label, False, False, 5) + _frame_box.pack_start(_list_box, False, False, 5) + _frame_box.show() + _frame.add(_frame_box) + _frame.show() + _main_box.pack_start(_frame, False) + + + _vbox = gtk.VBox() + _vbox.pack_start(_hbox, True, True, 5) + _vbox.show() + _main_box.pack_start(_scrollwindow, True, True, 5) + _main_box.show() + + _scrollwindow.add_with_viewport(_vbox) + _scrollwindow.show() + self.__widget = _main_box + + self.setFields() + + def setFields(self): + """setFields(self) + + Set the fields items in the field treeview + """ + _record = self.__budget.getRecord(self.__budget.getCode( + self.__active_path_record)) + _sheet = _record.getSheet() + _field_list = _sheet.getFields() + self.__field_liststore.clear() + for _field in _field_list: + _field_text = self.__budget.getSheetField(_field) + _iter = self.__field_liststore.append([_field, _field_text]) + _treeselection = self.__field_treeview.get_selection() + _treeselection.select_path(0) + + def setSection(self): + """setSection(self) + + Set the section items in the section treeview + """ + self.__section_liststore.clear() + if not self.__field_selection is None: + _record = self.__budget.getRecord(self.__budget.getCode( + self.__active_path_record)) + _sheet = _record.getSheet() + _section_list = _sheet.getSections(self.__field_selection) + for _section in _section_list: + _section_text = self.__budget.getSheetSection(_section) + _iter = self.__section_liststore.append([_section, _section_text]) + _treeselection = self.__section_treeview.get_selection() + _treeselection.select_path(0) + + def setText(self): + """setText(self) + + Set the text in the textview + """ + if not self.__section_selection is None and\ + not self.__field_selection is None: + _record = self.__budget.getRecord(self.__budget.getCode( + self.__active_path_record)) + _sheet = _record.getSheet() + _paragraph_code = _sheet.getParagraph(self.__field_selection, + self.__section_selection) + _paragraph = self.__budget.getSheetParagraph(_paragraph_code) + self.__textbuffer.set_text(_paragraph) + else: + self.__textbuffer.set_text("") + + def field_controlSelection(self, selection): + """def controlSelection(self, selection) + + selection: treeselection + + Method connected to set_selection_function() in field treeview + This method is called before any node is selected or unselected, + giving some control over which nodes are selected. + The selection function should return TRUE if the state + of the node may be toggled, and FALSE if the state of the node should + be left unchanged. + + When a user select a row in the field treeview the section treeview is + reloaded to show the sections of this field and already the text sheet. + """ + _treeiter = self.__field_liststore.get_iter(selection) + self.__field_selection = self.__field_liststore.get_value(_treeiter, 0) + self.setSection() + return True + + def section_controlSelection(self, selection): + """def controlSelection(self, selection) + + selection: treeselection + + Method connected to set_selection_function() in sector treeview + This method is called before any node is selected or unselected, + giving some control over which nodes are selected. + The selection function should return TRUE if the state + of the node may be toggled, and FALSE if the state of the node should + be left unchanged. + + When a user select a row in the field treeview the text sheet for this + section in showed + """ + _treeiter = self.__section_liststore.get_iter(selection) + self.__section_selection = self.__section_liststore.get_value(_treeiter, 0) + self.setText() + return True + + def setActivePathRecord(self, path_record): + """def setActivePathRecord(self, path_record)) + + path_record: active path record + + Set the new path code to show its sheet of condition text. + """ + self.__field_selection = None + self.__field_liststore.clear() + self.__section_selection = None + self.__section_liststore.clear() + self.__textbuffer.set_text("") + _budget = self.__budget + self.__active_path_record = path_record + _code = _budget.getCode(self.__active_path_record) + self.__label.set_text(utils.mapping(_("Sheet2 of Conditions of the "\ + "record $1"), (_code,))) + self.setFields() + + def runMessage(self, message, path, arg=None): + """def runMessage(self, message, path, arg=None) + + message: the message type + "change_active": change the active record + "clear": clear instance + path: tuple that identifies the pane in the notebook page + arg: tuple whit two items: + 0: record path in the budget + 1: record code + This method receives a message and executes its corresponding action + """ + _budget = self.__budget + if message == "change_active": + if _budget.hasPath(arg): + self.setActivePathRecord(arg) + elif message == "clear": + self._clear() + + def _clear(self): + """def _clear(self) + + Deletes all the instance atributes + """ + del self.__page + del self.__widget + del self.__path + del self.__budget + del self.__active_code + del self.__textbuffer + del self.__label + del self.__textbuffer + del self.__label + del self.__field_liststore + del self.__field_treeview + del self.__field_selection + del self.__section_liststore + del self.__section_treeview + del self.__section_selection + + def getWidget(self): + """def getWidget(self) + + return the main widget (gtk.ScrolledWindow) + """ + return self.__widget + + def getPath(self): + """def getPath(self) + + return the tuple that identifies the pane in the notebook page + """ + return self.__page + + def setPath(self, path): + """def setPath(self) + + sets the tuple that identifies the pane in the notebook page + """ + self.__path = path + + def getPage(self): + """def getPage(self) + + return the weak reference from Page instance + """ + return self.__page + + def setPage(self, page): + """def setPage(self) + + set the weak reference from Page instance + """ + self.__page = page + + def getBudget(self): + """def getBudget(self) + + return the budget object + """ + return self.__budget + + def getActivePathRecord(self): + """def getActivePathRecord(self) + + return the Active Path Record + """ + return self.__active_path_record + + path = property(getPath, setPath, None, + "Path that identifie the item in the page notebook") + widget = property(getWidget, None, None, + "Lista de configuracion de vistas") + page = property(getPage, setPage, None, + "Weak reference from Page instance which creates this class") + budget = property(getBudget, None, None, + "Budget object") + active_path_record = property(getActivePathRecord, None, None, + "Active Path Record") + +class FileView(object): + """gui.FileView + + Description: + Class to show the file icons of a record in a pane + Constructor: + Description(budget, code) + budget: budget + code: code record + Ancestry: + +-- object + +-- Description + Atributes: + "widget": the main widget (gtk.ScrolledWindow object) + "__icon_box": the box that contains the icon + "path": the tuple that identifies the pane in the notebook page + "budget": The budget (base.obra objetc) + "active_code": The active code of the record + Methods: + __init__(self, budget, code) + setActiveCode(self, code) + runMessage(self, message, nt, arg=None) + _clear(self) + getWidget(self) + {get/set}Path + {get/seg}Page + getBudget(self) + getActviCode(self) + """ + + def __init__(self, budget, page, path, path_record=(0,)): + """def __init__(self, budget, page, path, path_record=(0,)) + + budget: the budget (base.obra object) + page: weak reference from Page instance which creates this class + path: the path position of the description in the page + path_record: the path of the active record + + Creates an shows the scroledwindow that contain icon files + of the record to be showed in a pane. + """ + self.__budget = budget + self.__page = page + self.__path = path + self.__active_path_record = path_record + self.__active_code = budget.getCode(self.__active_path_record) + _budget = budget + _record = self.__budget.getRecord(self.__budget.getCode( + self.__active_path_record)) + + self.__icon_box = self.getIconBox(_record) + _scrollwindow = gtk.ScrolledWindow() + _scrollwindow.set_policy(gtk.POLICY_ALWAYS, + gtk.POLICY_NEVER) + + self.__icon_box.show() + _scrollwindow.add_with_viewport(self.__icon_box) + _scrollwindow.show() + self.__widget = _scrollwindow + + def getIconBox(self, record): + """getIconBox(self, record) + + record: the active record object + + Creates and returns the box whith te icon files of the active record. + """ + ## TODO: add others filetypes: avi, pdf, ppt... + _files = record.getFiles() + _hbox = gtk.HBox() + _frame = gtk.Frame() + _frame.set_shadow_type(gtk.SHADOW_IN) + for _file in _files: + _path = os.path.dirname(self.__budget.filename) + _file_path = os.path.join(_path, _file.name) + _filetype = utils.getFiletype(_file_path) + _box = gtk.VBox() + if _filetype == "image": + _event_box = gtk.EventBox() + try: + _image_pixbuf = gtk.gdk.pixbuf_new_from_file(_file_path) + _image_pixbuf = _image_pixbuf.scale_simple(64, 64, + gtk.gdk.INTERP_BILINEAR) + except: + _image_pixbuf = gtk.gdk.pixbuf_new_from_file( + globals.getAppPath("IMAGE-ICON")) + _image_pixbuf = _image_pixbuf.scale_simple(64, 64, + gtk.gdk.INTERP_BILINEAR) + _image_icon = gtk.Image() + _image_icon.set_from_pixbuf(_image_pixbuf) + _image_icon.show() + _event_box.add(_image_icon) + _box.pack_start(_event_box, False, False, 5) + _event_box.connect("button-press-event", self.launchFile, + "image", _file_path) + _event_box.show() + + elif _filetype == "dxf": + _event_box = gtk.EventBox() + _dxf_icon = gtk.Image() + _dxf_pixbuf = gtk.gdk.pixbuf_new_from_file( + globals.getAppPath("DXF-ICON")) + _dxf_pixbuf = _dxf_pixbuf.scale_simple(64, 64, + gtk.gdk.INTERP_BILINEAR) + _dxf_icon.set_from_pixbuf(_dxf_pixbuf) + _dxf_icon.show() + _event_box.add(_dxf_icon) + _box.pack_start(_event_box, False, False, 5) + _event_box.connect("button-press-event", self.launchFile, + "dxf", _file_path) + _event_box.show() + _label_event_box = gtk.EventBox() + _label = gtk.Label(_file.name) + _label_event_box.add(_label) + _label_event_box.show() + _label.show() + _box.pack_start(_label_event_box, False, False, 5) + _box.show() + _hbox.pack_start(_box, False, False, 5) + _hbox.show() + _frame.add(_hbox) + return _frame + + def launchFile(self, widget, event, kind, file_path): + """launchFile(self, widget, event, kind, file_path) + + widget: the widget that emit the signal + event: the event that emit the signal + king: kind of file + file_path: the path file to be launch + + Launch the file if a double click emit the signal. + Method connected to "button-press-event" signal in images event box + """ + if event.type is gtk.gdk._2BUTTON_PRESS: + openwith.launch_file(kind, file_path) + + def setActivePathRecord(self, path_record): + """def setActivePathRecord(self, path_record)) + + path_record: active path record + Set the new path code to show its description text. + """ + _budget = self.__budget + self.__active_path_record = path_record + _code = _budget.getCode(self.__active_path_record) + _record = self.__budget.getRecord(_code) + self.__icon_box.destroy() + self.__icon_box = self.getIconBox(_record) + self.__icon_box.show() + self.__widget.add_with_viewport(self.__icon_box) + + def runMessage(self, message, path, arg=None): + """def runMessage(self, message, path, arg=None) + + message: the message type + "change_active": change the active record + "clear": clear instance + path: tuple that identifies the pane in the notebook page + arg: tuple whit two items: + 0: record path in the budget + 1: record code + This method receives a message and executes its corresponding action + """ + _budget = self.__budget + if message == "change_active": + if _budget.hasPath(arg): + self.setActivePathRecord(arg) + elif message == "clear": + self._clear() + + def _clear(self): + """def _clear(self) + + Delete all instance atributes + """ + del self.__hbox + del self.__widget + del self.__path + del self.__budget + del self.__active_code + + def getWidget(self): + """def getWidget(self) + + return the main widget (gtk.ScrolledWindow) + """ + return self.__widget + + def getPath(self): + """def getPath(self) + + return the tuple that identifies the pane in the notebook page + """ + return self.__path + + def setPath(self, path): + """def setPath(self) + + sets the tuple that identifies the pane in the notebook page + """ + self.__path = path + + def getPage(self): + """def getPage(self) + + return the weak reference from Page instance + """ + return self.__page + + def setPage(self, page): + """def setPage(self) + + set the weak reference from Page instance + """ + self.__page = page + + def getBudget(self): + """def getBudget(self) + + return the budget object + """ + return self.__budget + + def getActivePathRecord(self): + """def getActivePathRecord(self) + + return the Active Path Record + """ + return self.__active_path_record + + path = property(getPath, setPath, None, + "Path that identifie the item in the page notebook") + widget = property(getWidget, None, None, + "The main widget (gtk.ScrolledWindow)") + page = property(getPage, setPage, None, + "Weak reference from Page instance which creates this class") + budget = property(getBudget, None, None, + "Budget object") + active_path_record = property(getActivePathRecord, None, None, + "Active Path Record") + + +class TextWindow(object): + """gui.TextWindow + + Description: + Class to show a description text of a record in a new gtk window + Constructor: + TextWindow(code, text) + code: code of the record + text: description text of the record + Ancestry: + +-- object + +-- TextWindow + Atributes: + Methods: + __init__(self, code, text) + main(self) + destroy(self, widget) + """ + + def __init__(self, code, text): + """def __init__(self, code, text) + + code: code of the record + text: description text of the record + Creates an shows the window. + """ + _window = gtk.Window(gtk.WINDOW_TOPLEVEL) + _window.set_resizable(True) + _window.set_default_size(700, 300) + _window.set_title(utils.mapping(_("$1 text"), (code,))) + _window.set_border_width(0) + _box1 = gtk.VBox(False, 0) + _window.add(_box1) + _box1.show() + _sw = gtk.ScrolledWindow() + _sw.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC) + _textview = gtk.TextView() + _textview.set_wrap_mode(gtk.WRAP_WORD) + _textbuffer = _textview.get_buffer() + _sw.add(_textview) + _sw.show() + _textview.show() + _box1.pack_start(_sw) + _textbuffer.set_text(text) + _window.connect("destroy", self.destroy) + _window.show() + + def main(self): + """def main(self) + + Starts the GTK+ event processing loop. + """ + gtk.main() + + def destroy(self, widget): + """destroy(self, widget) + widget: the widget where the event is emitted from + Method connected to "destroy" signal of window widget + This signal is emited when the method connected to "delete_event" + signal returns True or when the program call the destroy() method of + the gtk.Window widget. + The window is closed and the GTK+ event processing loop is ended. + """ + gtk.main_quit() + +class CompanyView(object): + """gui.CompanyView: + + Description: + Class to show the company records of a budget + Constructor: + CompanyView(budget, page, path) + budget: budget showed ("base.Budget" object) + page: weak reference from Page instance which creates this class + path: tuple that represents the path of the List in the Page + Returns the newly created CompanyView instance + Ancestry: + +-- object + +-- CompanyView + Atributes: + "budget": Budget to show, base.obra instance. + "active_path_record" + "widget": Window that contains the main widget, + (gtk.ScrolledWindow) + "path": Pane page identifier + "page": weak reference from Page instance which creates this class + "__methond_message": Method to send messages to the page + Methods: + __init__(self, budget, page, path, path_record=(0,)) + runMessage(self, message, path, arg=None) + _clear(self) + getWidget(self) + {get/set}Path + {get/set}Page + getBudget(self) + getActivePathRecord(self) + """ + + def __init__(self, budget, page, path, path_record=(0,)): + """def __init__(self, budget, page, path, path_record=(0,)) + + budget: budget: budget showed ("base.Budget" object) + page: weak reference from Page instance which creates this class + path: tuple that represents the path of the List in the Page + path_record: path of the active record in the budget + + Sets the init atributes + Creates the + + """ + self.__selection = None + # Seting init args + if not isinstance(budget, base.Budget): + raise ValueError, _("Argument must be a Budget object") + self.__budget = budget + self.__page = page + self.__path = path + self.__active_path_record = path_record + # HVox + self.__hbox = gtk.HPaned() + self.__hbox.set_position(230) + # TreeStore + self.__treestore = gtk.TreeStore(str, str) + self.setTreeStoreValues() + # Select Treeview + self.__select_treeview = gtk.TreeView(self.__treestore) + self.__select_treeview.set_enable_search(False) + self.__select_treeview.set_reorderable(False) + self.__select_treeview.set_headers_visible(False) + self.__select_treeview.show() + # Scrolled_window + self.__scrolled_window = gtk.ScrolledWindow() + self.__scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, + gtk.POLICY_AUTOMATIC) + self.__scrolled_window.add(self.__select_treeview) + # colors + _text_color = gtk.gdk.color_parse(globals.color["TEXT"]) + _background_color = [ + gtk.gdk.color_parse(globals.color["UNEVEN"]), + gtk.gdk.color_parse(globals.color["EVEN"])] + self.__code_column = gtk.TreeViewColumn() + self.__code_column.set_clickable(True) + self.__code_column.set_fixed_width(200) + _code_cell = gtk.CellRendererText() + _code_cell.set_property('foreground-gdk', _text_color) + self.__code_column.pack_start(_code_cell, True) + self.__code_column.add_attribute(_code_cell, 'text', 0) + _summary_cell = gtk.CellRendererText() + _summary_cell.set_property('foreground-gdk', _text_color) + self.__code_column.pack_start(_summary_cell, True) + self.__code_column.add_attribute(_summary_cell, 'text', 1) + # Index column + self.__select_treeview.append_column(self.__code_column) + # control selection + self.__treeselection = self.__select_treeview.get_selection() + self.__treeselection.set_mode(gtk.SELECTION_SINGLE) + self.__treeselection.set_select_function(self.controlSelection) + # Show + self.setColumnsHeaders() + self.__scrolled_window.show() + # Option View + self.__option_View = OptionView("") + # Selection + self.__select_treeview.set_cursor((0,), None, False) + self.__select_treeview.grab_focus() + # + self.__hbox.add1(self.__scrolled_window) + self.__hbox.add2(self.__option_View.widget) + self.__hbox.show() + self.__widget = self.__hbox + + def setOptions(self, type): + if type == "company": + _options = [("code", _("Code"), "string", + _("""Code that define the company""")), + ("summary", _("Summary"), "string", + _("""Summary of the company name""")), + ("name", _("Name"), "string", + _("""Complete name""")), + ("cif", _("CIF"), "string", + _("""Fiscal identifier number""")), + ("web", _("Web"), "string", + _("""Company web page""")), + ("email", _("Email"), "string", + _("""Company email""")), + ] + self.__option_View.setOptions(_options) + elif type == "office": + _options = [("type", _("Type"), "string", + _("""Type of Office: + C: Central office + D: Local office + R: Performer""")), + ("subname", _("Name"), "string", + _("Office name")), + ("address", _("Address"), "string",""), + ("postal code", _("Postal code"), "string",""), + ("town", _("Town"), "string",""), + ("province", _("Province"), "string",""), + ("country", _("Country"), "string",""), + ("phone", _("Phone"), "list", + _("Phone numbers of the office")), + ("fax", _("Fax"), "list", + _("Fax numbers of the office")), + ("contact person", _("Contact person"), "string", + _("Contact persons in the office")), + ] + self.__option_View.setOptions(_options) + else: + print _("Unknow Option Type") + def setOptionValues(self, company_key): + self.__option_View.setValues(_values) + + def setTreeStoreValues(self): + """def setListstoreValues(self) + + Sets the treestore values from the budget + """ + _budget = self.__budget + _company_keys = _budget.getCompanyKeys() + for _company_key in _company_keys: + _company = _budget.getCompany(_company_key) + _values = [_company_key, _company.summary] + _treeiter = self.__treestore.append(None, _values) + _offices = _company.offices + for _office in _offices: + # TODO: Test offices + _values = [_office.officeType, _office.subname] + self.__treestore.append(_treeiter, _values) + + def setColumnsHeaders(self): + """def setColumnsHeaders(self) + + Sets the headers column values + """ + #self.columns[1].set_title(_("Type")) # Σ parcial Σ total + #self.columns[2].set_title(_("Comment")) + #self.columns[3].set_title(_("N")) + #self.columns[4].set_title(_("Length")) + #self.columns[5].set_title(_("Width")) + #self.columns[6].set_title(_("Height")) + pass + + def controlSelection(self, selection): + """def controlSelection(self, selection) + + selection: selection + + Method connected to set_selection_function() + This method is called before any node is selected or unselected, + giving some control over which nodes are selected. + The selection function should return TRUE if the state + of the node may be toggled, and FALSE if the state of the node should + be left unchanged. + + The selection changes the company/office in the option treeview + """ + if len(selection) == 1: + # The selection is a company + _company_key = self.__treestore[selection][0] + _company = self.__budget.getCompany(_company_key) + _selection = "company" + _values = _company.values + else: + # The selection is a office + _company_key = self.__treestore[selection[:1]][0] + _company = self.__budget.getCompany(_company_key) + _selection = "office" + _office = _company.offices[selection[1]] + _values = _office.values + if not self.__selection == _selection: + self.__selection = _selection + self.setOptions(_selection) + self.__option_View.setValues(_values) + + return True + + def showMessageRecord(self, record_path): + """def showMessageRecord(self, record_path) + + record_path: the path of the record to show + Method connected to "change_active" message + Show the record especified in the "change_active" message + """ + self.__active_path_record = record_path + + def runMessage(self, message, path, arg=None): + """def runMessage(self, message, path, arg=None) + + message: the message type + "change_active": change the active record + "clear": clear instance + path: tuple that identifies the pane in the notebook page + arg: tuple whit two items: + 0: record path in the budget + 1: record code + This method receives a message and executes its corresponding action + """ + _budget = self.__budget + if message == "change_active": + if _budget.hasPath(arg): + _path_record = arg + self.showMessageRecord( _path_record) + pass + elif message == "clear": + self._clear() + + def colorCell(self, column, cell_renderer, tree_model, iter, lcolor): + """def colorCell(self, column, cell_renderer, tree_model, iter, lcolor) + + column: the gtk.TreeViewColumn in the treeview + cell_renderer: a gtk.CellRenderer + tree_model: the gtk.TreeModel + iter: gtk.TreeIter pointing at the row + lcolor: list with 2 gtk colors for even and uneven record + + Method connected to "set_cell_data_func" of many column + The set_cell_data_func() method sets the data function (or method) + to use for the column gtk.CellRenderer specified by cell_renderer. + This function (or method) is used instead of the standard attribute + mappings for setting the column values, and should set the attributes + of the cell renderer as appropriate. func may be None to remove the + current data function. The signature of func is: + -def celldatafunction(column, cell, model, iter, user_data) + -def celldatamethod(self, column, cell, model, iter, user_data) + where column is the gtk.TreeViewColumn in the treeview, cell is the + gtk.CellRenderer for column, model is the gtk.TreeModel for the + treeview and iter is the gtk.TreeIter pointing at the row. + + The method sets cell background color for all columns + and text for index and amount columns. + """ + _row_path = tree_model.get_path(iter) + _number = _row_path[-1] + if column is self.__index_column: + cell_renderer.set_property('text', str(_number + 1)) + self.__index_column.get_cell_renderers()[1].set_property( + 'cell-background-gdk', lcolor[_number % 2]) + if self.__treeview.get_cursor() == (_row_path,column): + cell_renderer.set_property('cell-background-gdk', + gtk.gdk.color_parse(globals.color["ACTIVE"])) + else: + cell_renderer.set_property('cell-background-gdk', + lcolor[_number % 2]) + + def _clear(self): + """def _clear(self) + + it deletes the __budget value + this would not be necessary if there were not circular references, + which are pending to fix + """ + del self.__budget + + def getWidget(self): + """def getWidget(self) + + return the main widget (gtk.ScrolledWindow) + """ + return self.__widget + + def getPath(self): + """def getPath(self) + + return the tuple that identifies the pane in the notebook page + """ + return self.__path + + def setPath(self, path): + """def setPath(self) + + sets the tuple that identifies the pane in the notebook page + """ + self.__path = path + def getPage(self): + """def getPage(self) + + return the Page + """ + return self.__page + + def setPage(self,page): + """def setPage(self) + + set the Page + """ + self.__page = page + + def getBudget(self): + """def getBudget(self) + + return the Budget objet + """ + return self.__budget + + def getActivePathRecord(self): + """def getActivePathRecord(self) + + return the Active Path Record + """ + return self.__active_path_record + + active_path_record = property(getActivePathRecord, None, None, + "Active path record") + widget = property(getWidget, None, None, + "main widget") + path = property(getPath, setPath, None, + "Path that identifies the item in the page notebook") + page = property(getPage, setPage, None, + "Weak reference from Page instance which creates this class") + budget = property(getBudget, None, None, + "Budget object") + + +class OptionView(object): + """gui.OptionView: + + Description: + It creates a treeview whith the column "Option Name" "Value" + and "Type" to show and edit Options + Constructor: + OptionView(option_list) + option_list: list of options + (option_name, type) + Ancestry: + +-- object + +-- OptionView + Atributes: + "__liststore" + "__treeview" + "__option_column" + "__value_column" + "__type_column" + "__treeselection" + "__widget": Main windget + "__option_list" + "__option_dict" + "__description_label" + "option_types" + "widget": __widget + Methods: + __init__(self, option_list) + createColumn(self, args) + createTextBaseColumn(self,args) + createBaseColumn(self,args) + """ + + def __init__(self, option_list): + """__init__(self, option_list) + + """ + self.__option_dict = {} + self.__option_list = [] + self.option_types = {"boolean" : _("Boolean"), + "integer": _("Integer"), + "string": _("Text"), + "color" : _("Color"), + "list" : _("List")} + # ListStore + self.__liststore = gtk.ListStore(str, str, str, str, str) + # Treeview + self.__treeview = gtk.TreeView(self.__liststore) + self.__treeview.set_enable_search(False) + self.__treeview.set_reorderable(False) + self.__treeview.set_headers_clickable(False) + # vbox + _vbox = gtk.VBox() + # Scrolled_window + _scrolled_window = gtk.ScrolledWindow() + _scrolled_window.set_policy(gtk.POLICY_AUTOMATIC, + gtk.POLICY_AUTOMATIC) + _scrolled_window.add(self.__treeview) + _scrolled_window.show() + _vbox.pack_start(_scrolled_window) + # colors + _text_color = gtk.gdk.color_parse(globals.color["TEXT"]) + _background_color = [ + gtk.gdk.color_parse(globals.color["UNEVEN"]), + gtk.gdk.color_parse(globals.color["EVEN"])] + # Option Column + self.__option_column = gtk.TreeViewColumn() + self.__option_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) + self.__option_column.set_fixed_width(150) + self.__option_column.set_resizable(True) + _option_cell = gtk.CellRendererText() + _option_cell.set_property('foreground-gdk', _text_color) + self.__option_column.pack_start(_option_cell, True) + self.__option_column.set_cell_data_func(_option_cell, self.colorCell, + _background_color) + self.__option_column.set_title(_("Option name")) + self.__option_column.add_attribute(_option_cell, 'text', 1) + self.__treeview.append_column(self.__option_column) + # Value Column + self.__value_column = gtk.TreeViewColumn() + self.__value_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) + self.__value_column.set_fixed_width(275) + self.__value_column.set_resizable(True) + _value_cell = gtk.CellRendererText() + _value_cell.set_property('foreground-gdk', _text_color) + self.__value_column.pack_start(_value_cell, True) + self.__value_column.set_cell_data_func(_value_cell, self.colorCell, + _background_color) + self.__value_column.set_title(_("Value")) + self.__value_column.add_attribute(_value_cell, 'text', 2) + self.__treeview.append_column(self.__value_column) + # Type Column + self.__type_column = gtk.TreeViewColumn() + self.__type_column.set_sizing(gtk.TREE_VIEW_COLUMN_FIXED) + self.__type_column.set_fixed_width(70) + self.__type_column.set_resizable(True) + _type_cell = gtk.CellRendererText() + _type_cell.set_property('foreground-gdk', _text_color) + self.__type_column.pack_start(_type_cell, True) + self.__type_column.set_cell_data_func(_type_cell, self.colorCell, + _background_color) + self.__type_column.set_title(_("Type")) + self.__treeview.append_column(self.__type_column) + # End Column + _end_column = gtk.TreeViewColumn() + _end_column.set_clickable(False) + _end_cell = gtk.CellRendererText() + _end_cell.set_property('cell-background-gdk', + gtk.gdk.color_parse(globals.color["UNEVEN"])) + _end_column.pack_start(_end_cell, True) + self.__treeview.append_column(_end_column) + # Connect + self.__treeview.connect("key-press-event", self.treeviewKeyPressEvent) + self.__treeview.connect("button-press-event", self.treeviewClickedEvent) + # control selection + self.__treeselection = self.__treeview.get_selection() + self.__treeselection.set_mode(gtk.SELECTION_MULTIPLE) + self.__treeselection.set_select_function(self.controlSelection) + # labels + _frame = gtk.Frame() + _frame.set_shadow_type(gtk.SHADOW_OUT) + _vbox2 = gtk.VBox() + _frame.add(_vbox2) + _alignement = gtk.Alignment(xalign=0, yalign=0, xscale=0, yscale=0) + _alignement.set_padding(0, 0, 12, 0) + _label = gtk.Label() + _label.set_markup("" + _("Description:") + "") + _label.show() + _alignement.add(_label) + _alignement.show() + _alignement2 = gtk.Alignment(xalign=0, yalign=0, xscale=0, yscale=0) + _alignement2.set_padding(0, 0, 24, 0) + self.__description_label = gtk.Label() + self.__description_label.show() + _alignement2.add(self.__description_label) + _alignement2.show() + _vbox2.pack_start(_alignement, False) + _vbox2.pack_start(_alignement2, False) + _vbox2.show() + _frame.show() + _vbox.pack_start(_frame, False) + # Show + self.__treeview.show() + _vbox.show() + self.__widget = _vbox + + def treeviewKeyPressEvent(self, widget, event): + """def treeviewKeyPressEvent(self, widget, event) + + widget: treewiew widget + event: Key Press event + Method connected to "key-press-event" signal + The "key-press-event" signal is emitted when the user presses a key + on the keyboard. + Returns :TRUE to stop other handlers from being invoked for the event. + Returns :FALSE to propagate the event further. + + If the user press the right cursor button and the cursor is in the + value column or pres the left cursor button and the cursor is + in the value column the event is estoped, else the event is propagated. + """ + (_cursor_path, _column) = self.__treeview.get_cursor() + if (event.keyval == gtk.keysyms.Right \ + and _column == self.__value_column) \ + or (event.keyval == gtk.keysyms.Left \ + and _column == self.__value_column): + return True + else: + _description = self.__liststore[_cursor_path][4] + self.__description_label.set_text(_description) + return False + + def treeviewClickedEvent(self, widget, event): + """def treeviewClickedEvent(self, widget, event) + + widget: treewiew widget + event: clicked event + Method connected to "button-press-event" signal + The "button-press-event" signal is emitted when a mouse button is + pressed. + Returns TRUE to stop other handlers from being invoked for the event. + Returns FALSE to propagate the event further. + + The cursos is moved to value column. + """ + path_at_pos = self.__treeview.get_path_at_pos(int(event.x), + int(event.y)) + if not path_at_pos is None: + _path_cursor, _column, _x, _y = path_at_pos + _description = self.__liststore[_path_cursor][4] + self.__description_label.set_text(_description) + if _column == self.__value_column: + return False + else: + self.__treeview.set_cursor(_path_cursor,self.__value_column, + True) + self.__treeview.grab_focus() + return True + return True + + def controlSelection(self, selection): + """def controlSelection(self, selection) + + selection: treeselection + + Method connected to set_selection_function() + This method is called before any node is selected or unselected, + giving some control over which nodes are selected. + The selection function should return TRUE if the state + of the node may be toggled, and FALSE if the state of the node should + be left unchanged. + + Return False so none row is selected + """ + return False + + + def colorCell(self, column, cell_renderer, tree_model, iter, lcolor): + """def colorCell(self, column, cell_renderer, tree_model, iter, lcolor) + + column: the gtk.TreeViewColumn in the treeview + cell_renderer: a gtk.CellRenderer + tree_model: the gtk.TreeModel + iter: gtk.TreeIter pointing at the row + lcolor: list with 2 gtk colors for even and uneven record + + Method connected to "set_cell_data_func" of the column + The set_cell_data_func() method sets the data function (or method) + to use for the column gtk.CellRenderer specified by cell_renderer. + This function (or method) is used instead of the standard attribute + mappings for setting the column values, and should set the attributes + of the cell renderer as appropriate. func may be None to remove the + current data function. The signature of func is: + -def celldatafunction(column, cell, model, iter, user_data) + -def celldatamethod(self, column, cell, model, iter, user_data) + where column is the gtk.TreeViewColumn in the treeview, cell is the + gtk.CellRenderer for column, model is the gtk.TreeModel for the + treeview and iter is the gtk.TreeIter pointing at the row. + + The method sets cell background color for all columns + and text for type column. + """ + _row_path = tree_model.get_path(iter) + _number = _row_path[-1] + if self.__treeview.get_cursor() == (_row_path,column): + cell_renderer.set_property('cell-background-gdk', + gtk.gdk.color_parse(globals.color["ACTIVE"])) + else: + cell_renderer.set_property('cell-background-gdk', + lcolor[_number % 2]) + if column is self.__type_column: + _type = self.option_types[tree_model[_row_path][3]] + cell_renderer.set_property('text', _type) + + def setOptions(self, option_list): + """setOptions(self, option_list) + + option_list: list of tuples + (option, option name, type) + option: option identifier + option name: a string with the option name + Description: a string with the option description + type: can be "boolean" + "integer" + "string" + "color" + Sets the Options in the treeview rows + """ + self.__option_dict = {} + self.__option_list = [] + self.__liststore.clear() + if isinstance(option_list, list): + for _option in option_list: + if isinstance(_option, tuple) and len(_option) == 4: + _option_key = _option[0] + _option_name = _option[1] + _option_type = _option[2] + _option_description = _option[3] + if isinstance(_option_key, str) and \ + (isinstance(_option_name, str) or\ + isinstance(_option_name, unicode))and \ + _option_type in self.option_types.keys(): + self.__liststore.append([_option_key, _option_name, "", + _option_type, _option_description]) + self.__option_dict[_option_key] = [_option_name, "", + _option_type, _option_description] + self.__option_list.append(_option_key) + else: + print _("Option values must be strings") + else: + print _("Option must be a tuple with 4 items") + else: + print _("Option list must be a list") + return + def setValues(self, values): + """setValues(self, values) + + values: dictionary {option : value} + + Sets the Options values + """ + if isinstance(values, dict): + for _option, _value in values.iteritems(): + if _option in self.__option_dict: + _type = self.__option_dict[_option][2] + if _type == "boolean": + if isinstance(_value, bool): + _num = self.__option_list.index(_option) + _iter = self.__liststore.get_iter((_num,)) + self.__liststore.set_value(_iter, 2, _value) + self.__option_dict[_option][1] = _value + else: + print _("Icorrect type, must be boolean") + elif _type == "integer": + try: + _value = int(_value) + except ValueError: + print _("Icorrect type, must be integer") + else: + _num = self.__option_list.index(_option) + _iter = self.__liststore.get_iter((_num,)) + self.__liststore.set_value(_iter, 2, _value) + self.__option_dict[_option][1] = _value + elif _type == "string": + if isinstance(_value, str): + _num = self.__option_list.index(_option) + _iter = self.__liststore.get_iter((_num,)) + self.__liststore.set_value(_iter, 2, _value) + self.__option_dict[_option][1] = _value + else: + print _("Icorrect type, must be string") + elif _type == "list": + if isinstance(_value, list): + _num = self.__option_list.index(_option) + _iter = self.__liststore.get_iter((_num,)) + _str_value = "" + for _item_value in _value: + _str_value = _str_value + _item_value + "," + if _str_value[-1] == ",": + _str_value = _str_value[:-1] + self.__liststore.set_value(_iter, 2, _str_value) + self.__option_dict[_option][1] = _value + else: + print _("Icorrect type, must be list") + elif _type == "color": + if isinstance(_value, str): + try: + _color = gtk.gdk.color_parse(_value) + except ValueError: + print _("Icorrect type, must be a parseable " \ + "color") + else: + _num = self.__option_list.index(_option) + _iter = self.__liststore.get_iter((_num,)) + self.__liststore.set_value(_iter, 2, _value) + self.__option_dict[_option][1] = _value + else: + print _("Type must be boolean, integer, string or "\ + "color") + else: + print _("Value must be in the option dict") + else: + print _("Values must be a dict") + self.__treeview.set_cursor((0),self.__value_column, False) + self.__treeview.grab_focus() + (_cursor_path, _column) = self.__treeview.get_cursor() + _description = self.__liststore[_cursor_path][4] + self.__description_label.set_text(_description) + + def getWidget(self): + """def getWidget(self) + + return the main widget (gtk.ScrolledWindow) + """ + return self.__widget + widget = property(getWidget, None, None, + "The main widget (gtk.ScrolledWindow)") + + +class OpenDurusDatabase(object): + """gui.OpenFiebdc + + Description: + Class to show a dialog to open a durus price database + Constructor: + OpenDurusDatabase() + Ancestry: + +-- object + +-- OpenDurusDatabase + Atributes: + "_openMethod": The method to open the file + "__file": the filename to open + "__window": the File Selection window + Methods: + __init__(self) + setActiveCode(self, code) + """ + def __init__(self, openMethod): + self.__openMethod = openMethod + self.__file = None + self.__window = gtk.FileSelection("Abrir Archivo") + self.__window.connect("destroy", self.destroy) + self.__window.ok_button.connect("clicked", self._openFile) + self.__window.cancel_button.connect("clicked", + lambda w: self.__window.destroy()) + self.__window.set_filename(globals.getHomePath("DURUS-DATABASE")) + self.__window.show() + + def destroy(self, widget): + gtk.main_quit() + + def main(self): + gtk.main() + + def _openFile(self, w): + self.__file = self.__window.get_filename() + _filename = os.path.basename(self.__file) + _filename_list = _filename.split(".") + _filename_ext = _filename_list[-1] + if _filename == "": + print _("No file selected") + elif len(_filename_list) < 2 or _filename_ext != "durus": + print _("The filename must have durus extension") + else: + try: + self.__openMethod(self.__window.get_filename()) + self.__window.destroy() + except IOError: + print utils.mapping("IOError, $1", (self.__file,)) diff -r a1703c4f2990 -r 2ac1551ad2ab Gtk/importFiebdc.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Gtk/importFiebdc.py Sun Oct 31 20:07:33 2010 +0100 @@ -0,0 +1,456 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +## File importFiebdc.py +## This file is part of pyArq-Presupuestos. +## +## Copyright (C) 2010 Miguel Ángel Bárcena Rodríguez +## +## +## pyArq-Presupuestos is free software: you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## pyArq-Presupuestos is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program. If not, see . + +# Modules +import sys +import time +import os.path +import pygtk +pygtk.require('2.0') +import gtk +import gobject +import threading +gobject.threads_init() + +from Generic import utils +from Generic import globals +import gui + +class FileSelectionWindow(object): + """importFiebdc.FileSelectionWindow: + + Description: + Class to show the selection file window + Constructor: + importFiebdc.FileSelectionWindow(mainWindow, readFileMethod, + budget, arg_List, cancelMethod) + Ancestry: + +-- object + +-- FileSelectionWindow + Atributes: + "__mainWindow": gui.MainWindow object + "__readFileMethod": Method to read the selected file + "__budget": Budget object + "__filename": "file" + "__cancelMethod": Method to cancel the read method + "__file": The selected file + "__window": The selection file window + Methods: + __init__(self, mainWindow, readFileMethod, budget + arg_List, cancelMethod) + destroy(self, widget) + main(self) + _launchProgressWindow(self, _file) + _openFile(self, w) + _openFile2(Self, filename) + """ + + def __init__(self, mainWindow, readFileMethod, budget, filename, + cancelMethod): + """def __init__(self, mainWindow, readFileMethod, budget, + filename, cancelMethod) + + mainWindow: gui.MainWindow object + readFileMethod: Method to read the selected file + budget: base.Budget object + filename: "file" + cancelMethod: Method to cancel the read method + Sets the init atributes, creates the file selection window + Connects the events: + * clicked ok button: _openFile + * clicked cancel button: destroy window + * destroy event: _destroy + """ + # TODO: Add file filter + self.__mainWindow = mainWindow + self.__readFileMethod = readFileMethod + self.__budget = budget + self.__filename = filename + self.__cancelMethod = cancelMethod + self.__file = None + self.__window = gtk.FileChooserDialog(title=_("Open File"), + action=gtk.FILE_CHOOSER_ACTION_OPEN, + buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL, + gtk.STOCK_OPEN,gtk.RESPONSE_OK)) + self.__window.set_default_response(gtk.RESPONSE_OK) + self.__window.set_filename(globals.getHomePath("BUDGET")) + if self.__window.run() == gtk.RESPONSE_OK: + self._openFile(self.__window.get_filename()) + self.__window.destroy() + + def _launchProgressWindow(self, file): + """def _launchProgressWindow(self, file) + + Launch the progress window + """ + self.__filename = file + + _emptyPage = gui.EmptyPage(self.__mainWindow, self.__readFileMethod, + self.__budget, self.__filename, + self.__cancelMethod) + self.__mainWindow.getNotebook().append_page(_emptyPage.widget, + _emptyPage.title) + self.__mainWindow.getPageList().append(_emptyPage) + _emptyPage.run() + + def _launchProgressTab(self, file): + """def _launchProgressTab(self, file) + + Launch the progress window + """ + self.__filename = file + print "abriendo fichero", self.__filename + + def _openFile(self, filename): + """def _openFile(self, filename) + + filename: the filename to open + If the selected file has a bc3 extension + _launchProgressWindow is called + """ + _file = filename + if sys.getfilesystemencoding(): + _file = _file.decode(sys.getfilesystemencoding()) + #-# + _file = _file.encode("utf-8") + #-# + self.__file = _file + _filename = os.path.basename(self.__file) + _filename_ext = _filename.split(".")[-1] + if _filename_ext != "bc3" and _filename_ext != "BC3": + print _("The file must have 'bc3' extension") + else: + self.__window.destroy() + if self.__file: + # TODO: the file exits? is it not binary?, can it be readed? + self._launchProgressWindow(self.__file) + + + +class ProgressWindow(object): + """importFiebdc.ProgressWindow: + + Description: + Class to show the progress window and launch a thread to read + the database file + Constructor: + importFiebdc.ProgressWindow(mainWindow, readFileMethod, budget, + filename, cancelMethod) + Ancestry: + +-- object + +-- ProgressWindow + Atributes: + "__mainWindow": + "__readFileMethod": + "__budget": + "__filename": + "__cancelMethod": + "__children": Thread instance + "__cancel": list with boolean values + "__window": progress window widget + "__progress_bar": probres bar widget + "__label": label widget + Methods: + closeWindow(self) + __init__(self, mainWindow, readFileMethod, budget + filename, cancelMethod) + closeWindow(self) + main(self) + _autoClose(self) + _updateLabel(self, _time) + _updateProgressBar(self) + _launchTimeout(self) + _launchChildren(self, mainWindow, readFileMethod, budget + filename, cancelMethod) + _cancelChildren(self,widget=None) + _destroy(self, widget) + _delete_event(self, widget, event) + + """ + def __init__(self, mainWindow, readFileMethod, budget, + filename, cancelMethod): + """def __init__(self, mainWindow, readFileMethod, budget + filename, cancelMethod) + + mainWindow: gui.MainWindow object + readFileMethod: Method to read the selected file + budget: base.Budget object + filenamer: "file" + cancelMethod: Method to cancel the read method + Sets the init atributes, creates the progress window + Connects the events: + * destroy signal: self._destroy + * delete_event signal: self._delete_event + * clicked cancel button: self._cancelChildren + """ + self.__mainWindow = mainWindow + self.__readFileMethod = readFileMethod + self.__filename = filename + self.__budget = budget + self.__cancelMethod = cancelMethod + self.__children = None + self.__cancel = [False, False] + self.__window = gtk.Window() + self.__window.set_title(_("Loading file ...")) + self.__window.connect("destroy", self._destroy) + self.__window.connect("delete_event", self._delete_event) + _Vbox1 = gtk.VBox(False, 0) + self.__window.add(_Vbox1) + _align = gtk.Alignment(0.5, 0.5, 0, 0) + _align.show() + _Vbox1.pack_start(_align, False, False, 5) + self.__progress_bar = gtk.ProgressBar() + self.__progress_bar.show() + _align.add(self.__progress_bar) + self.__label = gtk.Label() + self.__label.set_text(_("Time: 0s")) + self.__label.show() + _Vbox1.add(self.__label) + self.__throbber = gtk.Image() + self.__throbber.set_from_file(globals.getAppPath("THROBBER-ICON")) + _Vbox1.add(self.__throbber) + self.__throbber.show() + self.__animation = gtk.gdk.PixbufAnimation(globals.getAppPath("THROBBER-GIF")) + _pixbuf = self.__throbber.get_pixbuf() + self.__throbber.set_from_animation(self.__animation) + _Hbox1 = gtk.HBox(False, 0) + _Vbox1.add(_Hbox1) + _button1 = gtk.Button(_("Cancel")) + _button1.connect("clicked", self._cancelChildren) + _button1.show() + _Hbox1.pack_start(_button1, True, True, 0) + _Hbox1.show() + _Vbox1.show() + + def main(self): + """def main(self) + + Launch the thread + Launch the timeouts + Shows window and starts the GTK+ event processing loop. + """ + + self._launchChildren() + self._launchTimeout() + self.__window.show() + gtk.main() + + def closeWindow(self): + """def closeWindow(self) + + Sets the __children atribute to None + This causes that the timiouts is ended and then the window is + closed. + This method is called from thread when it is finished + """ + self.__children = None + + def _launchTimeout(self): + """def _launchTimeout(self) + + Launch the timeouts: + 1- update progress bar + 2- update time labal + 3- If the other timetouts are stoped the window is closed + """ + gobject.timeout_add(100, self._updateProgressBar) + gobject.timeout_add(1000, self._updateLabel, time.time()) + self.__cancel = [False, False] + gobject.timeout_add(1000, self._autoClose) + + def _updateProgressBar(self): + """def _updateProgressBar(self) + + update progress bar in a timeout + If the thread end or is canceled the timeout is stoped + """ + if self.__children is None or self.__children.isCanceled() == True: + self.__cancel[0] = True + return False + else: + self.__progress_bar.pulse() + return True + + def _updateLabel(self, _time): + """def _updateProgressBar(self) + + update time label in a timeout + If the thread end or is canceled the timeout is stoped + """ + if self.__children is None or self.__children.isCanceled() == True: + self.__cancel[1] = True + return False + else: + _time = time.time() - _time + self.__label.set_text(utils.mapping(_("Time: $1"), + ("%.0f" %_time,))) + return True + + def _autoClose(self): + """def _updateProgressBar(self) + + If the time label and progress bar timeouts are stoped the window is + closed and ist tiemeout is stoped + """ + if self.__cancel == [ True, True ]: + self.__window.destroy() + return False + else: + return True + + def _launchChildren(self): + """_launchChildren(self) + + Launch the thread to read the file + """ + if self.__children is None: + self.__children = Thread(self, self.__mainWindow, + self.__readFileMethod, self.__budget, self.__filename, + self.__cancelMethod) + self.__children.start() + + def _cancelChildren(self,widget=None): + """_cancelChildren(self,widget=None) + + Method connected to "clicked" singal of cancel button + Stops the thread and close the window + """ + if self.__children: + self.__children.cancel() + self.__window.destroy() + + def _delete_event(self, widget, event): + """_delete_event(self, widget, event) + + widget: the widget where the event is emitted from + event: the "gtk.gdk.Event" + Method connected to "delete_event" signal of window widget + This signal is emitted when a user press the close titlebar button. + Stops the thread if exits. + Returns True so the signal "destroy" is emitted. + """ + if self.__children: + self._cancelChildren() + return True + + def _destroy(self, widget): + """_destroy(self, widget) + + widget: the widget where the event is emitted from + Method connected to "destroy" signal of window widget + This signal is emited when the method connected to "delete_event" + signal returns True or when the program call the destroy() method of + the gtk.Window widget. + The window is closed and the GTK+ event processing loop is ended. + """ + gtk.main_quit() + +class Thread(threading.Thread): + """importFiebdc.Thread: + + Description: + Thread class to read a file without freeze the gui + Constructor: + importFiebdc.Thread(page, mainWindow, + readFileMethod, arg_tuple, cancelMethod) + Ancestry: + +--threading.Thread + +-- importFiebdc.Thread + Atributes: + "__page": The page instanca that launch the thread + "__mainWindow": gui.MainWindow instance + "__readFileMethod": Method to read the selected file + "__arg_tuple": ("file", base.Budget()) + "__cancelMethod": Method to cancel the read method + "__cancel": Boolean value, True: the thread is stoped + Methods: + __init__(self, page, mainWindow, + readFileMethod, arg_tuple, cancelMethod) + run(self) + cancel(self) + isCanceled(self) + """ + + def __init__(self, page, mainWindow, + readFileMethod, budget, filename, cancelMethod): + """def __init__(self, page, mainWindow, + readFileMethod, budget, filename, cancelMethod) + + page: The page instance that launch the thread + mainWindow: gui.Mainwindow object + readFileMethod: Method to read the selected file + budget: base.Budget object + filename: "file" + cancelMethod: Method to cancel the read method + Sets the instance atributes. + """ + super(Thread, self).__init__() + self.__page = page + self.__mainWindow = mainWindow + self.__readFileMethod = readFileMethod + self.__budget = budget + self.__filename = filename + self.__cancelMethod = cancelMethod + self.__cancel = False + + def run(self): + """run(self) + + + """ + _result = self.__readFileMethod(self.__budget, self.__filename, + self.__page) + if _result is None: + self.__page.threadCanceled() + else: + _mainWindow = self.__mainWindow + _mainWindow._addBudget(_result) + self.__page.closeWindow() + self.clear() + + def cancel(self): + """cancel(self) + + Sets the "__cancel" atribute to True and call "__cancelMethod" to stop + read the file + """ + self.__cancel = True + self.__cancelMethod() + + def isCanceled(self): + """isCanceled(self) + + Return True if the thread has been canceled + """ + return self.__cancel + + def clear(self): + del self.__page + del self.__mainWindow + del self.__readFileMethod + del self.__budget + del self.__filename + del self.__cancelMethod + del self.__cancel + + + + diff -r a1703c4f2990 -r 2ac1551ad2ab __init__.py --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/__init__.py Sun Oct 31 20:07:33 2010 +0100 @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# +## This file is part of pyArq-Presupuestos. +## +## Copyright (C) 2010 Miguel Ángel Bárcena Rodríguez +## +# +# this file is needed for Python's import mechanism +# + diff -r a1703c4f2990 -r 2ac1551ad2ab images/acumulatedline.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/images/acumulatedline.svg Sun Oct 31 20:07:33 2010 +0100 @@ -0,0 +1,273 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + Σ + a + + diff -r a1703c4f2990 -r 2ac1551ad2ab images/arrow.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/images/arrow.svg Sun Oct 31 20:07:33 2010 +0100 @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + diff -r a1703c4f2990 -r 2ac1551ad2ab images/budget.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/images/budget.svg Sun Oct 31 20:07:33 2010 +0100 @@ -0,0 +1,633 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + Spreadsheet + + + Jakub Steiner + + + http://jimmac.musichall.cz + + + spreadheet + document + office + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r a1703c4f2990 -r 2ac1551ad2ab images/calculate.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/images/calculate.svg Sun Oct 31 20:07:33 2010 +0100 @@ -0,0 +1,367 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + 13.5 + + + + + + + + + + + + + + diff -r a1703c4f2990 -r 2ac1551ad2ab images/calculatedline.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/images/calculatedline.svg Sun Oct 31 20:07:33 2010 +0100 @@ -0,0 +1,274 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + ƒ + (x) + + diff -r a1703c4f2990 -r 2ac1551ad2ab images/chapter.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/images/chapter.svg Sun Oct 31 20:07:33 2010 +0100 @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + diff -r a1703c4f2990 -r 2ac1551ad2ab images/close.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/images/close.svg Sun Oct 31 20:07:33 2010 +0100 @@ -0,0 +1,81 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + + diff -r a1703c4f2990 -r 2ac1551ad2ab images/connected.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/images/connected.svg Sun Oct 31 20:07:33 2010 +0100 @@ -0,0 +1,285 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + diff -r a1703c4f2990 -r 2ac1551ad2ab images/decomposition.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/images/decomposition.svg Sun Oct 31 20:07:33 2010 +0100 @@ -0,0 +1,100 @@ + + + + + + + + + image/svg+xml + + + + + + + + + + + 1 med2 pre3 con + + diff -r a1703c4f2990 -r 2ac1551ad2ab images/description.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/images/description.svg Sun Oct 31 20:07:33 2010 +0100 @@ -0,0 +1,77 @@ + + + + + + + + + image/svg+xml + + + + + + T + 1 + + diff -r a1703c4f2990 -r 2ac1551ad2ab images/disconnected.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/images/disconnected.svg Sun Oct 31 20:07:33 2010 +0100 @@ -0,0 +1,277 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + diff -r a1703c4f2990 -r 2ac1551ad2ab images/dxf.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/images/dxf.svg Sun Oct 31 20:07:33 2010 +0100 @@ -0,0 +1,178 @@ + + + + + + + + + image/svg+xml + + + + + + dxf + 0SECTION 2HEADER 9$ACADVER 1AC1015 9$HANDSEED 5FFFF 9$DIMASZ 402.5 9 + + + + + diff -r a1703c4f2990 -r 2ac1551ad2ab images/image.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/images/image.svg Sun Oct 31 20:07:33 2010 +0100 @@ -0,0 +1,66 @@ + + + + + + + + + image/svg+xml + + + + + + img + + diff -r a1703c4f2990 -r 2ac1551ad2ab images/labourforce.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/images/labourforce.svg Sun Oct 31 20:07:33 2010 +0100 @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + diff -r a1703c4f2990 -r 2ac1551ad2ab images/machinery.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/images/machinery.svg Sun Oct 31 20:07:33 2010 +0100 @@ -0,0 +1,127 @@ + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + diff -r a1703c4f2990 -r 2ac1551ad2ab images/material.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/images/material.svg Sun Oct 31 20:07:33 2010 +0100 @@ -0,0 +1,1323 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r a1703c4f2990 -r 2ac1551ad2ab images/measure.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/images/measure.svg Sun Oct 31 20:07:33 2010 +0100 @@ -0,0 +1,109 @@ + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + diff -r a1703c4f2990 -r 2ac1551ad2ab images/menu.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/images/menu.svg Sun Oct 31 20:07:33 2010 +0100 @@ -0,0 +1,89 @@ + + + + + + + + + + + + image/svg+xml + + + + + + + + + diff -r a1703c4f2990 -r 2ac1551ad2ab images/normalline.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/images/normalline.svg Sun Oct 31 20:07:33 2010 +0100 @@ -0,0 +1,74 @@ + + + + + + + + + image/svg+xml + + + + + + + + + + + diff -r a1703c4f2990 -r 2ac1551ad2ab images/parcialline.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/images/parcialline.svg Sun Oct 31 20:07:33 2010 +0100 @@ -0,0 +1,274 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + Σ + p + + diff -r a1703c4f2990 -r 2ac1551ad2ab images/pyArq-Presupuestos.png Binary file images/pyArq-Presupuestos.png has changed diff -r a1703c4f2990 -r 2ac1551ad2ab images/pyArq-Presupuestos.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/images/pyArq-Presupuestos.svg Sun Oct 31 20:07:33 2010 +0100 @@ -0,0 +1,1323 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff -r a1703c4f2990 -r 2ac1551ad2ab images/pyArq.png Binary file images/pyArq.png has changed diff -r a1703c4f2990 -r 2ac1551ad2ab images/pyArqPresupuestos.xpm --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/images/pyArqPresupuestos.xpm Sun Oct 31 20:07:33 2010 +0100 @@ -0,0 +1,1439 @@ +/* XPM */ +static char * arquimeter_xpm[] = { +"128 128 1308 2", +" c None", +". c #592D2D", +"+ c #5D2E2C", +"@ c #5A2D2D", +"# c #5B2D2D", +"$ c #5B2E2E", +"% c #5A2E2D", +"& c #5E2E2D", +"* c #743C32", +"= c #8D4835", +"- c #A55639", +"; c #B45E3C", +"> c #A5563A", +", c #61322F", +"' c #5C2E2E", +") c #5A2C2C", +"! c #6D3730", +"~ c #864534", +"{ c #9E5238", +"] c #B25D3C", +"^ c #A7573A", +"/ c #64332F", +"( c #5A2E2E", +"_ c #653430", +": c #7E4133", +"< c #974E37", +"[ c #AF5B3B", +"} c #984F33", +"| c #7D412A", +"1 c #8A482E", +"2 c #B25D3B", +"3 c #AA583A", +"4 c #66342F", +"5 c #5C2D2D", +"6 c #5B2C2C", +"7 c #60312F", +"8 c #773D32", +"9 c #904A36", +"0 c #A9583A", +"a c #B35E3C", +"b c #8F4A30", +"c c #4C2818", +"d c #1B0E07", +"e c #221106", +"f c #301803", +"g c #351A05", +"h c #2B1609", +"i c #693721", +"j c #B35D3C", +"k c #AC5A3B", +"l c #693630", +"m c #5C2E2D", +"n c #713931", +"o c #894735", +"p c #A25339", +"q c #88482E", +"r c #150B06", +"s c #190D05", +"t c #1E0F01", +"u c #301802", +"v c #391C02", +"w c #422103", +"x c #4A2503", +"y c #522904", +"z c #3D1F04", +"A c #30190D", +"B c #AC5A39", +"C c #AD5A3B", +"D c #6C362F", +"E c #683530", +"F c #824334", +"G c #9A5038", +"H c #B15C3C", +"I c #391E13", +"J c #0B0500", +"K c #190C01", +"L c #211001", +"M c #2A1502", +"N c #321902", +"O c #3A1D02", +"P c #432103", +"Q c #4B2503", +"R c #532904", +"S c #432104", +"T c #30190E", +"U c #B05B3B", +"V c #703931", +"W c #62322F", +"X c #7B3F33", +"Y c #934C36", +"Z c #AB593A", +"` c #985033", +" . c #6C3923", +".. c #6D3924", +"+. c #9D5234", +"@. c #592F1E", +"#. c #080401", +"$. c #110800", +"%. c #1A0D01", +"&. c #221101", +"*. c #331902", +"=. c #3B1D02", +"-. c #4C2603", +";. c #542A04", +">. c #2B1605", +",. c #793F28", +"'. c #723A31", +"). c #733B31", +"!. c #8C4835", +"~. c #A45539", +"{. c #904B30", +"]. c #4F291A", +"^. c #1C0F08", +"/. c #201006", +"(. c #2F1803", +"_. c #331A07", +":. c #3B1E10", +"<. c #9B5134", +"[. c #A55637", +"}. c #110906", +"|. c #090500", +"1. c #120900", +"2. c #231101", +"3. c #2B1502", +"4. c #3C1E03", +"5. c #442203", +"6. c #3B1D03", +"7. c #422215", +"8. c #773C31", +"9. c #6C3730", +"0. c #844534", +"a. c #9D5138", +"b. c #190D08", +"c. c #180C05", +"d. c #1B0D01", +"e. c #2D1602", +"f. c #361B02", +"g. c #3E1F03", +"h. c #472303", +"i. c #4F2703", +"j. c #4F2704", +"k. c #221107", +"l. c #85452C", +"m. c #89472D", +"n. c #120A06", +"o. c #0A0500", +"p. c #130900", +"q. c #241201", +"r. c #2C1602", +"s. c #341A02", +"t. c #3B1E03", +"u. c #1A0D04", +"v. c #673522", +"w. c #7A3F33", +"x. c #65332F", +"y. c #7D4033", +"z. c #964E37", +"A. c #7C4129", +"B. c #0F0704", +"C. c #150A01", +"D. c #261301", +"E. c #2E1702", +"F. c #371B02", +"G. c #3F1F03", +"H. c #502804", +"I. c #1B0E05", +"J. c #964F32", +"K. c #26140D", +"L. c #100803", +"M. c #130901", +"N. c #1D0E06", +"O. c #4B2718", +"P. c #804133", +"Q. c #592E2E", +"R. c #5B2E2D", +"S. c #5F2F2E", +"T. c #763D32", +"U. c #8F4A36", +"V. c #A8573A", +"W. c #653521", +"X. c #512A19", +"Y. c #773E27", +"Z. c #9F5335", +"`. c #0F0804", +" + c #0E0700", +".+ c #160B01", +"++ c #1F0F01", +"@+ c #271301", +"#+ c #2F1702", +"$+ c #381C02", +"%+ c #402003", +"&+ c #482403", +"*+ c #512804", +"=+ c #462303", +"-+ c #321A0F", +";+ c #82442B", +">+ c #3B1F13", +",+ c #2F180F", +"'+ c #542B1C", +")+ c #944D31", +"!+ c #834334", +"~+ c #6F3830", +"{+ c #884635", +"]+ c #A05339", +"^+ c #954E32", +"/+ c #542C1B", +"(+ c #1E1009", +"_+ c #1E0F06", +":+ c #2D1603", +"<+ c #3B1E04", +"[+ c #2D1709", +"}+ c #653520", +"|+ c #3D2014", +"1+ c #090401", +"2+ c #0F0700", +"3+ c #170B01", +"4+ c #201001", +"5+ c #281402", +"6+ c #412003", +"7+ c #492403", +"8+ c #512904", +"9+ c #0C0603", +"0+ c #914C30", +"a+ c #532B1A", +"b+ c #381D10", +"c+ c #4B2716", +"d+ c #884735", +"e+ c #673430", +"f+ c #814234", +"g+ c #995038", +"h+ c #B05C3B", +"i+ c #391D13", +"j+ c #130A05", +"k+ c #180C01", +"l+ c #291402", +"m+ c #3A1D05", +"n+ c #3B1F11", +"o+ c #AD5A3A", +"p+ c #2A160E", +"q+ c #0B0502", +"r+ c #100800", +"s+ c #311802", +"t+ c #231205", +"u+ c #361C11", +"v+ c #472517", +"w+ c #190D07", +"x+ c #2E1602", +"y+ c #482404", +"z+ c #361B06", +"A+ c #A05435", +"B+ c #8D4936", +"C+ c #61312E", +"D+ c #7A3E32", +"E+ c #924B36", +"F+ c #AA593A", +"G+ c #090503", +"H+ c #381C05", +"I+ c #B15D3B", +"J+ c #512A1B", +"K+ c #0D0700", +"L+ c #180B01", +"M+ c #1A0E07", +"N+ c #482617", +"O+ c #552C1C", +"P+ c #100804", +"Q+ c #4C2503", +"R+ c #221108", +"S+ c #914B36", +"T+ c #5A2D2C", +"U+ c #5D2E2D", +"V+ c #723B31", +"W+ c #8A4835", +"X+ c #A25539", +"Y+ c #663521", +"Z+ c #412213", +"`+ c #4E2817", +" @ c #8C492F", +".@ c #25130C", +"+@ c #080400", +"@@ c #452203", +"#@ c #4D2603", +"$@ c #552A04", +"%@ c #1A0D05", +"&@ c #A25436", +"*@ c #A65737", +"=@ c #653421", +"-@ c #3F2114", +";@ c #562C1C", +">@ c #924C31", +",@ c #0C0600", +"'@ c #4D2703", +")@ c #1E1008", +"!@ c #944D37", +"~@ c #5B2D2C", +"{@ c #6A3630", +"]@ c #9C5138", +"^@ c #B15C3B", +"/@ c #5A2F1D", +"(@ c #20110A", +"_@ c #1C0F06", +":@ c #291503", +"<@ c #361C0E", +"[@ c #0E0705", +"}@ c #1C0E01", +"|@ c #351A02", +"1@ c #3D1E03", +"2@ c #4E2703", +"3@ c #261305", +"4@ c #592F1D", +"5@ c #2F180D", +"6@ c #321A0E", +"7@ c #5D301D", +"8@ c #A75738", +"9@ c #613320", +"0@ c #0A0502", +"a@ c #301804", +"b@ c #703B25", +"c@ c #64322F", +"d@ c #7C4033", +"e@ c #954E37", +"f@ c #773E28", +"g@ c #0E0704", +"h@ c #150B01", +"i@ c #211107", +"j@ c #8C492E", +"k@ c #783E28", +"l@ c #110905", +"m@ c #0B0600", +"n@ c #140A01", +"o@ c #251201", +"p@ c #271405", +"q@ c #180C07", +"r@ c #A35536", +"s@ c #974F32", +"t@ c #4D2819", +"u@ c #1B0E08", +"v@ c #2A1508", +"w@ c #6A3822", +"x@ c #1B0E09", +"y@ c #070300", +"z@ c #512A1A", +"A@ c #5A2E2C", +"B@ c #5D302E", +"C@ c #753B31", +"D@ c #8E4936", +"E@ c #A6563A", +"F@ c #462417", +"G@ c #1A0D06", +"H@ c #934C31", +"I@ c #26140C", +"J@ c #110904", +"K@ c #0F0701", +"L@ c #432315", +"M@ c #85462C", +"N@ c #964E32", +"O@ c #331905", +"P@ c #4D2818", +"Q@ c #A45637", +"R@ c #1F100A", +"S@ c #321802", +"T@ c #2B1501", +"U@ c #241206", +"V@ c #B45D3C", +"W@ c #834434", +"X@ c #68352F", +"Y@ c #6E3730", +"Z@ c #874535", +"`@ c #9F5339", +" # c #B25E3C", +".# c #0D0704", +"+# c #381C03", +"@# c #582E1C", +"## c #934D31", +"$# c #5B301E", +"%# c #5D301E", +"&# c #80432B", +"*# c #291505", +"=# c #753E26", +"-# c #AF5B3A", +";# c #110902", +"># c #120901", +",# c #190D06", +"'# c #1C0E08", +")# c #502A1A", +"!# c #6E3831", +"~# c #5C2F2D", +"{# c #60302D", +"]# c #66332F", +"^# c #984F37", +"/# c #AE5B3B", +"(# c #AE5B3A", +"_# c #7C412A", +":# c #3A1E12", +"<# c #231209", +"[# c #2A1509", +"}# c #391D0F", +"|# c #27140D", +"1# c #070301", +"2# c #341B0F", +"3# c #AB5939", +"4# c #3F2113", +"5# c #62331F", +"6# c #0F0805", +"7# c #1F1008", +"8# c #7B4029", +"9# c #995033", +"0# c #974E38", +"a# c #753C31", +"b# c #602F2E", +"c# c #71392F", +"d# c #894530", +"e# c #A25031", +"f# c #B35A32", +"g# c #69352E", +"h# c #5F312F", +"i# c #793E32", +"j# c #904B36", +"k# c #A8583A", +"l# c #81432B", +"m# c #170C06", +"n# c #1F1004", +"o# c #381C06", +"p# c #391E10", +"q# c #AA5939", +"r# c #7C4029", +"s# c #A95838", +"t# c #6C3924", +"u# c #2A160D", +"v# c #241203", +"w# c #422003", +"x# c #2D1708", +"y# c #5F311E", +"z# c #6F3A25", +"A# c #1D0E01", +"B# c #4A2403", +"C# c #9F5238", +"D# c #7F4033", +"E# c #82412F", +"F# c #9A4E31", +"G# c #B15832", +"H# c #B45A32", +"I# c #723930", +"J# c #8A4635", +"K# c #A15439", +"L# c #9E5335", +"M# c #0A0503", +"N# c #452204", +"O# c #27140A", +"P# c #592E1E", +"Q# c #1A0E08", +"R# c #110903", +"S# c #3E2013", +"T# c #130A02", +"U# c #391C05", +"V# c #3D2012", +"W# c #663522", +"X# c #2D1702", +"Y# c #241205", +"Z# c #683722", +"`# c #A6573A", +" $ c #874735", +".$ c #62312D", +"+$ c #7C3E2F", +"@$ c #944A31", +"#$ c #AB5632", +"$$ c #9A5033", +"%$ c #492617", +"&$ c #7F422B", +"*$ c #A05335", +"=$ c #321905", +"-$ c #5C301D", +";$ c #2C170E", +">$ c #180D07", +",$ c #150A03", +"'$ c #8B492E", +")$ c #AD5B3B", +"!$ c #934B37", +"~$ c #733A31", +"{$ c #5E2F2D", +"]$ c #753A2E", +"^$ c #8D4630", +"/$ c #A55332", +"($ c #B45A33", +"_$ c #7B3F32", +":$ c #1E100A", +"<$ c #140A05", +"[$ c #150B07", +"}$ c #542904", +"|$ c #88472D", +"1$ c #9A5138", +"2$ c #7B3E32", +"3$ c #62312E", +"4$ c #6E372F", +"5$ c #874330", +"6$ c #9F5032", +"7$ c #B25A33", +"8$ c #5E2F2E", +"9$ c #743B31", +"0$ c #693723", +"a$ c #2B160C", +"b$ c #2B160B", +"c$ c #361C0F", +"d$ c #6F3B23", +"e$ c #120906", +"f$ c #090502", +"g$ c #1D0F08", +"h$ c #261308", +"i$ c #2F180B", +"j$ c #512A18", +"k$ c #87462D", +"l$ c #130A04", +"m$ c #844434", +"n$ c #67332E", +"o$ c #804130", +"p$ c #984D32", +"q$ c #AE5934", +"r$ c #B45B34", +"s$ c #6C3830", +"t$ c #5F321F", +"u$ c #22110A", +"v$ c #231202", +"w$ c #391C03", +"x$ c #3E1F05", +"y$ c #2D170B", +"z$ c #944E32", +"A$ c #1A0D09", +"B$ c #0B0501", +"C$ c #1D0F06", +"D$ c #25130A", +"E$ c #170C07", +"F$ c #271408", +"G$ c #7C4028", +"H$ c #753D27", +"I$ c #281303", +"J$ c #1B0E06", +"K$ c #3D2013", +"L$ c #AB593B", +"M$ c #5C2F2E", +"N$ c #61302D", +"O$ c #7A3D2F", +"P$ c #924931", +"Q$ c #AA5633", +"R$ c #582C2C", +"S$ c #341B11", +"T$ c #0D0701", +"U$ c #211106", +"V$ c #8C482F", +"W$ c #532C1C", +"X$ c #21110A", +"Y$ c #1A0D07", +"Z$ c #160C06", +"`$ c #27150C", +" % c #2E180F", +".% c #261405", +"+% c #753D26", +"@% c #9A5134", +"#% c #0E0701", +"$% c #170C04", +"%% c #2D180E", +"&% c #703A25", +"*% c #A85838", +"=% c #763C31", +"-% c #5F302D", +";% c #723A30", +">% c #8A4632", +",% c #A35232", +"'% c #B35B34", +")% c #592C2C", +"!% c #532A24", +"~% c #944C37", +"{% c #20110B", +"]% c #502704", +"^% c #A25536", +"/% c #7A4029", +"(% c #A05439", +"_% c #6B362E", +":% c #854331", +"<% c #9D4F33", +"[% c #B15934", +"}% c #B45B35", +"|% c #502822", +"1% c #5A2D2B", +"2% c #0B0603", +"3% c #743C27", +"4% c #231207", +"5% c #2B1608", +"6% c #331A0D", +"7% c #7F4229", +"8% c #321903", +"9% c #874635", +"0% c #6B3730", +"a% c #64322D", +"b% c #7D3F30", +"c% c #954B32", +"d% c #AE5734", +"e% c #4E2822", +"f% c #4B2819", +"g% c #572E1D", +"h% c #5C2E2C", +"i% c #23120B", +"j% c #271404", +"k% c #643521", +"l% c #4E2819", +"m% c #1C0E06", +"n% c #241101", +"o% c #381B02", +"p% c #391C06", +"q% c #371D10", +"r% c #3B1F14", +"s% c #482303", +"t% c #211103", +"u% c #643421", +"v% c #914C37", +"w% c #773D30", +"x% c #904832", +"y% c #A85534", +"z% c #4C2721", +"A% c #522B1B", +"B% c #5E3220", +"C% c #5D302C", +"D% c #874534", +"E% c #291502", +"F% c #231204", +"G% c #391E12", +"H% c #0D0603", +"I% c #29150B", +"J% c #492618", +"K% c #120905", +"L% c #271302", +"M% c #542B1B", +"N% c #9C5037", +"O% c #70382F", +"P% c #884632", +"Q% c #A05234", +"R% c #B35C36", +"S% c #B45C36", +"T% c #582C2B", +"U% c #4A2620", +"V% c #422316", +"W% c #4E291A", +"X% c #5A2F1E", +"Y% c #663622", +"Z% c #60302B", +"`% c #733C26", +" & c #22120B", +".& c #180C06", +"+& c #160B06", +"@& c #7F422A", +"#& c #0E0703", +"$& c #341B04", +"%& c #5D311D", +"&& c #321A10", +"*& c #1D0F09", +"=& c #562D1C", +"-& c #A5553A", +";& c #834435", +">& c #69352F", +",& c #824231", +"'& c #9A4E33", +")& c #B05A36", +"!& c #482520", +"~& c #3E2114", +"{& c #4A2718", +"]& c #623320", +"^& c #63332B", +"/& c #7F4133", +"(& c #201009", +"_& c #301806", +":& c #2E1809", +"<& c #462514", +"[& c #090400", +"}& c #1C0E07", +"|& c #8E4836", +"1& c #6E3830", +"2& c #7B3E30", +"3& c #934B33", +"4& c #AB5736", +"5& c #B45C37", +"6& c #46231F", +"7& c #3A1E13", +"8& c #462517", +"9& c #5E311F", +"0& c #69362C", +"a& c #7A3F32", +"b& c #6C3824", +"c& c #241207", +"d& c #82432A", +"e& c #8E4B30", +"f& c #974F38", +"g& c #753C32", +"h& c #60302E", +"i& c #753B30", +"j& c #8D4833", +"k& c #A55436", +"l& c #44221F", +"m& c #361C12", +"n& c #422216", +"o& c #713B25", +"p& c #7D4229", +"q& c #70392D", +"r& c #763C32", +"s& c #070402", +"t& c #140901", +"u& c #88462D", +"v& c #1C0E09", +"w& c #0C0601", +"x& c #231201", +"y& c #1F1006", +"z& c #A15339", +"A& c #6E382F", +"B& c #864532", +"C& c #9E5135", +"D& c #B25B37", +"E& c #582B2B", +"F& c #41211E", +"G& c #311A10", +"H& c #552D1C", +"I& c #783F28", +"J& c #84462C", +"K& c #763D2E", +"L& c #4F2804", +"M& c #613321", +"N& c #27150D", +"O& c #874634", +"P& c #804131", +"Q& c #984E34", +"R& c #AE5B38", +"S& c #B45D38", +"T& c #40201D", +"U& c #2D180F", +"V& c #452417", +"W& c #5C301E", +"X& c #743D26", +"Y& c #80432A", +"Z& c #7D4130", +"`& c #221105", +" * c #924C36", +".* c #783E31", +"+* c #914A33", +"@* c #A95736", +"#* c #3E1F1C", +"$* c #29150D", +"%* c #351C11", +"&* c #412215", +"** c #4C2819", +"=* c #582E1D", +"-* c #87472D", +";* c #874632", +">* c #AE5A3B", +",* c #0F0803", +"'* c #9C5134", +")* c #9C5038", +"!* c #8B4734", +"~* c #A25437", +"{* c #B45C39", +"]* c #572B2B", +"^* c #3C1E1C", +"/* c #311910", +"(* c #482618", +"_* c #542C1C", +":* c #603220", +"<* c #773F27", +"[* c #83452B", +"}* c #8F4B2F", +"|* c #9B5233", +"1* c #904B34", +"2* c #68342F", +"3* c #140A06", +"4* c #1F1001", +"5* c #1E1005", +"6* c #A5573A", +"7* c #6A362F", +"8* c #834332", +"9* c #9C5036", +"0* c #B15B38", +"a* c #B45D39", +"b* c #B25932", +"c* c #9F4F31", +"d* c #864330", +"e* c #3A1D1B", +"f* c #21110B", +"g* c #2D170F", +"h* c #381D12", +"i* c #442316", +"j* c #683622", +"k* c #7F432A", +"l* c #A35636", +"m* c #995037", +"n* c #402115", +"o* c #25130B", +"p* c #8D4A2F", +"q* c #8E4835", +"r* c #7E4032", +"s* c #964C35", +"t* c #AC5938", +"u* c #AB5532", +"v* c #934A31", +"w* c #7E3F2F", +"x* c #8C4630", +"y* c #AE5733", +"z* c #A55231", +"A* c #8D4730", +"B* c #753B2F", +"C* c #381C1A", +"D* c #1C0F09", +"E* c #28150D", +"F* c #3F2115", +"G* c #633421", +"H* c #924D30", +"I* c #9E5334", +"J* c #AA5938", +"K* c #A35539", +"L* c #63322F", +"M* c #5F2F2D", +"N* c #763D31", +"O* c #8E4A35", +"P* c #A75739", +"Q* c #B45E3A", +"R* c #B05934", +"S* c #9A4E32", +"T* c #824130", +"U* c #AC5632", +"V* c #7B3E2F", +"W* c #63302D", +"X* c #5C2D2C", +"Y* c #361B1A", +"Z* c #180C08", +"`* c #24130C", +" = c #2F190F", +".= c #532C1B", +"+= c #5E321F", +"@= c #6A3823", +"#= c #763E27", +"$= c #82452B", +"%= c #8E4B2F", +"&= c #995133", +"*= c #A55737", +"== c #A55739", +"-= c #5F302E", +";= c #A15238", +">= c #7D4133", +",= c #6F3930", +"'= c #884634", +")= c #A05337", +"!= c #B35E3A", +"~= c #A25132", +"{= c #894531", +"]= c #70392F", +"^= c #64322E", +"/= c #7E3F30", +"(= c #6A342E", +"_= c #341A19", +":= c #2B170E", +"<= c #371D12", +"[= c #432316", +"}= c #723C26", +"|= c #7E422A", +"1= c #8A492E", +"2= c #954F31", +"3= c #A15535", +"4= c #AD5B39", +"5= c #B45F3C", +"6= c #A7583A", +"7= c #974F37", +"8= c #69342F", +"9= c #814233", +"0= c #994F37", +"a= c #B05C39", +"b= c #8F4831", +"c= c #783C2F", +"d= c #61302E", +"e= c #592D2C", +"f= c #321918", +"g= c #100805", +"h= c #331B11", +"i= c #4B2719", +"j= c #6E3A24", +"k= c #7A4028", +"l= c #9D5334", +"m= c #A95938", +"n= c #AA5A3A", +"o= c #5D2F2D", +"p= c #B45E3B", +"q= c #AE5834", +"r= c #964C33", +"s= c #7E4031", +"t= c #301818", +"u= c #81442B", +"v= c #7C3F33", +"w= c #7C4032", +"x= c #B25A35", +"y= c #9E5033", +"z= c #864331", +"A= c #8E4A2F", +"B= c #4C2718", +"C= c #311804", +"D= c #341A06", +"E= c #2D180A", +"F= c #773E26", +"G= c #63332F", +"H= c #713C25", +"I= c #89482D", +"J= c #954E31", +"K= c #A15435", +"L= c #A55434", +"M= c #8D4832", +"N= c #703A31", +"O= c #140B06", +"P= c #311902", +"Q= c #371C05", +"R= c #020100", +"S= c #904C30", +"T= c #9C5234", +"U= c #AC5835", +"V= c #944B33", +"W= c #7C3F30", +"X= c #63322E", +"Y= c #0D0600", +"Z= c #3A1D04", +"`= c #422214", +" - c #391D1D", +".- c #0C0604", +"+- c #160B07", +"@- c #512B1B", +"#- c #5D311F", +"$- c #8C4A2E", +"%- c #985032", +"&- c #AF5C3A", +"*- c #B15A37", +"=- c #9C4F33", +"-- c #231105", +";- c #6C3731", +">- c #422121", +",- c #150B08", +"'- c #1E0F0A", +")- c #9F5435", +"!- c #AB5A39", +"~- c #B45B37", +"{- c #A35335", +"]- c #5D2F2E", +"^- c #201106", +"/- c #381B03", +"(- c #2F1808", +"_- c #452414", +":- c #341902", +"<- c #572D1C", +"[- c #6F3730", +"}- c #492524", +"|- c #84452C", +"1- c #A75837", +"2- c #B35E3B", +"3- c #AA5636", +"4- c #914933", +"5- c #783D31", +"6- c #3D1F03", +"7- c #4E2827", +"8- c #27130E", +"9- c #381E12", +"0- c #442416", +"a- c #AF5A37", +"b- c #984E35", +"c- c #68342E", +"d- c #66332E", +"e- c #0B0602", +"f- c #0D0702", +"g- c #522A29", +"h- c #2F1811", +"i- c #402215", +"j- c #6F3B25", +"k- c #7B4129", +"l- c #AA5A38", +"m- c #B25D38", +"n- c #9F5235", +"o- c #874532", +"p- c #602F2D", +"q- c #592F1C", +"r- c #462416", +"s- c #562B2B", +"t- c #381C15", +"u- c #3C1F14", +"v- c #6B3823", +"w- c #9A5133", +"x- c #7B4033", +"y- c #A65636", +"z- c #8E4933", +"A- c #763C30", +"B- c #3C1E02", +"C- c #391D05", +"D- c #4B2504", +"E- c #562D1B", +"F- c #3F2119", +"G- c #673622", +"H- c #7D4032", +"I- c #2A1402", +"J- c #532A04", +"K- c #1F110B", +"L- c #391D02", +"M- c #201004", +"N- c #452416", +"O- c #331907", +"P- c #432313", +"Q- c #48251D", +"R- c #86462C", +"S- c #783F32", +"T- c #9D5136", +"U- c #844333", +"V- c #6B362F", +"W- c #311805", +"X- c #633320", +"Y- c #0E0600", +"Z- c #4A2617", +"`- c #110804", +" ; c #472304", +".; c #A15436", +"+; c #4E281F", +"@; c #A45436", +"#; c #8B4733", +"$; c #462415", +"%; c #572D1B", +"&; c #150B05", +"*; c #1A0D08", +"=; c #29150C", +"-; c #542A22", +";; c #753C30", +">; c #924C35", +",; c #844334", +"'; c #9B5138", +"); c #582F1D", +"!; c #1F1009", +"~; c #432203", +"{; c #341B07", +"]; c #3C1F11", +"^; c #1F1005", +"/; c #311A0E", +"(; c #351C0F", +"_; c #6A3722", +":; c #83452C", +"<; c #582D25", +"[; c #794028", +"}; c #733C30", +"|; c #994F36", +"1; c #814232", +"2; c #693530", +"3; c #221208", +"4; c #643422", +"5; c #412103", +"6; c #4B2603", +"7; c #401F04", +"8; c #793F27", +"9; c #080300", +"0; c #2C1603", +"a; c #5B2E28", +"b; c #693724", +"c; c #713B2F", +"d; c #A05438", +"e; c #703930", +"f; c #5E302E", +"g; c #86462D", +"h; c #110803", +"i; c #1C0E05", +"j; c #442315", +"k; c #89482E", +"l; c #0D0604", +"m; c #2B1606", +"n; c #62341F", +"o; c #170C08", +"p; c #2C1502", +"q; c #5D2F29", +"r; c #703A2F", +"s; c #904A35", +"t; c #88472E", +"u; c #50291A", +"v; c #5B2F1E", +"w; c #572B04", +"x; c #201005", +"y; c #100801", +"z; c #5E302B", +"A; c #763E28", +"B; c #6F392F", +"C; c #67342F", +"D; c #2C170A", +"E; c #402112", +"F; c #190D09", +"G; c #060300", +"H; c #361B03", +"I; c #462516", +"J; c #753E27", +"K; c #412214", +"L; c #6D3823", +"M; c #5E312D", +"N; c #733C2B", +"O; c #60312E", +"P; c #2E180E", +"Q; c #4E2704", +"R; c #271305", +"S; c #170C05", +"T; c #2A150D", +"U; c #251203", +"V; c #3F2004", +"W; c #6F3A24", +"X; c #A05239", +"Y; c #65322F", +"Z; c #160A01", +"`; c #402005", +" > c #4A2719", +".> c #0E0805", +"+> c #130902", +"@> c #200F01", +"#> c #311905", +"$> c #502A19", +"%> c #2F1802", +"&> c #2A1501", +"*> c #723B25", +"=> c #6B3630", +"-> c #331A05", +";> c #8F4B30", +">> c #562B04", +",> c #2A1505", +"'> c #723C25", +")> c #140A03", +"!> c #140A07", +"~> c #5B2E2C", +"{> c #2D170D", +"]> c #2C160B", +"^> c #1E0F08", +"/> c #281509", +"(> c #31190C", +"_> c #5C2F1D", +":> c #713B26", +"<> c #9E5138", +"[> c #5D301F", +"}> c #241202", +"|> c #3A1C03", +"1> c #4D2704", +"2> c #3B1E06", +"3> c #321A0D", +"4> c #231102", +"5> c #1D0F05", +"6> c #271409", +"7> c #5F3220", +"8> c #281404", +"9> c #4A2717", +"0> c #9F5338", +"a> c #0F0802", +"b> c #472518", +"c> c #28150C", +"d> c #6A3723", +"e> c #87472C", +"f> c #25140C", +"g> c #160B05", +"h> c #100701", +"i> c #170C03", +"j> c #532A23", +"k> c #4E2821", +"l> c #512A1C", +"m> c #090402", +"n> c #381D0F", +"o> c #623321", +"p> c #2C1604", +"q> c #4B271F", +"r> c #582E1E", +"s> c #180D08", +"t> c #221104", +"u> c #743D27", +"v> c #452415", +"w> c #1E0F04", +"x> c #48251F", +"y> c #603222", +"z> c #241204", +"A> c #1B0D06", +"B> c #3E1E04", +"C> c #46241E", +"D> c #5E2F2C", +"E> c #0A0602", +"F> c #2C1605", +"G> c #44231D", +"H> c #6E3A25", +"I> c #61312C", +"J> c #2E1707", +"K> c #2E170A", +"L> c #2C170C", +"M> c #41221C", +"N> c #64332D", +"O> c #211101", +"P> c #231208", +"Q> c #452303", +"R> c #30190F", +"S> c #3E211B", +"T> c #68362D", +"U> c #080402", +"V> c #3C1F1B", +"W> c #70392E", +"X> c #522A1B", +"Y> c #3A1E19", +"Z> c #783D2F", +"`> c #432317", +" , c #371C19", +"., c #7F4131", +"+, c #090501", +"@, c #341B18", +"#, c #884633", +"$, c #B25A32", +"%, c #321917", +"&, c #975032", +"*, c #914B35", +"=, c #8D462F", +"-, c #301817", +";, c #984E37", +">, c #2D1715", +",, c #9E5339", +"', c #B15833", +"), c #9A4D31", +"!, c #2A1615", +"~, c #A05539", +"{, c #9D5238", +"], c #B35A33", +"^, c #A15132", +"/, c #281414", +"(, c #A75533", +"_, c #904831", +":, c #261313", +"<, c #301910", +"[, c #AD5833", +"}, c #964C32", +"|, c #65322D", +"1, c #582D2D", +"2, c #251313", +"3, c #844331", +"4, c #6C362E", +"5, c #4D2727", +"6, c #040201", +"7, c #B35B35", +"8, c #A45233", +"9, c #8B4631", +"0, c #73392F", +"a, c #AA5634", +"b, c #924932", +"c, c #7A3D30", +"d, c #3A1E1D", +"e, c #140B07", +"f, c #AC5B39", +"g, c #AF5935", +"h, c #994E33", +"i, c #432221", +"j, c #1B0F09", +"k, c #A85938", +"l, c #B25C36", +"m, c #9F5134", +"n, c #A45636", +"o, c #A65535", +"p, c #4F2827", +"q, c #2E1811", +"r, c #944E31", +"s, c #AC5836", +"t, c #944C34", +"u, c #7C3F31", +"v, c #63312D", +"w, c #542A29", +"x, c #361C14", +"y, c #9B4F35", +"z, c #824232", +"A, c #6A352F", +"B, c #552B2A", +"C, c #3E2117", +"D, c #B45C38", +"E, c #A25335", +"F, c #8A4532", +"G, c #46241A", +"H, c #A85737", +"I, c #904A34", +"J, c #4D281E", +"K, c #6D3830", +"L, c #AD5937", +"M, c #964E35", +"N, c #7F4132", +"O, c #532B21", +"P, c #B25C39", +"Q, c #9D5035", +"R, c #854332", +"S, c #6C372F", +"T, c #582D24", +"U, c #6B372F", +"V, c #B35D39", +"W, c #A45437", +"X, c #8C4734", +"Y, c #743A30", +"Z, c #5C3026", +"`, c #AB5838", +" ' c #924B35", +".' c #7A3E31", +"+' c #5E3128", +"@' c #713C26", +"#' c #69362E", +"$' c #995036", +"%' c #814333", +"&' c #61312A", +"*' c #68352E", +"=' c #B25E3A", +"-' c #9F5337", +";' c #874633", +">' c #6F382F", +",' c #7F432B", +"'' c #67342D", +")' c #A65739", +"!' c #8E4934", +"~' c #61322D", +"{' c #AC5A3A", +"]' c #944D36", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" . + ", +" @ # @ @ @ @ $ ", +" # % @ & * = - ; > , ' ", +" ) $ @ # ! ~ { ] ; ; ; ; ; ^ / $ ", +" ( $ @ @ _ : < [ ; ; ; } | 1 2 ; ; ; 3 4 $ ", +" @ 5 6 @ 7 8 9 0 ; ; ; a b c d e f g h i j ; ; k l # ", +" 5 ' @ m n o p a ; ; ; ; ; q r s t u v w x y z A B ; ; C D % ", +" @ # @ @ E F G H ; ; ; ; ; ; ; ; ; I J K L M N O P Q R S T a ; ; U V # ", +" ( $ @ @ W X Y Z ; ; ; ; ` ...+.; ; ; ; ; @.#.$.%.&.M *.=.P -.;.>.,.; ; ; H '.. ", +" % % @ m ).!.~.; ; ; ; ; {.].^./.(.v _.:.<.; ; ; [.}.|.1.%.2.3.*.4.5.-.6.7.; ; ; ; ] 8.@ ", +" ( # @ % 9.0.a.] ; ; ; ; ; ; [.b.c.d.e.f.g.h.i.j.k.l.; ; ; m.n.o.p.d.q.r.s.t.*.u.v.; ; ; ; ; a w.@ # ", +" @ # @ @ x.y.z.C ; ; ; ; ; ; ; ; ; ; A.B.C.t D.E.F.G.h.H.;.I.` ; ; ; J.K.L.M.d.d.N.d O.b ; ; ; ; ; ; ; a P.@ Q. ", +" ( R.# @ S.T.U.V.; ; ; +.W.X.Y.B ; ; ; ; ; ; Z.`. +.+++@+#+$+%+&+*+=+-+; ; ; ; ; ;+>+,+'+)+; ; ; ; ; ; ; ; ; ; ; a !+@ @ ", +" . $ @ # ~+{+]+a ; ; ; ^+/+(+_+:+G.<+[+}+a ; ; ; ; ; |+1+2+3+4+5+u v 6+7+8+9+; ; ; ; ; ; ; ; ; ; ; ; ; ; 0+a+b+c+b ; ; ; ; d+@ @ ", +" ( ( @ @ e+f+g+h+; ; ; ; ; ; i+j+k+l+N O P Q R m+n+h+; ; ; ; o+p+q+r+k+4+l+s+$+N t+u+; ; ; ; ; ; ; ; ; ; ; 1 v+w+/.x+5.y+z+:.A+; ; ; B+@ # ", +" @ # . @ C+D+E+F+; ; ; ; ; ; ; ; ; ; G+1.%.&.3.*.=.P -.;.H+c ; ; ; ; ; I+J+b.K+3+L+I.M+N+1 a ; ; ; ; ; ; ; ; ; ; O+P+L+@+#+$+%+&+H.Q+R+J.; ; ; S+T+% ", +" @ # @ U+V+W+X+a ; A+Y+Z+`+ @; ; ; ; ; ; ; .@+@p.d.2.r.s.4.@@#@$@%@&@; ; ; ; ; ; *@=@-@;@>@; ; ; ; ; ; ; ; ; ; ; ; ; ; p+,@3+4+5+u v 6+7+*+'@)@o+; ; ; !@# ~@ ", +" @ $ @ % {@!+]@^@; ; <./@(@_@:@G.@@z+<@<.; ; ; ; ; >@[@J p.}@q.r.|@1@@@2@3@| ; ; ; ; ; ; ; ; ; ; ; ; ; ` 4@5@6@7@8@; ; ; ; ; ; 9@0@r+k+4+l+s+v w x y a@b@; ; ; ; g+m $ ", +" ) $ @ @ c@d@e@k ; ; ; ; ; f@g@h@q.E.F.G.h.H.j.i@j@; ; ; ; ; k@l@m@n@}@o@e.|@u p@q@r@; ; ; ; ; ; ; ; ; ; s@t@u@_+5+%+-.5.v@w@; ; ; ; ; h+x@y@r+K L l+N O w Q u z@; ; ; ; ; ]@% % ", +" A@% @ B@C@D@E@; ; ; ; ; ; ; ; ; F@|..+++@+#+F.%+&+H.*+G@[.; ; ; ; ; H@I@J@K@n@I.q@L@M@I+; ; ; ; ; ; ; ; ; ; N@9+M.&.3.*.4.5.-.$@O@P@; ; ; ; ; Q@R@1+$.%.&.M S@T@U@d 0+; ; V@X+W@X@@ @ # ", +" ) # @ # Y@Z@`@ #; ; ; ; ; ; ; ; ; ; ; ; | .#2+3+4+5+u v 6+7+*++#@#; ; ; ; ; ; ; ##$#%#b a ; ; ; ; ; ; ; ; ; ; ; ; ; &#B.p.d.q.r.s.1@@@#@$@*#=#; ; ; ; ; -#J+u@;#>#,#'#)#0+; 3 B+!#~#% @ @ {#@ # ", +" # $ @ @ ]#P.^#/#; ; ; (#_#:#<#[#}#M@; ; ; ; ; ; a |#1#r+k+4+l+s+v w x 4.2#; ; ; ; ; ; ; ; ; ; ; ; ; ; 3#Y.4#2#5#8@; ; ; ; ; B 6#m@n@}@o@e.|@g.=+2@j.7#; ; ; ; ; ; ; (#l.8#9#U 0#a#b## @ @ m c#d#e#f#g## ", +" ) $ 6 @ h#i#j#k#; ; ; ; ; l#,+m#n##+6+7+H.o#p#B ; ; ; ; ; q#.@q+r+K L l+N #+3@m#r#; ; ; ; ; ; ; ; ; ; ; s#t#u#,#v#+#x w#x#y#a ; ; ; ; z#.#,@C.A#o@E.f.g.h.B#9+; ; ; ; ; ; ; #C#D#/ # % @ # g#E#F#G#H#H#H#H#g## ", +" # $ @ U+I#J#K#V@; ; ; ; ; ; ; L#M#C.L l+s+O w x y N#O#o+; ; ; ; ; I+P#Q#R#>#%@m#S#&#h+; ; ; ; ; ; ; ; ; ; ; -#6#T#++M *.=.P -.;.U#V#2 ; ; ; ; W#l@,@C.t D.X#l+Y#,#Z#; ; ; ; `# ${@R.# @ @ .$+$@$#$H#H#H#H#H#H#H#H#g## ", +" ) $ @ @ E F G H ; ; ; ; ; ; ; ; ; ; ; $$6#$.K L M N O P Q R <+%$; ; ; ; ; ; ; (#&$z#1 j ; ; ; ; ; ; ; ; ; ; ; ; ; ; *$g@1.%.2.3.*.4.5.-.$@=$-$; ; ; ; ; J.;$>$,$s w+N+'$j ; )$!$~${$# @ @ {$]$^$/$($($($($H#H#H#H#H#H#H#H#g## ", +" @ # @ @ , _$Y Z ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; :$y@$.%.&.M *.=.P -.;.<$h+; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; [$o.p.d.2.r.s.4.@@#@}$<$h+; ; ; ; ; ; *@|$` ; ^@1$2$3$@ @ @ # 4$5$6$7$($($($($($($($H#H#H#H#H#H#H#H#g## ", +" Q.# R.@ 8$9$B+> ; ; ; ; ; r@0$a$b$c$d$j ; ; ; ; ; ; ; ; } e$|.1.d.2.3.*.4.5.@@f$-#; ; ; ; ; ; ; ; ; ; ; ; j @O.g$h$i$j$s#; ; ; ; ; ; k$[@,@n@}@o@e.|@1@=+2@l$*$; ; ; ; ; ; a ~.m$X@@ # @ @ n$o$p$q$r$($($($($($($($($($($H#H#H#H#H#H#H#H#g## ", +" @ @ s${ ] ; ; ; ; ; s#t$u$G@v$w$h.2@x$y$##; ; ; ; ; ; ; ; z$A$B$p.}@q.5+v$C$D$,.; ; ; ; ; ; ; ; ; ; ; A+7.E$C$@+4.@@2@7+F$G$; ; ; ; ; ; H$l@m@C.A#o@E.u I$J$K$; ; ; ; L$D@~+M$$ @ @ N$O$P$Q$r$r$r$r$r$($($($($($($($($($($H#H#H#H#H#H#H#H#g## ", +" R$# m ^#; ; ; ; ; ; S$T$}@D.E.F.G.h.H.H.U$V$; ; ; ; ; ; ; ; (#W$X$Y$Z$`$0$Q@; ; ; ; ; ; ; ; ; ; ; ; ; %T$}@o@e.|@g.=+2@$@.%+%; ; ; ; ; ; @% %m##%$%m#%%&%*%; h+< =%-%@ @ @ m ;%>%,%'%r$r$r$r$r$r$r$r$($($($($($($($($($($H#H#H#H#H#H#H#H#g## ", +" )%!%T+# ~%; ; ; ; ; {%,@.+++@+#+$+%+&+H.]%d 3#; ; ; ; ; ; ; ; ; ; s#j ; ; ; ; ; ; o+o+; ; ; ; ; ; ; ; ;$J C.A#o@E.f.g.h.i.R %@^%; ; ; ; ; ; ; r@/%M@o+a (%: x.@ # @ % _%:%<%[%}%}%r$r$r$r$r$r$r$r$r$r$($($($($($($($($($($H#H#H#H#H#H#H#H#g## ", +" )%|%J+1%% 9 ; ; ; ; W.2%2+3+4+5+u v 6+7+*+a@3%; ; ; ; ; ; ; ; ; ; ; ; ; ; ; 0+z@)@4%5%6%7%; ; ; ; ; ; ,..# +.+t @+#+F.G.&+H.8%W.; ; ; ; ; ; ; ; V.9%0%R.@ @ @ a%b%c%d%}%}%}%}%}%}%r$r$r$r$r$r$r$r$r$r$($($($($($($($($($($H#H#H#H#H#H#H#H#g## ", +" )%e%f%g%h%# !.; ; ; 2 i%#.r+k+4+l+s+v w x j%k%; ; ; ; ; ; ; ; ; ; ; ; h+l%q@m%n%o%w x *+p%q%B ; ; ; ; ; r%9+ +3+++@+u $+%+s%t%u%; ; ; ; /#v%'.{$# @ @ -%w%x%y%}%}%}%}%}%}%}%}%}%}%r$r$r$r$r$r$r$r$r$r$($($($($($($($($($($H#H#H#H#H#H#H#H#g## ", +" R$z%v+A%B%C%@ D%; ; ; B ;$9+r+K L l+E%F%J$G%[.; ; ; ; ; ; ; ; ; ; ; ; f@H%K L l+N O w Q R N#I%I+; ; ; ; a J%K% +3+4+L%&./.g$M%o+] N%D+3$@ @ @ m O%P%Q%R%S%S%S%}%}%}%}%}%}%}%}%}%}%r$r$r$r$r$r$r$r$r$r$($($($($($($($($($($H#H#H#H#H#H#H#H#g## ", +" T%U%V%W%X%Y%Z%@ F a ; ; ; `% &.&,#+&G%| (#; ; ; ; Q@` h+; ; ; ; ; ; ; @&#&$.%.&.M N =.P Q ;.$&%&; ; ; ; ; ; N@&&'#>$*&=&J.-&;&E # # @ @ >&,&'&)&S%S%S%S%S%S%S%}%}%}%}%}%}%}%}%}%}%r$r$r$r$r$r$r$r$r$r$($($($($($($($($($($H#H#H#H#H#H#H#H#g## ", +" T%!&~&{&=&]&..^&@ /&V@; ; ; ; Q@+.2 ; ; ; ; ^+=&(&U$_&:&<&*@; ; ; ; ; -#[$[&1.%.2.3.*.4.5.-.j.}&; ; ; ; ; ; ; ; ; Z |&1&m # @ @ .$2&3&4&5&5&S%S%S%S%S%S%S%S%S%}%}%}%}%}%}%}%}%}%}%r$r$r$r$r$r$r$r$r$r$($($($($($($($($($($H#H#H#H#H#H#H#H#g## ", +" T%6&7&8&A%9&0$H$0&@ a& #; ; ; ; ; ; ; ; b&>$G@4+*.g.=+2@Q c&d&; ; ; ; ; e&}.|.p.d.2.r.s.4.5.S@X$; ; ; ; ; H f&g&h&# @ @ {$i&j&k&5&5&5&5&5&5&S%S%S%S%S%S%S%S%S%}%}%}%}%}%}%}%}%}%}%r$r$r$r$r$r$r$r$r$r$($($($($($($($($($($H#H#H#H#H#H#H#H#g## ", +" T%l&m&n&W%X%W.o&p&q&@ r&] ; ; ; ; ; ; I+s&t&A#o@E.f.g.h.i.}$U$u&; ; ; ; ; ##v&w&p.}@x&4+y&d %$Z.; V@z&: x.# # @ # A&B&C&D&5&5&5&5&5&5&5&5&5&S%S%S%S%S%S%S%S%S%}%}%}%}%}%}%}%}%}%}%r$r$r$r$r$r$r$r$r$r$($($($($($($($($($($H#H#H#H#H#H#H#H#g## ", +" E&F&G&|+J%H&9@t#I&J&K&@ V+H ; ; ; ; ; ; 6# +.+t D.#+F.G.&+H.L&d j ; ; ; ; ; I+M&N&(+*&)#0+; 0 O&0%R.# @ @ n$P&Q&R&S&S&S&5&5&5&5&5&5&5&5&5&5&S%S%S%S%S%S%S%S%S%}%}%}%}%}%}%}%}%}%}%r$r$r$r$r$r$r$r$r$r$($($($($($($($($($($H#H#H#H#H#H#H#H#g## ", +" E&T&U&I V&)#W&Z#X&Y&'$Z&@ ~+[ ; ; ; ; ; @.2% +3+++@+u $+%+7+*+`&b ; ; ; ; ; ; ; ; ; /# *'.{$# @ @ N$.*+*@*S&S&S&S&S&S&S&5&5&5&5&5&5&5&5&5&5&S%S%S%S%S%S%S%S%S%}%}%}%}%}%}%}%}%}%}%r$r$r$r$r$r$r$r$r$r$($($($($($($($($($($H#H#H#H#H#H#H#H#g## ", +" E&#*$*%*&***=*k%b@A.-*##;*R.9.>*; ; ; ; 2 %2%2+3+4+5+u v 6+$+,*'*; ; ; ; ; #)*w.3$# @ @ M$;%!*~*{*S&S&S&S&S&S&S&S&S&S&5&5&5&5&5&5&5&5&5&5&S%S%S%S%S%S%S%S%S%}%}%}%}%}%}%}%}%}%}%r$r$r$r$r$r$r$r$r$r$($($($($($($($($($($H#H#H#H#H#H#H#H#g## ", +" ]*^*.@/*|+(*_*:*t#<*[*}*|*1*R.2*Z ; ; ; ; 2 (*3* +k+4*t 5*Y$L@b ; ; ; 6*;&E ( # @ % 7*8*9*0*a*a*a*a*S&S&S&S&S&S&S&S&S&S&5&5&5&5&5&5&5&5&5&5&S%S%S%S%S%S%S%S%S%}%}%}%}%}%}%}%}%}%}%r$r$r$r$r$r$r$r$r$r$($($($($($($($($($($H#H#H#H#H#b*c*d*@ . ", +" ]*e*f*g*h*i*)#W&j*`%k*'$s@l*m*U+]#0 ; ; ; ; ; @%n*o*X$O.p*a ; k q*~+U+# @ @ / r*s*t*a*a*a*a*a*a*a*a*S&S&S&S&S&S&S&S&S&S&5&5&5&5&5&5&5&5&5&5&S%S%S%S%S%S%S%S%S%}%}%}%}%}%}%}%}%}%}%r$r$r$r$r$r$r$r$r$r$u*v*w*x*y*($($($($($H#H#z*A*B*{$@ @ % ", +" ]*C*D*E*S$F*f%g%G*z#8#-*H*I*J*K*& L*`#; ; ; ; ; ; ; ; ^@< g&h&# @ @ M*N*O*P*Q*Q*a*a*a*a*a*a*a*a*a*a*S&S&S&S&S&S&S&S&S&S&5&5&5&5&5&5&5&5&5&5&S%S%S%S%S%S%S%S%S%}%}%}%}%}%}%}%}%}%}%r$r$r$r$r$r$R*S*T*g#@ @ @ @ 7*U*($($#$v*V*W*@ @ X*( ", +" ]*Y*Z*`* =>+v+.=+=@=#=$=%=&=*=I+==-=3$~.; ; ; a ;=>=4 R.@ @ m ,='=)=!=Q*Q*Q*Q*Q*a*a*a*a*a*a*a*a*a*a*S&S&S&S&S&S&S&S&S&S&5&5&5&5&5&5&5&5&5&5&S%S%S%S%S%S%S%S%S%}%}%}%}%}%}%}%}%}%}%r$r$'%~={=]=m @ 8$g&= - ; { -=^=/=(=# @ % @ ", +" ]*_=3*R@:=<=[=].X%Y%}=|=1=2=3=4=5=6=C+h&7='=D R.@ @ @ 8=9=0=a=Q*Q*Q*Q*Q*Q*Q*Q*Q*a*a*a*a*a*a*a*a*a*a*S&S&S&S&S&S&S&S&S&S&5&5&5&5&5&5&5&5&5&5&S%S%S%S%S%S%S%S%S%}%}%}%}%}%}%}%}%}%y%b=c=d=@ # 1&~ { ] ; ; ; ; ; K#h&# e=. ", +" ]*f=g=x@|#h=F*i==&]&j=k=M@0+l=m=5=5=n=o=# @ @ 3$X Y 3#p=p=p=Q*Q*Q*Q*Q*Q*Q*Q*Q*Q*a*a*a*a*a*a*a*a*a*a*S&S&S&S&S&S&S&S&S&S&5&5&5&5&5&5&5&5&5&5&S%S%S%S%S%S%S%S%S%}%}%}%}%}%q=r=s=n$@ @ 4 : < [ ; ; ; J.;+)+; ; ; ; ~.W $ ", +" ]*t=2%E$i%,+>+8&A%9&@=#=u=p*9#*=I+5=5=v=@ w=- p=p=p=p=p=p=p=Q*Q*Q*Q*Q*Q*Q*Q*Q*Q*a*a*a*a*a*a*a*a*a*a*S&S&S&S&S&S&S&S&S&S&5&5&5&5&5&5&5&5&5&5&S%S%S%S%S%S%S%S%S%}%x=y=z=4$# @ C+8 9 0 ; ; ; a A=B='#e C=D=E=F=; ; ; ^ G=# ", +" R$t=s&n.:$p+m&n&W%X%W.H=p&I=J=K=B 5=5=d@@ ~.p=p=p=p=p=p=p=p=Q*Q*Q*Q*Q*Q*Q*Q*Q*Q*a*a*a*a*a*a*a*a*a*a*S&S&S&S&S&S&S&S&S&S&5&5&5&5&5&5&5&5&5&5&S%S%S%S%S%S%S%L=M=i&8$@ m N=o p a ; ; ; ; ; |=O=s ++P=O P Q y Q=S#h+; ; 0 ]## ", +" ]*@ R=g@Q#I@&&|+J%H&9@t#I&J&S=T=*%5=5=d@@ ~.p=p=p=p=p=p=p=p=Q*Q*Q*Q*Q*Q*Q*Q*Q*Q*a*a*a*a*a*a*a*a*a*a*S&S&S&S&S&S&S&S&S&S&5&5&5&5&5&5&5&5&5&5&S%S%S%U=V=W=X=@ @ >&F G h+; ; ; ; ; ; ; ; ; K.Y=%.&.M *.=.P -.;.Z=`=; ; ; Z E $ ", +" @ -.-+- & %I V&@-#-Z#X&Y&$-%-l*&-5=d@@ ~.p=p=p=p=p=p=p=p=Q*Q*Q*Q*Q*Q*Q*Q*Q*Q*a*a*a*a*a*a*a*a*a*a*S&S&S&S&S&S&S&S&S&S&5&5&5&5&5&5&5&5&5&*-=-8*7*# @ L*X Y Z ; ; ; ; } }=`%[.; ; ; ; ; [=y@1.d.2.3.s.4.5.-.$@-- @; ; ; >*;-R. ", +" . >-,-'-$*%*&*t@4@k%b@A.|$##)-!-5=d@@ ~.p=p=p=p=p=p=p=p=Q*Q*Q*Q*Q*Q*Q*Q*Q*Q*a*a*a*a*a*a*a*a*a*a*S&S&S&S&S&S&S&S&S&S&5&5&5&5&5&~-{->%c#]-@ o=9$!.~.; ; ; ; a {.t@^.^-(./-(-_-*@; ; ; <.6#J p.}@q.r.|@1@@@#@:-<-; ; ; ; [ [-% ", +" . }-'-.@/*|+J%O+:*t#I&|-}*|*1-2-d@@ ~.p=p=p=p=p=p=p=p=Q*Q*Q*Q*Q*Q*Q*Q*Q*Q*a*a*a*a*a*a*a*a*a*a*S&S&S&S&S&S&S&S&S&S&5&5&3-4-5-3$@ % s$0.a.] ; ; ; ; ; ; Z.E$c.}@E.F.G.&+H.x i@##; ; ; f@g=m@n@}@o@e.|@6-s.$%#=; ; ; ; ; H '.6 ", +" )%7-8-g*9-0-)#W&j*X&k*'$s@l*&-d@@ ~.p=p=p=p=p=p=p=p=Q*Q*Q*Q*Q*Q*Q*Q*Q*Q*a*a*a*a*a*a*a*a*a*a*S&S&S&S&S&S&S&S&a-b-P&c-@ @ d-y.z.C ; ; ; ; ; ; ; ; ; ; b&e-3+++@+u $+%+7+*+*+J$Q@; ; ; 1=(@f-n@}@%.C$d P@{.; ; ; ; ; ; ; ] r&@ ", +" )%g-h-S$i-f%g%G*j-k--*H*I*l-d@@ ~.p=p=p=p=p=p=p=p=Q*Q*Q*Q*Q*Q*Q*Q*Q*Q*a*a*a*a*a*a*a*a*a*a*S&S&S&S&m-n-o-,=5 @ p-T.U.V.; ; ; '*Y+q-u=h+; ; ; ; ; ; p*`.2+3+4+5+u v 6+7+y g.r-; ; ; ; 2 H$h=;$O+^+; ; ; ; ; ; ; ; ; ; ; #a&@ ", +" R$s-t-u-v+.=t$v-#=$=%=w-*@x-@ ~.p=p=p=p=p=p=p=p=Q*Q*Q*Q*Q*Q*Q*Q*Q*Q*a*a*a*a*a*a*a*a*a*a*S&y-z-A--=@ 5 ~+{+]+a ; ; ; )+/+(+_+:+B-C-[#F=; ; ; ; ; a p+1#r+k+4+l+s+v w x D-m#; ; ; ; ; ; ; ; ; ; ; ; ; ; 0+/+V#E-9#; ; ; V@/&@ . ", +" R$R$F-[=].$#G-}=|=1=J.^%a&@ ~.p=p=p=p=p=p=p=p=Q*Q*Q*Q*Q*Q*Q*Q*Q*Q*a*a*a*a*a*a*a*t*s*H-/ @ @ X@f+g+h+; ; ; ; ; ; U&<$K I-*.4.5.-.J-=$c a ; ; ; ; *@K-1+$.K L M N L-N M-L@; ; ; ; ; ; ; ; ; ; ; 1 N-w+/.#+5.@@O-P-s#; ; a F @ ( ", +" R$e=Q-i=g%G*j=k=R-H*l=S-@ ~.p=p=p=p=p=p=p=p=Q*Q*Q*Q*Q*Q*Q*Q*Q*Q*a*a*a*0*T-U-V-% @ 3$D+E+F+; ; ; ; ; ; ; ; ; (#G+p.d.2.r.s.4.@@#@$@W-X-; ; ; ; ; B i*+&Y-k+K m%w+Z-'$; ; ; ; ; ; ; ; ; ; ; 0-`-k+5+u v 6+7+y ;D$.;; ; ; D%@ ( ", +" . )%+;A%9&@=#=;+A=9#8 @ ~.p=p=p=p=p=p=p=p=Q*Q*Q*Q*Q*Q*Q*Q*Q*!=@;#;V+o=@ ]-~$W+X+a ; A+W.$;%;J.; ; ; ; ; ; ; E$o.p.}@q.r.|@1@@@2@;.&;(#; ; ; ; ; ; Z.=*h*;@)+; ; ; ; ; ; ; ; ; ; ; ; ; ; *; +k+4+l+s+v w x y s%=;2 ; ; ; !.@ @ ", +" ( @ -;X%W.H=p&I=J=;;@ ~.p=p=p=p=p=p=p=p=Q*Q*Q*Q*Q*Q*3#>;a&3$@ % D ,;';^@; ; 9#);!;C$:@G.~;{;];Q@; ; ; ; ; l#.#,@n@A#o@e.f.g.=+2@^;A=; ; ; ; ; ; ; ; ; ; ; ; ; s@4@/;(;_;(#; ; ; ; ; ; i=#.$.K L M N O w Q R *#:;; ; ; ; 9 @ A@ ", +" @ <;9@..[;J&S=};@ ~.p=p=p=p=p=p=p=p=Q*Q*a=|;1;2;@ @ / d@e@k ; ; ; ; ; Y%`..+o@u $+%+&+*+Q 3;9#; ; ; ; ; 4;g=Y=C.t D.E.F.s+p@Y$3#; ; ; ; ; ; ; ; ; ; ^+**u@_+M 5;6;7;h 8;; ; ; ; ; s#.#9;$.%.&.M *.=.P -.0;u%; ; ; ; ; ~%@ ~@ ", +" ) a;b;H$Y&$-c;@ ~.p=p=p=p=p=p=2-d;'=e;m @ f;a#D@E@; ; ; ; ; ; ; ; ; G&J 3+4+5+u v 6+7+y #@7#(#; ; ; ; ; g;f*h;>#.+i;w+j;g;2 ; ; ; ; ; ; ; ; ; ; k;l;n@x&r.|@1@@@2@$@m;n;; ; ; ; ; <.o;|.1.%.2.3.S@p;U@^.9#; ; V@X+W@X@@ @ . ", +" @ q;&%A.|$r;@ ~.p=p=p=P*s;5-h&@ 5 1&Z@`@ #; ; ; ; ; ; ; ; ; ; ; ; G-0@r+k+4+l+s+v w x y C=t#; ; ; ; ; ; a t;u;v;b a ; ; ; ; ; ; ; ; ; ; ; ; ; b@e-n@}@o@e.|@g.=+2@w;x;u&; ; ; ; ; s#i*>$y;>#,#'#z@0+; 3 B+!#~#% @ @ {#@ ) ", +" % z;A;|-B;@ { z./&4 @ @ C;P.^#/#; ; ; (#r#G%<#D;E;H@; ; ; ; ; ; h+F;G;$.K L M N O w Q H;I;; ; ; ; ; ; ; ; ; ; ; ; ; ; 3#J;K;];L;B ; ; ; ; ; *$6#,@C.A#o@E.f.g.h.i.s%T ; ; ; ; ; ; ; s#I&I&$$U 0#a#b## @ @ m c#d#e#f#M*@ ", +" ( M;N;a%# # @ O;i#j#k#; ; ; ; ; A.P;m#M-u w Q Q;O-$;h+; ; ; ; ; ^%x@1+$.%.&.M *.#+R;S;M@; ; ; ; ; ; ; ; ; ; ; *%v-T;G@U;w$7+V;[+W;; ; ; ; ; g%2%Y=.+t D.#+F.G.h.=+m#; ; ; ; ; ; ; #X;/&Y;# % @ # g#E#F#G#H#H#H#H#M*@ ", +" # @ @ ' n J#K#V@; ; ; ; ; ; ; {.9+Z;L M N O P Q R `;6@2 ; ; ; ; ; B >b.y;>#%@.&S#;+h+; ; ; ; ; ; ; ; ; ; ; *@.>+>@>3.*.4.5.-.$@#>$>; ; ; ; ; ].`. +3+++@+%>&>Y#,#*>; ; ; ; `# $=>R.# @ @ .$+$@$#$H#H#H#H#H#H#H#H#M*@ ", +" ) $ @ @ E F G H ; ; ; ; ; ; ; ; ; ; ; |$B.1.%.&.M *.=.P -.;.->%&; ; ; ; ; ; ; s##=G-'$j ; ; ; ; ; ; ; ; ; ; ; ; ; ; ;>`.p.}@q.r.s.1@@@#@>>,>'>; ; ; ; ; k;.@m#)>s w+Z-'$j ; )$!$~$o=# @ @ {$]$^$/$($($($($H#H#H#H#H#H#H#H#M*@ ", +" @ # @ @ , _$Y Z ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; I+!>[&p.d.2.r.s.4.5.#@y .&; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; ; h+6#m@n@}@o@e.|@g.=+2@y ,#; ; ; ; ; ; ; L#|-` ; ^@1$2$3$@ @ @ # 4$5$6$7$($($($($($($($H#H#H#H#H#H#H#H#M*@ ", +" ( ~>R.@ U+9$!.> ; ; ; ; ; r@j*{>]>:.G$; ; ; ; ; ; ; ; ; q 6#o.p.}@q.r.|@1@@@5.9+; ; ; ; ; ; ; ; ; ; ; ; ; j @O.^>/>(>_>(#; ; ; ; ; ; :>.-,@C.A#o@E.f.g.h.2@9+(#; ; ; ; ; ; a ~.m$X@@ # @ @ n$o$p$q$r$($($($($($($($($($($H#H#H#H#H#H#H#H#M*@ ", +" % @ 9$<>] ; ; ; ; ; *%[>(@J$}>|>h.1>2>3>Z.; ; ; ; ; ; ; ; l.+-m@n@}@o@l+4>C$o*&#; ; ; ; ; ; ; ; ; ; ; +.-@E$5>@+6-=+2@N#6>'$; ; ; ; ; ; 7>g=Y=C.t D.E.s+8>%@9>; ; ; ; L$D@~+M$$ @ @ N$O$P$Q$r$r$r$r$r$($($($($($($($($($($H#H#H#H#H#H#H#H#M*@ ", +" @ # 8$0>; ; ; ; ; ; i%a>A#@+u $+%+7+*+-.i@9#; ; ; ; ; ; ; ; s#b>(+Y$Z$c>d>Q@; ; ; ; ; ; ; ; ; ; ; ; ; R@#%t D.E.f.G.h.i.y U$e>; ; ; ; ; ; 1=f>g>h>i>m#%%:>*%; h+< =%-%@ @ @ m ;%>%,%'%r$r$r$r$r$r$r$r$($($($($($($($($($($H#H#H#H#H#H#H#H#M*@ ", +" ) j>~@o=]@; ; ; ; ; e$ +3+4+5+u v 6+7+y x <#I+; ; ; ; ; ; ; ; ; a 8@-#; ; ; ; ; ; I+-#; ; ; ; ; ; ; ; x@Y=.+t @+#+F.%+&+H.*+J$B ; ; ; ; ; ; ; $$f@l#o+a (%: x.@ # @ % _%:%<%[%}%}%r$r$r$r$r$r$r$r$r$r$($($($($($($($($($($H#H#H#H#H#H#H#H#M*@ ", +" ) k>l>R.M$g+; ; ; ; **m>r+k+4+l+s+v w x y *#g;; ; ; ; ; ; ; ; ; ; ; ; ; ; ; {.u;^>4%h n>j@; ; ; ; ; ; o>2% +3+++@+u $+%+7+*+p>,.; ; ; ; ; ; ; ; V.9%0%R.@ @ @ a%b%c%d%}%}%}%}%}%}%r$r$r$r$r$r$r$r$r$r$($($($($($($($($($($H#H#H#H#H#H#H#H#M*@ ", +" ) q>**r>X*R.!@; ; ; o+s>y@$.K L M N O w Q t>u>; ; ; ; ; ; ; ; ; ; ; ; o+{&E$m%q.v w Q H.D=v>I+; ; ; ; j $*0@2+k+4+5+u v 6+s%w>`%; ; ; ; /#v%'.{$# @ @ -%w%x%y%}%}%}%}%}%}%}%}%}%}%r$r$r$r$r$r$r$r$r$r$($($($($($($($($($($H#H#H#H#H#H#H#H#M*@ ", +" ) x>(*_*y>~## S+; ; ; [.f*q+$.%.&.M I-z>A>K$q#; ; ; ; ; ; ; ; ; ; ; ; W#q+%.&.M *.=.P -.;.B>q%; ; ; ; ; h+m&`-r+K L L%&./.g$<-h+] N%a&3$@ @ @ m O%P%Q%R%S%S%S%}%}%}%}%}%}%}%}%}%}%r$r$r$r$r$r$r$r$r$r$($($($($($($($($($($H#H#H#H#H#H#H#H#M*@ ", +" ) C>i*)#W&Y%D># B+; ; ; j 7>R@S;,#+&G%|=(#; ; ; ; *@+.j ; ; ; ; ; ; ; ..E>1.%.2.3.*.4.5.-.$@F>b@; ; ; ; ; ; l.:=u@m#*&=*s@-&;&E # # @ @ >&,&'&)&S%S%S%S%S%S%S%}%}%}%}%}%}%}%}%}%}%r$r$r$r$r$r$r$r$r$r$($($($($($($($($($($H#H#H#H#H#H#H#H#M*@ ", +" ) G>F*f%g%G*H>I>@ d+; ; ; ; ; <.<.2 ; ; ; ; ^+/+(&e J>K>j$B ; ; ; ; ; 8@}.|.p.d.2.r.s.4.@@#@h.L>; ; ; ; ; ; ; ; h+Z |&1&m # @ @ .$2&3&4&5&5&S%S%S%S%S%S%S%S%S%}%}%}%}%}%}%}%}%}%}%r$r$r$r$r$r$r$r$r$r$($($($($($($($($($($H#H#H#H#H#H#H#H#M*@ ", +" )%M>>+v+.=+=@=A;N>@ !+a ; ; ; ; ; ; ; ; u%q@I.O>s.G.h.i. ;P>S=; ; ; ; ; | 6#m@n@}@q.e.|@1@Q>E.R>; ; ; ; ; H f&g&h&# @ @ {$i&j&k&5&5&5&5&5&5&S%S%S%S%S%S%S%S%S%}%}%}%}%}%}%}%}%}%}%r$r$r$r$r$r$r$r$r$r$($($($($($($($($($($H#H#H#H#H#H#H#H#M*@ ", +" )%S><=[=].X%Y%}=|=T>@ P.a ; ; ; ; ; ; 8@U>h@t @+#+F.%+&+H.y i;J.; ; ; ; ; |-s>w&C.A#q.L ^;d O..;; V@z&: x.# # @ # A&B&C&D&5&5&5&5&5&5&5&5&5&S%S%S%S%S%S%S%S%S%}%}%}%}%}%}%}%}%}%}%r$r$r$r$r$r$r$r$r$r$($($($($($($($($($($H#H#H#H#H#H#H#H#M*@ ", +" )%V>h=F*{&=&]&j=k=M@W>@ w.a ; ; ; ; ; (#.# +3+++@+u $+%+7+*+B#D$; ; ; ; ; ; B X>i%u@(+z@>@; 0 O&0%R.# @ @ n$P&Q&R&S&S&S&5&5&5&5&5&5&5&5&5&5&S%S%S%S%S%S%S%S%S%}%}%}%}%}%}%}%}%}%}%r$r$r$r$r$r$r$r$r$r$($($($($($($($($($($H#H#H#H#H#H#H#H#M*@ ", +" )%Y>,+>+8&A%9&@=#=u=p*Z># 8.] ; ; ; ; ; `>m>2+3+4+5+u v 6+7+y %@*$; ; ; ; ; ; ; ; ; /# *'.{$# @ @ N$.*+*@*S&S&S&S&S&S&S&5&5&5&5&5&5&5&5&5&5&S%S%S%S%S%S%S%S%S%}%}%}%}%}%}%}%}%}%}%r$r$r$r$r$r$r$r$r$r$($($($($($($($($($($H#H#H#H#H#H#H#H#M*@ ", +" )% ,p+m&n&W%X%W.o&p&I=J=.,% ).H ; ; ; ; (# &+,r+k+L l+s+O w v 9+*%; ; ; ; ; #)*w.3$# @ @ M$;%!*~*{*S&S&S&S&S&S&S&S&S&S&5&5&5&5&5&5&5&5&5&5&S%S%S%S%S%S%S%S%S%}%}%}%}%}%}%}%}%}%}%r$r$r$r$r$r$r$r$r$r$($($($($($($($($($($H#H#H#H#H#H#H#H#M*@ ", +" )%@,I@G&|+J%H&9@t#I&J&S=T=#,# V U ; ; ; ; (#I K%r+K 4+++^;w+j;>@; ; ; 6*;&E ( # @ % 7*8*9*0*a*a*a*a*S&S&S&S&S&S&S&S&S&S&5&5&5&5&5&5&5&5&5&5&S%S%S%S%S%S%S%S%S%}%}%}%}%}%}%}%}%}%}%r$r$r$r$r$r$r$r$r$r$($($($($($($($($($($H#H#H#H#H#$,c*5$@ . ", +" )%%, &U&I V&@-#-Z#X&Y&$-&,l**,R.D C ; ; ; ; ; A=<=X$(@O.b a ; k q*~+U+# @ @ / r*s*t*a*a*a*a*a*a*a*a*S&S&S&S&S&S&S&S&S&S&5&5&5&5&5&5&5&5&5&5&S%S%S%S%S%S%S%S%S%}%}%}%}%}%}%}%}%}%}%r$r$r$r$r$r$r$r$r$r$($($($($($($($($($($H#H#z*=,B*{$@ @ % ", +" )%-,'-$*%*&*t@=*k%b@A.|$##)-!-0=R.l k ; ; ; ; ; ; ; ; ^@;,r&h&# @ @ M*N*O*P*Q*Q*a*a*a*a*a*a*a*a*a*a*S&S&S&S&S&S&S&S&S&S&5&5&5&5&5&5&5&5&5&5&S%S%S%S%S%S%S%S%S%}%}%}%}%}%}%}%}%}%}%r$r$r$r$r$r$r$r$r$r$($($($($($($($($#$@$V*.$@ @ X*( ", +" )%>,b..@/*|+J%_*:*t#I&|-}*|*1-2-,,M$4 3 ; ; ; a ;=>=4 R.@ @ m ,='=)=!=Q*Q*Q*Q*Q*a*a*a*a*a*a*a*a*a*a*S&S&S&S&S&S&S&S&S&S&5&5&5&5&5&5&5&5&5&5&S%S%S%S%S%S%S%S%S%}%}%}%}%}%}%}%}%}%}%r$r$r$r$r$r$r$r$r$r$($($($($',),T*(=# @ % @ ", +" )%!,[$f*g*9-0-)#W&j*X&k*'$s@l*&-5=~,]-^={,'=D R.@ @ @ 8=9=0=a=Q*Q*Q*Q*Q*Q*Q*Q*Q*a*a*a*a*a*a*a*a*a*a*S&S&S&S&S&S&S&S&S&S&5&5&5&5&5&5&5&5&5&5&S%S%S%S%S%S%S%S%S%}%}%}%}%}%}%}%}%}%}%r$r$r$r$r$r$r$r$r$r$],^,{=O%R.@ . ~@. ", +" )%/,g=D*E*S$F*f%g%G*j-8#-*H*I*l-5=5=6*@ # @ @ 3$X Y 3#p=p=p=Q*Q*Q*Q*Q*Q*Q*Q*Q*Q*a*a*a*a*a*a*a*a*a*a*S&S&S&S&S&S&S&S&S&S&5&5&5&5&5&5&5&5&5&5&S%S%S%S%S%S%S%S%S%}%}%}%}%}%}%}%}%}%}%r$r$r$r$r$r$r$(,_,c={#@ @ R.( ", +" )%:,.-Z*`*<,>+v+.=t$v-#=$=%=w-*=I+5=5=n @ O&- p=p=p=p=p=p=p=Q*Q*Q*Q*Q*Q*Q*Q*Q*Q*a*a*a*a*a*a*a*a*a*a*S&S&S&S&S&S&S&S&S&S&5&5&5&5&5&5&5&5&5&5&S%S%S%S%S%S%S%S%S%}%}%}%}%}%}%}%}%}%}%r$r$r$[,},/=|,@ @ ~@@ ", +" 1,2,U>3*(@;$<=[=].$#Y%}=|=1=J.3=4=5=5=V @ /#p=p=p=p=p=p=p=p=Q*Q*Q*Q*Q*Q*Q*Q*Q*Q*a*a*a*a*a*a*a*a*a*a*S&S&S&S&S&S&S&S&S&S&5&5&5&5&5&5&5&5&5&5&S%S%S%S%S%S%S%S%S%}%}%}%}%}%}%}%}%}%[%=-3,4,% @ @ . ", +" ) 5,6,g=v&E*h=F*i=g%]&j=k=R-H*l=m=5=5=V @ /#p=p=p=p=p=p=p=p=Q*Q*Q*Q*Q*Q*Q*Q*Q*Q*a*a*a*a*a*a*a*a*a*a*S&S&S&S&S&S&S&S&S&S&5&5&5&5&5&5&5&5&5&5&S%S%S%S%S%S%S%S%S%}%}%}%}%}%7,8,9,0,U+@ @ ~@) ", +" @ t=2%E$i%,+>+v+A%9&@=#=;+p*9#*=I+5=V @ /#p=p=p=p=p=p=p=p=Q*Q*Q*Q*Q*Q*Q*Q*Q*Q*a*a*a*a*a*a*a*a*a*a*S&S&S&S&S&S&S&S&S&S&5&5&5&5&5&5&5&5&5&5&S%S%S%S%S%S%S%S%S%}%}%a,b,c,N$@ @ m # ", +" . d,e,:$p+m&V%W%X%W.H=p&I=J=K=f,5=V @ /#p=p=p=p=p=p=p=p=Q*Q*Q*Q*Q*Q*Q*Q*Q*Q*a*a*a*a*a*a*a*a*a*a*S&S&S&S&S&S&S&S&S&S&5&5&5&5&5&5&5&5&5&5&S%S%S%S%S%S%S%g,h,o$c-@ @ % @ ", +" . i,j,I@&&~&J%H&9@..[;J&S=T=k,5=V @ /#p=p=p=p=p=p=p=p=Q*Q*Q*Q*Q*Q*Q*Q*Q*Q*a*a*a*a*a*a*a*a*a*a*S&S&S&S&S&S&S&S&S&S&5&5&5&5&5&5&5&5&5&5&S%S%S%l,m,o-A&# @ 6 6 6 ", +" )%}-.@ %7&V&@-#-0$H$Y&$-%-n,&-V @ /#p=p=p=p=p=p=p=p=Q*Q*Q*Q*Q*Q*Q*Q*Q*Q*a*a*a*a*a*a*a*a*a*a*S&S&S&S&S&S&S&S&S&S&5&5&5&5&5&5&5&5&5&5&o,M=i&{$@ @ R.( ", +" 1,p,q,m&&*t@4@W.b@A.|$r,A+!-V @ /#p=p=p=p=p=p=p=p=Q*Q*Q*Q*Q*Q*Q*Q*Q*Q*a*a*a*a*a*a*a*a*a*a*S&S&S&S&S&S&S&S&S&S&5&5&5&5&5&5&s,t,u,v,@ @ R.# ", +" R$w,x,|+J%O+9@t#I&|-S=T=1-e;@ /#p=p=p=p=p=p=p=p=Q*Q*Q*Q*Q*Q*Q*Q*Q*Q*a*a*a*a*a*a*a*a*a*a*S&S&S&S&S&S&S&S&S&S&5&5&*-y,z,A,# @ % . ", +" )%B,C,0-)#W&j*X&k*'$s@l*e;@ /#p=p=p=p=p=p=p=p=Q*Q*Q*Q*Q*Q*Q*Q*Q*Q*a*a*a*a*a*a*a*a*a*a*S&S&S&S&S&S&S&S&D,E,F,c#m @ @ % 6 ", +" . R$G,**g%G*j-k--*H*I*1&@ /#p=p=p=p=p=p=p=p=Q*Q*Q*Q*Q*Q*Q*Q*Q*Q*a*a*a*a*a*a*a*a*a*a*S&S&S&S&S&H,I,w%{#@ @ m . ", +" )%. J,.=t$v-<*[*%=w-K,@ /#p=p=p=p=p=p=p=p=Q*Q*Q*Q*Q*Q*Q*Q*Q*Q*a*a*a*a*a*a*a*a*a*a*S&L,M,N,d-@ @ X*@ ", +" R$) O,$#G-`%|=1=J.9.@ /#p=p=p=p=p=p=p=p=Q*Q*Q*Q*Q*Q*Q*Q*Q*Q*a*a*a*a*a*a*a*P,Q,R,S,~@@ # ) ", +" @ @ T,G*z#k=R-H*U,@ /#p=p=p=p=p=p=p=p=Q*Q*Q*Q*Q*Q*Q*Q*Q*Q*a*a*a*V,W,X,Y,U+@ @ R.( ", +" 6 @ Z,@=#=;+A=7*@ /#p=p=p=p=p=p=p=p=Q*Q*Q*Q*Q*Q*Q*Q*Q*Q*`, '.'3$@ @ ' @ ", +" @ +'@'p&I=#'@ /#p=p=p=p=p=p=p=p=Q*Q*Q*Q*Q*Q*a=$'%'8=@ @ T+# ", +" @ &'I&M@*'@ /#p=p=p=p=p=p=p=p=Q*Q*='-';'>'# @ 6 @ ) ", +" e=I>,'''@ /#p=p=p=p=p=p=p=)'!'=%M*@ @ ' # ", +" # ~'N>@ /#p=p=p={']'d@^=@ @ m . ", +" ( @ @ 3#N%U-=># @ # ) ", +" @ @ @ @ @ % Q. ", +" % @ ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" ", +" "}; diff -r a1703c4f2990 -r 2ac1551ad2ab images/sheet.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/images/sheet.svg Sun Oct 31 20:07:33 2010 +0100 @@ -0,0 +1,78 @@ + + + + + + + + + image/svg+xml + + + + + + T + 2 + + diff -r a1703c4f2990 -r 2ac1551ad2ab images/throbber.gif Binary file images/throbber.gif has changed diff -r a1703c4f2990 -r 2ac1551ad2ab images/throbber.png Binary file images/throbber.png has changed diff -r a1703c4f2990 -r 2ac1551ad2ab images/throbber.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/images/throbber.svg Sun Oct 31 20:07:33 2010 +0100 @@ -0,0 +1,256 @@ + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + + + + + + + diff -r a1703c4f2990 -r 2ac1551ad2ab images/unit.svg --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/images/unit.svg Sun Oct 31 20:07:33 2010 +0100 @@ -0,0 +1,167 @@ + + + + + + + + + + + + + + + + + + + + + + image/svg+xml + + + + + + + + + + + + + + diff -r a1703c4f2990 -r 2ac1551ad2ab mo/es/LC_MESSAGES/pyArq-Presupuestos.mo Binary file mo/es/LC_MESSAGES/pyArq-Presupuestos.mo has changed diff -r a1703c4f2990 -r 2ac1551ad2ab mo/pyArq-Presupuestos.es.po --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mo/pyArq-Presupuestos.es.po Sun Oct 31 20:07:33 2010 +0100 @@ -0,0 +1,1117 @@ +# pyArq-Presupuestos translatión to Spanish. +# Copyright (C) 2010 Miguel Ángel Bárcena Rodríguez +# This file is distributed under the same license as the pyArq-Presupuestos +# package. +# Changes: +# - Initial translation: +# Miguel Ángel Bárcena Rodríguez , 2010 +# +# +msgid "" +msgstr "" +"Project-Id-Version: pyArq-Presupuestos 0.0.0\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2010-10-31 20:01+0100\n" +"PO-Revision-Date: 2010-01-11 12:24+0100\n" +"Last-Translator: Miguel Ángel Bárcena Rodríguez \n" +"Language-Team: Spanish\n" +"Language: Spannish\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" + +#: ../Generic/base.py:358 +msgid "Invalid code: $1" +msgstr "Código no válido: $1" + +#: ../Generic/base.py:373 +msgid "Synonyms ($1) must be a list, code: $2" +msgstr "Sinónimos ($1) debe ser una lista, código: $2" + +#: ../Generic/base.py:377 +msgid "Invalid Code in synomyms list ($1) code: $2" +msgstr "Código no válido es la lista de sinónimos ($1) código: $2" + +#: ../Generic/base.py:408 +msgid "Unit ($1) must be a string: $2" +msgstr "Unidad ($1) debe ser una cadena: $2" + +#: ../Generic/base.py:422 +msgid "Summary ($1) must be a string: $1" +msgstr "Resumen ($1) debe ser una cadena: $1" + +#: ../Generic/base.py:439 +msgid "Prices ($1) must be a list: $2" +msgstr "«Prices» ($1) debe ser una lista: $2" + +#: ../Generic/base.py:460 +msgid "Price ($1) must be a list with two items: $2" +msgstr "«Prece» ($1) debe ser una lista con dos elementos: $2" + +#: ../Generic/base.py:465 +msgid "Price must be a float number: $1" +msgstr "Precio debe ser un número de coma flotante: $1" + +#: ../Generic/base.py:475 +#, python-format +msgid "The record do not have this Price. Code: %s" +msgstr "El registro no tiene ese precio. Código: %s" + +#: ../Generic/base.py:481 +msgid "The record do not have this Price" +msgstr "El registro no tiene ese precio" + +#: ../Generic/base.py:496 +msgid "Parents ($1) must be a list: $2" +msgstr "Padres ($1) debe ser una lista: $2" + +#: ../Generic/base.py:500 ../Generic/base.py:512 +msgid "Invalid parent code ($1) in the record: $2" +msgstr "Código padre no válido ($1) en el registro: $2" + +#: ../Generic/base.py:528 +msgid "children ($1) must be a list, record: $2" +msgstr "Hijos ($1) debe ser una lista, registro: $2" + +#: ../Generic/base.py:532 +msgid "child ($1) must be a Decomposition object, record: $2" +msgstr "«child» ($1) debe ser una objeto «Decomposition», registro $2" + +#: ../Generic/base.py:580 +msgid "Text ($1) must be a string, record: $2" +msgstr "Texto ($1) debe ser una cadena, registro: $2" + +#: ../Generic/base.py:593 +msgid "sheet must be a Sheet instance" +msgstr "sheet debe ser una instancia de Sheet" + +#: ../Generic/base.py:606 +msgid "files must be a list: $1" +msgstr "«files» debe ser una lista: $1" + +#: ../Generic/base.py:617 ../Generic/base.py:632 +msgid "Incorrect path" +msgstr "Ruta incorrecta" + +#: ../Generic/base.py:622 +msgid "file must be a list or a File object: $1" +msgstr "«file» debe ser una lista o un objeto «File: $1" + +#: ../Generic/base.py:651 +msgid "labels must be a list" +msgstr "«labels» debe ser una lista" + +#: ../Generic/base.py:657 +msgid "label must be a string" +msgstr "«label» debe ser una cadena de texto." + +#: ../Generic/base.py:666 +msgid "Label must be a string" +msgstr "Label debe ser una cadena de texto." + +#: ../Generic/base.py:894 +msgid "Position must be a integer" +msgstr "«Position» debe ser un número entero" + +#: ../Generic/base.py:904 +msgid "BudgetMeasures atribute must be a list" +msgstr "El atributo «BudgetMeasures» debe ser una lista" + +#: ../Generic/base.py:907 +msgid "BudgetMeasures item must be a Measure " +msgstr "El atributo «BudgetMeasures» debe ser un objeto «Measure»" + +#: ../Generic/base.py:914 +msgid "Certification atribute must be a list or None" +msgstr "El atributo «Certification» debe ser una lista o «None»" + +#: ../Generic/base.py:920 +msgid "Real cost atribute must be a list or None" +msgstr "El atributo «Real cost» debe ser listo o «None»" + +#: ../Generic/base.py:926 +msgid "Cost goals atribute must be a list or None" +msgstr "El atributo «Cost goals» debe ser una lista o «None»" + +#: ../Generic/base.py:932 +msgid "Cost Planned atribute must be a list or None" +msgstr "El atributo «Cost Planned» debe ser una lista o «None»" + +#: ../Generic/base.py:1023 +msgid "Measure must be a float number. Type: $1" +msgstr "«Measure» debe ser un número de coma flotante. Tipo: $1" + +#: ../Generic/base.py:1034 +msgid "Lines must be a list" +msgstr "«Lines» debe ser una lista" + +#: ../Generic/base.py:1037 +msgid "Line must be a MeasureLine objetc" +msgstr "«Line» debe ser un objeto «MeasureLine»" + +#: ../Generic/base.py:1045 +msgid "Factor must be a float number |$1|" +msgstr "«Factor» debe ser un número de coma flotante |$1|" + +#: ../Generic/base.py:1057 +msgid "Yield must be a float number" +msgstr "«Yield» debe ser un número de coma flotante" + +#: ../Generic/base.py:1068 +msgid "Fixed must be boolean object" +msgstr "«Fixed» debe ser un objeto booleano" + +#: ../Generic/base.py:1137 +msgid "Type must be M or A. Type: $1" +msgstr "«Type» debe ser M o A. Type: $1" + +#: ../Generic/base.py:1269 +msgid " Parcial Subtotal must be a float number. Parcial: $1" +msgstr "«Parcial Subtotal» debe ser un número de coma flotante. Parcial: $1" + +#: ../Generic/base.py:1276 +msgid " Acumulated Subtotal must be a float number. Parcial: $1" +msgstr " «Acumulate Subtotal» debe ser un número de coma flotante. Parcial: $1" + +#: ../Generic/base.py:1311 +msgid "Invalid measure line type ($1)" +msgstr "Tipo de línea de medición no válido ($1)" + +#: ../Generic/base.py:1316 +msgid "Measure Comment must be a string ($1)" +msgstr "El comentario de la medición debe ser una cadena ($1)" + +#: ../Generic/base.py:1322 +msgid "Invalid Measure Units ($1)" +msgstr "Unidades de medición no vacías ($1)" + +#: ../Generic/base.py:1334 +msgid "Invalid Measure length ($1)" +msgstr "Longitud de medición no válida ($1)" + +#: ../Generic/base.py:1346 +msgid "Invalid Measure Width ($1)" +msgstr "Anchura de medición no válida ($1)" + +#: ../Generic/base.py:1358 +msgid "Invalid Measure Height ($1)" +msgstr "Altura de medición no válida ($1)" + +#: ../Generic/base.py:1369 +msgid "Formula must be a string ($1)" +msgstr "«Formula» debe ser una cadena de texto ($1)" + +#: ../Generic/base.py:1372 +msgid "There is invalid charactersin formula ($1)" +msgstr "Hay caracteres inválidos en la fórmula ($1)" + +#: ../Generic/base.py:1437 ../Generic/base.py:2406 +msgid "'a' value must be a float number" +msgstr "El valor «a» debe ser un número de coma flotante" + +#: ../Generic/base.py:1441 ../Generic/base.py:2410 +msgid "'b' value must be a float number" +msgstr "El valor «b» debe ser un número de coma flotante" + +#: ../Generic/base.py:1445 ../Generic/base.py:2414 +msgid "'c' value must be a float number" +msgstr "El valor «c» debe ser un número de coma flotante" + +#: ../Generic/base.py:1449 ../Generic/base.py:2418 +msgid "'d' value must be a float number" +msgstr "El valor «d» debe ser un número de coma flotante" + +#: ../Generic/base.py:1477 ../Generic/base.py:2446 +msgid "Invalid formula" +msgstr "Fórmula no válida" + +#: ../Generic/base.py:1727 +msgid "sheet_dict must be a dictionay" +msgstr "sheet_dict debe ser un diccionario." + +#: ../Generic/base.py:1744 ../Generic/base.py:1750 +msgid "sheet field must be a string" +msgstr "El ambito del pliego debe ser una cadena de texto" + +#: ../Generic/base.py:1746 +msgid "section_dict must be a dictionary" +msgstr "section_dict debe ser un dicionario" + +#: ../Generic/base.py:1752 +msgid "sheet section must be a string" +msgstr "La seccion del pliego debe ser una cadena de texto" + +#: ../Generic/base.py:1754 +msgid "sheet paragraph must be a string" +msgstr "El parrafo del pliego debe ser una cadena de texto" + +#: ../Generic/base.py:1992 +msgid "Owner must be a string" +msgstr "«Owner» debe ser una cadena de texto." + +#: ../Generic/base.py:2008 +#, python-format +msgid "Invalid Date: %s" +msgstr "Fecha inválida: %s" + +#: ../Generic/base.py:2019 +msgid "Comment must be a string" +msgstr "El comentario debe ser una cadena" + +#: ../Generic/base.py:2035 +msgid "Budget type must be 1, 2, 3 or 4." +msgstr "El tipo de presupuesto debe ser 1, 2, 3 o 4" + +#: ../Generic/base.py:2047 +msgid "Certificate order must be a integer." +msgstr "«Certificate order» debe ser un número entero" + +#: ../Generic/base.py:2062 +msgid "Budget certificate Date must be a valid Date." +msgstr "«Budget certificate Date» debe ser una fecha válida" + +#: ../Generic/base.py:2076 +msgid "Invalid title list format" +msgstr "Formato de lista de rótulos no válida" + +#: ../Generic/base.py:2105 +msgid "Invalid Index Title" +msgstr "Índice de rótulo no válido" + +#: ../Generic/base.py:2140 +msgid "Decimal Key error" +msgstr "Clave de decimales errónea" + +#: ../Generic/base.py:2176 +msgid "Invalid Percentage key" +msgstr "Clave de porcentajes no válida" + +#: ../Generic/base.py:2291 +msgid "Invalid parent code: $1" +msgstr "Código del padre inválido: $1" + +#: ../Generic/base.py:2294 +msgid "Invalid child code: $1" +msgstr "Código de hijo inválido: $1" + +#: ../Generic/base.py:2297 +msgid "Invalid position in measure $1, in code $2" +msgstr "Posición no válida en medición $1, en código $2" + +#: ../Generic/base.py:2305 +msgid "" +"Circular Decomposition, parent code: $1, child code: $2, repeated code: $3" +msgstr "" +"Descomposición circular, código padre: $1, código hijo: $2, código repetido: " +"$3" + +#: ../Generic/base.py:2360 ../Generic/base.py:2372 +msgid "" +"Error: Invalid child position in decomposition. Parent code: $1 Child code: " +"$2 Position: $3" +msgstr "" +"Error: Posición del hijo no válida en descomposición. Código padre: $1 " +"Código hijo: $2 Posición: $3" + +#: ../Generic/base.py:2366 +msgid "Error: Empty child code. Parent code: $1 Position: $2" +msgstr "Error: Código hijo vacío. Código padre: $1 Posición: $2" + +#: ../Generic/base.py:2457 +msgid "Invalid code" +msgstr "Código no válido" + +#: ../Generic/base.py:2467 +msgid "Invalid record: $1" +msgstr "Registro no válido: $1" + +#: ../Generic/base.py:2530 +msgid "Only can be one root record" +msgstr "Sólo puede haber un registro raíz" + +#: ../Generic/base.py:2624 ../Generic/base.py:2630 +msgid "This record does not exits" +msgstr "Este registro no existe" + +#: ../Generic/base.py:2627 +msgid "Path item must be a integer" +msgstr "El elemento del camino debe ser un número entero" + +#: ../Generic/base.py:2632 +msgid "Path must be a not empty tuple: $1" +msgstr "El camino debe ser una tupla no vacía: $1" + +#: ../Generic/base.py:2683 +msgid "The sheet code must be a string" +msgstr "El código del pliego debe ser una cadena de texto" + +#: ../Generic/base.py:2685 +msgid "The sheet title must be a string" +msgstr "El título del pliego debe ser una cadena de texto." + +#: ../Generic/base.py:2693 +msgid "The sheet sections must be a dictionary" +msgstr "La sección del pliego debe ser un dicionario" + +#: ../Generic/base.py:2698 +msgid "The field code must be a string" +msgstr "El código del ámbito debe ser una cadena de texto" + +#: ../Generic/base.py:2700 +msgid "The field title must be a string" +msgstr "El título del ámbito debe ser una cadena de texto." + +#: ../Generic/base.py:2708 +msgid "The sheet field must be a dictionary" +msgstr "El ámbito del pliego debe ser un dicionario." + +#: ../Generic/base.py:2713 +msgid "The paragraph code must be a string" +msgstr "El código del parrafo debe ser una cadena de texto." + +#: ../Generic/base.py:2715 +msgid "The paragraph text must be a string" +msgstr "El texto del parrafo debe ser una cadena de texto" + +#: ../Generic/base.py:2723 +msgid "The paragraph dict must be a dictionary" +msgstr "El dicionario del parrafo debe ser un diccionario" + +#: ../Generic/base.py:2728 ../Generic/base.py:2752 ../Generic/base.py:2882 +#: ../Generic/base.py:2906 ../Generic/base.py:2928 +msgid "The record_code code must be a string" +msgstr "record_code debe ser una cadena de texto" + +#: ../Generic/base.py:2730 +msgid "The field must be a string" +msgstr "El ámbito debe ser una cadena de texto." + +#: ../Generic/base.py:2732 +msgid "The section dict must be a dictionary" +msgstr "El dicionario de la seccion debe ser un dicionario." + +#: ../Generic/base.py:2736 +msgid "" +"Error: The budget do not have this record code and can not be added the " +"sheet text in the field $1. Record Code: $2" +msgstr "" +"Error: El presupuesto no tiene ese codigo de registro y no puede añadirse el " +"texto del pliego en el ambito $1. Código del registro: $2" + +#: ../Generic/base.py:2754 +msgid "The filename must be a string" +msgstr "El nombre del fichero debe ser una cadena de texto." + +#: ../Generic/base.py:2758 +msgid "" +"Error: The budget do not have the record code $1 and can not be added the " +"file: $2" +msgstr "" +"Error: El presupuesto no tiene el código de registro $1 y no puede añadirse " +"el fichero: $2" + +#: ../Generic/base.py:2768 +msgid "The company code must be a string" +msgstr "El código de la entidad debe ser una cadena de texto." + +#: ../Generic/base.py:2770 +msgid "The summary must be a string" +msgstr "El resumen debe ser una cadena de texto" + +#: ../Generic/base.py:2772 ../Generic/base.py:2800 +msgid "The name must be a string" +msgstr "El nombre debe ser una cadena de texto." + +#: ../Generic/base.py:2774 +msgid "The name must be a list" +msgstr "El nombre debe ser una lista" + +#: ../Generic/base.py:2778 +msgid "The office must be a list" +msgstr "«The office» debe ser una lista" + +#: ../Generic/base.py:2780 +msgid "The office must be a 10 items list" +msgstr "«The office» debe ser una lista de 10 elementos" + +#: ../Generic/base.py:2783 +msgid "This office item must be a string" +msgstr "El elemento de la oficina debe ser una cadena de texto." + +#: ../Generic/base.py:2787 +msgid "This office item must be a list" +msgstr "Este elemento de la oficina debe ser una lista" + +#: ../Generic/base.py:2802 +msgid "The web must be a string" +msgstr "La web debe ser una cadena de texto." + +#: ../Generic/base.py:2804 +msgid "The email must be a string" +msgstr "El email debe ser una cadena de texto." + +#: ../Generic/base.py:2814 +msgid "The tecnical info code must be a string" +msgstr "La información técnica debe ser una cadena de texto." + +#: ../Generic/base.py:2816 +msgid "The tecnical info description must be a string" +msgstr "La descripción de la información técnica debe ser una cadena de texto" + +#: ../Generic/base.py:2819 +msgid "The tecnical info unit must be a string" +msgstr "La unidad de la información técnica debe ser una cadena de texto." + +#: ../Generic/base.py:2866 +msgid "The label must be a string" +msgstr "La etiqueta debe ser una cadena de texto." + +#: ../Generic/base.py:2884 +msgid "The parametric select comment must be a string" +msgstr "El comentario de selección paramétrico debe ser una cadena de texto" + +#: ../Generic/base.py:2887 +msgid "" +"Error: The budget do not have the record code $1 and can not be added the " +"Parametric select comment: $2" +msgstr "" +"Error: El presupuesto no tiene el código de registro $1 y no puede añadirse " +"el comentario de selección paramétrico: $2" + +#: ../Generic/base.py:2894 +msgid "" +"Error: The Record $1 is not a Parametric Record and can not have Parametric " +"comment" +msgstr "" +"Error: El registro $1 no es un registro paramétrico y no puede tener " +"comentario paramétrico." + +#: ../Generic/base.py:2908 +msgid "The summary record must be a string" +msgstr "El resumen del registro debe ser una cadena de texto" + +#: ../Generic/base.py:2910 +msgid "" +"Error: The budget do not have the record code $1 and can not be seted the " +"summary: $2" +msgstr "" +"Error: El presupuesto no tiene el codigo de registro $1 y no puede fijarse " +"el resumen: $2" + +#: ../Generic/base.py:2916 +msgid "" +"Error: The Record $1 is not a Parametric Record and can not have Parametric " +"summary" +msgstr "" +"Error: El registro $1 no es un registro paramétrico y no puede tener resumen " +"paramétrico." + +#: ../Generic/base.py:2930 +msgid "The text record must be a string" +msgstr "El texto del registro debe ser una cadena de texto" + +#: ../Generic/base.py:2932 +msgid "" +"Error: The budget do not have the record code $1 and can not be seted the " +"text: $2" +msgstr "" +"Error: El presupuesto no tiene el codigo de registro $1 y no puede fijarse " +"el texto: $2" + +#: ../Generic/base.py:2938 +msgid "" +"Error: The Record $1 is not a Parametric Record and can not have Parametric " +"text" +msgstr "" +"Error: El registro $1 no es un registro paramétrico y no puede tener texto " +"paramétrico." + +#: ../Generic/base.py:3396 +msgid "Invalid Hierarchy ($1) The hierarchy must be -1, 0, 1 or 2" +msgstr "Categoría no válida ($1) La categoria debe ser -1, 0, 1 or 2" + +#: ../Generic/base.py:3403 +msgid "Invalid type ($1),the type must be (empty string,0,1,2,3)" +msgstr "Tipo inválido ($1), el tipo debe ser (cadena vacía, 0, 1, 2, 3)" + +#: ../Generic/base.py:3412 +msgid "" +"Invalid subtype ($1), The subtype must one in (empty string, EA, EU, EC, EF, " +"OB, PA, PU, H, Q, %, MC, MCr, MM, MS, ME, MCu, MAl, ML, M)" +msgstr "" +"Subtipo inválido ($1), el subtipo debe ser uno de (cadena vacía, EA, EU, EC, " +"EF, OB, PA, PU, H, Q, %, MC, MCr, MM, MS, ME, MCu, MAl, ML, M)" + +#: ../Generic/fiebdc.py:151 +msgid "Invalid code, it must be a string" +msgstr "Código no válido, debe ser una cadena" + +#: ../Generic/fiebdc.py:157 +msgid "The code '$1' have invalid characters." +msgstr "El código «$1» tiene caracteres inválidos." + +#: ../Generic/fiebdc.py:170 +msgid "The code '$1' contains special characters repeated." +msgstr "El código «$1» contiene caractectes especiales repetidos." + +#: ../Generic/fiebdc.py:391 +msgid "" +"The 'V' record (Property and Version) must be the first record in the file " +"but it is the number: $1" +msgstr "" +"El registro «V» (Propiedad y versión) debe ser el primer registro en el " +"archivo pero es el número: $1" + +#: ../Generic/fiebdc.py:394 +msgid "The default values were taken and this V record is ignored" +msgstr "El valor por amisión ha sido tomado y el registro V es ignorado" + +#: ../Generic/fiebdc.py:428 +#, python-format +msgid "FIEBDC format: %s" +msgstr "Formato FIEBDC: %s" + +#: ../Generic/fiebdc.py:437 +#, python-format +msgid "FIEBDC file generated by %s" +msgstr "Fichero FIEBDC generado por %s" + +#: ../Generic/fiebdc.py:776 +msgid "Record C without a valid code" +msgstr "Registro C sin un código válido" + +#: ../Generic/fiebdc.py:852 ../Generic/fiebdc.py:864 ../Generic/fiebdc.py:892 +msgid "Incorrect type ($1) in the code $2" +msgstr "Tipo incorrecto ($1) en el código $2" + +#: ../Generic/fiebdc.py:943 +msgid "" +"ValueError loadig the descomposition of the record $1, the factor of the " +"child $2 must be a float number and can not be $3, seted default value 1.0" +msgstr "" +"Error de valor al cargar la descomposición del registro $1, el factor del " +"hijo $2 debe ser un número de coma flotante y no puede ser $3, fijado el " +"valor por omisión 1.0" + +#: ../Generic/fiebdc.py:954 +msgid "" +"ValueError loading the descomposition of the record $1, the yield of the " +"child $2, must be a float number and cannot be $3, seted default value 1.0" +msgstr "" +"Error de valor cargando la descomposición del registro $1, el rendimiento " +"del hijo $2, debe ser un número de coma flotante y no puede ser $3, fijado " +"el valor por omisión 1.0" + +#: ../Generic/fiebdc.py:1041 +msgid "Invalid codes in $1 record, codes $2" +msgstr "Códigos no válidos en registro $1, códigos $2" + +#: ../Generic/fiebdc.py:1045 +msgid "Empty child code in $1 record, codes: $2" +msgstr "Código de hijo vacío en el registro $1, códigos: $2" + +#: ../Generic/fiebdc.py:1058 +msgid "Invalid path in $1 record, codes $2" +msgstr "Camino no válido en el registro $1, códigos $2" + +#: ../Generic/fiebdc.py:1069 +msgid "Invalid Total Measure value in $1 record, codes $2" +msgstr "Total de medición no válido en el registro $1, códigos $2" + +#: ../Generic/fiebdc.py:1089 +msgid "" +"The comment is not a formula or its have invalid characters, in the $1 " +"record, codes $2" +msgstr "" +"El comentario no es una fórmula o tiene caracteres inválidos, en el registro " +"$1, códigos $2" + +#: ../Generic/fiebdc.py:1403 ../Generic/fiebdc.py:1657 +msgid "The file $1 do not exist" +msgstr "El archivo $1 no existe" + +#: ../Generic/fiebdc.py:1763 ../Generic/fiebdc.py:1770 +msgid "PyArq hates parametric DLLs" +msgstr "PyArq odia las DLLs paramétricas" + +#: ../Generic/fiebdc.py:1980 +msgid "Loading file $1" +msgstr "Cargando archivo $1" + +#: ../Generic/fiebdc.py:2000 +msgid "This codepage do not exist in FIEBDC3! Default codepage: $1" +msgstr "¡Esta codificación no existe en FIEBDC3! Codificación por omisión: $1" + +#: ../Generic/fiebdc.py:2004 +msgid "This V record dot have a codepage! Default codepage: $1" +msgstr "¡Este registro V no define codificación! Codificación por omisión: $1" + +#: ../Generic/fiebdc.py:2008 +msgid "Not 'V' record in File! Default codepage: $1" +msgstr "¡No hay registro «V» en el archivo! Codificación por omisión: $1" + +#: ../Generic/fiebdc.py:2049 +msgid "Cancelled process" +msgstr "Proceso cancelado" + +#: ../Generic/fiebdc.py:2052 +msgid "Time to load: $1 seconds" +msgstr "Tiempo de carga: $1 segundos" + +#: ../Generic/fiebdc.py:2054 +msgid "Records/Valid Records: $1/$2" +msgstr "Registro/Registros válidos: $1/$2" + +#: ../Generic/fiebdc.py:2057 +msgid "$1 unsuported record type O: Comercial Relationship" +msgstr "" + +#: ../Generic/fiebdc.py:2060 +msgid "This file is not a valid FIBDC3 file" +msgstr "El archivo no es un archivo FIEBDC3 válido" + +#: ../Generic/fiebdc.py:2097 +msgid "Testing budget ..." +msgstr "Comprobando presupuesto ..." + +#: ../Generic/fiebdc.py:2115 +msgid "End Test" +msgstr "Comprobación finalizada" + +#: ../Gtk/gui.py:69 +msgid "The icon file does not exist. '$1'" +msgstr "El archivo de icono no existe. «$1»" + +#: ../Gtk/gui.py:75 +msgid "pyArq-Presupuestos running on $1" +msgstr "pyArq-Presupuestos ejecutandose en $1" + +#: ../Gtk/gui.py:164 +msgid "_File" +msgstr "_Archivo" + +#: ../Gtk/gui.py:165 +msgid "_Import Fiebdc" +msgstr "_Importar Fiebdc" + +#: ../Gtk/gui.py:167 +msgid "_Close" +msgstr "_Cerrar" + +#: ../Gtk/gui.py:169 +msgid "_View" +msgstr "_Ver" + +#: ../Gtk/gui.py:170 +msgid "_Text" +msgstr "_Texto" + +#: ../Gtk/gui.py:172 +msgid "_Test" +msgstr "_Pruebas" + +#: ../Gtk/gui.py:174 +msgid "Import Fiebdc _price database" +msgstr "Importar base de _precios Fiebdc" + +#: ../Gtk/gui.py:176 +msgid "_Open price database" +msgstr "_Abrir base de precios" + +#: ../Gtk/gui.py:248 +msgid "Saving file: $1" +msgstr "Guardando archivo: $1" + +#: ../Gtk/gui.py:253 +msgid "Saving time: $1 seconds" +msgstr "Tiempo de guardado: $1 segundos" + +#: ../Gtk/gui.py:321 +msgid "Loading file: $1:" +msgstr "Cargando archivo: $1" + +#: ../Gtk/gui.py:468 ../Gtk/importFiebdc.py:224 +msgid "Time: 0s" +msgstr "Tiempo: 0s" + +#: ../Gtk/gui.py:532 ../Gtk/importFiebdc.py:303 +msgid "Time: $1" +msgstr "Tiempo: $1" + +#: ../Gtk/gui.py:809 +msgid "The value must be a list" +msgstr "El valor debe ser una lista" + +#: ../Gtk/gui.py:812 +msgid "Incorrect len" +msgstr "Longuitud incorrecta" + +#: ../Gtk/gui.py:840 +msgid "Incorrect item $1" +msgstr "Elemento incorrecto $1" + +#: ../Gtk/gui.py:854 +msgid "The budget does not have the path record: $1" +msgstr "El presupuesto no tiene el registro con camino: $1" + +#: ../Gtk/gui.py:989 +msgid "Decomposition" +msgstr "Descomposición" + +#: ../Gtk/gui.py:990 ../Gtk/gui.py:2006 +msgid "Description" +msgstr "Descripción" + +#: ../Gtk/gui.py:991 ../Gtk/gui.py:2008 +msgid "Measure" +msgstr "Medición" + +#: ../Gtk/gui.py:992 +msgid "Sheet of Conditions" +msgstr "Pliego de condicones" + +#: ../Gtk/gui.py:993 +msgid "Files" +msgstr "Archivos" + +#: ../Gtk/gui.py:994 +msgid "Companies" +msgstr "Entidades" + +#: ../Gtk/gui.py:1355 +msgid "The item must be a widget object." +msgstr "El elemento debe ser un objeto «widget»." + +#: ../Gtk/gui.py:1361 +msgid "Invalid orientation." +msgstr "Orientación no válida" + +#: ../Gtk/gui.py:1736 ../Gtk/gui.py:2370 ../Gtk/gui.py:3780 +msgid "Argument must be a Budget object" +msgstr "El argumento debe ser un objeto «Budget»" + +#: ../Gtk/gui.py:1745 +msgid "DecompositionList.__init__: Record path can not be None" +msgstr "«DecompositionList.__init__:» La ruta del registro no puede ser «None»" + +#: ../Gtk/gui.py:1776 +msgid "a" +msgstr "a" + +#: ../Gtk/gui.py:2003 ../Gtk/gui.py:3840 +msgid "Code" +msgstr "Código" + +#: ../Gtk/gui.py:2004 +msgid "Unit" +msgstr "Unidad" + +#: ../Gtk/gui.py:2010 +msgid "Price" +msgstr "Precio" + +#: ../Gtk/gui.py:2012 +msgid "Amount" +msgstr "Importe" + +#: ../Gtk/gui.py:2024 ../Gtk/gui.py:2502 +msgid "Invalid path" +msgstr "Camino no válido" + +#: ../Gtk/gui.py:2375 +msgid "Record path must be a tuple" +msgstr "La posición del registro debe ser una tupla" + +#: ../Gtk/gui.py:2519 +msgid "measure must be a Measure object. Type: $1" +msgstr "«measure» debe ser un objeto «Measure». Tipo: $1" + +#: ../Gtk/gui.py:2530 ../Gtk/gui.py:3855 ../Gtk/gui.py:4182 +msgid "Type" +msgstr "Tipo" + +#: ../Gtk/gui.py:2531 +msgid "Comment" +msgstr "Comentario" + +#: ../Gtk/gui.py:2532 +msgid "" +"N\n" +"(a)" +msgstr "" +"N\n" +"(a)" + +#: ../Gtk/gui.py:2533 +msgid "" +"Length\n" +"(b)" +msgstr "" +"Longitud\n" +"(b)" + +#: ../Gtk/gui.py:2534 +msgid "" +"Width\n" +"(c)" +msgstr "" +"Anchura\n" +"(c)" + +#: ../Gtk/gui.py:2535 +msgid "" +"Height\n" +"(d)" +msgstr "" +"Altura\n" +"(d)" + +#: ../Gtk/gui.py:2536 +msgid "Formula" +msgstr "Fórmula" + +#: ../Gtk/gui.py:2537 +#, python-format +msgid "" +"Parcial\n" +"[%s]" +msgstr "" +"Parcial\n" +"[%s]" + +#: ../Gtk/gui.py:2538 +msgid "Subtotal" +msgstr "Subtotal" + +#: ../Gtk/gui.py:2946 ../Gtk/gui.py:2968 +msgid "Description text of the record $1" +msgstr "Texto descriptivo del registro $1" + +#: ../Gtk/gui.py:3125 +msgid "Sheet of Conditions of the record $1" +msgstr "Pliego de condiciones del registro $1" + +#: ../Gtk/gui.py:3145 +msgid "Field" +msgstr "Ámbito" + +#: ../Gtk/gui.py:3167 +msgid "Section" +msgstr "Sección" + +#: ../Gtk/gui.py:3320 +msgid "Sheet2 of Conditions of the record $1" +msgstr "Pliego2 de condiciones del registro $1" + +#: ../Gtk/gui.py:3696 +msgid "$1 text" +msgstr "Texto de $1" + +#: ../Gtk/gui.py:3841 +msgid "Code that define the company" +msgstr "Código que define la entidad" + +#: ../Gtk/gui.py:3842 +msgid "Summary" +msgstr "Resumen" + +#: ../Gtk/gui.py:3843 +msgid "Summary of the company name" +msgstr "Resumen del nombre de la entidad" + +#: ../Gtk/gui.py:3844 ../Gtk/gui.py:3860 +msgid "Name" +msgstr "Nombre" + +#: ../Gtk/gui.py:3845 +msgid "Complete name" +msgstr "Nombre completo" + +#: ../Gtk/gui.py:3846 +msgid "CIF" +msgstr "CIF" + +#: ../Gtk/gui.py:3847 +msgid "Fiscal identifier number" +msgstr "Número de identificación fiscal" + +#: ../Gtk/gui.py:3848 +msgid "Web" +msgstr "Web" + +#: ../Gtk/gui.py:3849 +msgid "Company web page" +msgstr "Página web de la entidad" + +#: ../Gtk/gui.py:3850 +msgid "Email" +msgstr "Email" + +#: ../Gtk/gui.py:3851 +msgid "Company email" +msgstr "Correo electrónico de la entidad" + +#: ../Gtk/gui.py:3856 +msgid "" +"Type of Office:\n" +" C: Central office\n" +" D: Local office\n" +" R: Performer" +msgstr "" +"Type of Office:\n" +" C: Oficina central\n" +" D: Delegación\n" +" R: Representante" + +#: ../Gtk/gui.py:3861 +msgid "Office name" +msgstr "Nombre de la oficina" + +#: ../Gtk/gui.py:3862 +msgid "Address" +msgstr "Dirección" + +#: ../Gtk/gui.py:3863 +msgid "Postal code" +msgstr "Código postal" + +#: ../Gtk/gui.py:3864 +msgid "Town" +msgstr "Ciudad" + +#: ../Gtk/gui.py:3865 +msgid "Province" +msgstr "Provincia" + +#: ../Gtk/gui.py:3866 +msgid "Country" +msgstr "Ciudad" + +#: ../Gtk/gui.py:3867 +msgid "Phone" +msgstr "Teléfono" + +#: ../Gtk/gui.py:3868 +msgid "Phone numbers of the office" +msgstr "Teléfono de la officina" + +#: ../Gtk/gui.py:3869 +msgid "Fax" +msgstr "Fax" + +#: ../Gtk/gui.py:3870 +msgid "Fax numbers of the office" +msgstr "Fax de la oficina" + +#: ../Gtk/gui.py:3871 +msgid "Contact person" +msgstr "Persona de contacto" + +#: ../Gtk/gui.py:3872 +msgid "Contact persons in the office" +msgstr "Persona de contacto en la oficina" + +#: ../Gtk/gui.py:3876 +msgid "Unknow Option Type" +msgstr "Tipo de opcion desconocida" + +#: ../Gtk/gui.py:4120 +msgid "Boolean" +msgstr "Booleano" + +#: ../Gtk/gui.py:4121 +msgid "Integer" +msgstr "Entero" + +#: ../Gtk/gui.py:4122 +msgid "Text" +msgstr "Texto" + +#: ../Gtk/gui.py:4123 +msgid "Color" +msgstr "Color" + +#: ../Gtk/gui.py:4124 +msgid "List" +msgstr "Lista" + +#: ../Gtk/gui.py:4156 +msgid "Option name" +msgstr "Nombre de Opción" + +#: ../Gtk/gui.py:4169 +msgid "Value" +msgstr "Valor" + +#: ../Gtk/gui.py:4207 +msgid "Description:" +msgstr "Descripción:" + +#: ../Gtk/gui.py:4369 +msgid "Option values must be strings" +msgstr "Los valores de la opción deben ser cadenas" + +#: ../Gtk/gui.py:4371 +msgid "Option must be a tuple with 4 items" +msgstr "La opcion debe ser una tupla de 4 elementos" + +#: ../Gtk/gui.py:4373 +msgid "Option list must be a list" +msgstr "La lista de opciones debe ser una lista lista" + +#: ../Gtk/gui.py:4393 +msgid "Icorrect type, must be boolean" +msgstr "Tipo erroneo, debe ser booleano" + +#: ../Gtk/gui.py:4398 +msgid "Icorrect type, must be integer" +msgstr "Tipo erroneo, debe ser un número entero" + +#: ../Gtk/gui.py:4411 +msgid "Icorrect type, must be string" +msgstr "Tipo erroneo, debe ser una cadena de texto" + +#: ../Gtk/gui.py:4424 +msgid "Icorrect type, must be list" +msgstr "Tipo erroneo, debe ser una lista" + +#: ../Gtk/gui.py:4430 +msgid "Icorrect type, must be a parseable color" +msgstr "Tipo erroneo, debe ser un color parseable" + +#: ../Gtk/gui.py:4438 +msgid "Type must be boolean, integer, string or color" +msgstr "El tipo debe ser booleano, entero, cadena de texto o color" + +#: ../Gtk/gui.py:4441 +msgid "Value must be in the option dict" +msgstr "El valor debe estar en el diccionario de opciones" + +#: ../Gtk/gui.py:4443 +msgid "Values must be a dict" +msgstr "El valor debe ser un dicionario" + +#: ../Gtk/gui.py:4501 +msgid "No file selected" +msgstr "Ningún fichero seleccionado" + +#: ../Gtk/gui.py:4503 +msgid "The filename must have durus extension" +msgstr "El nombre del archivo debe tener extensión «durus»" + +#: ../Gtk/importFiebdc.py:89 +msgid "Open File" +msgstr "Abrir archivo" + +#: ../Gtk/importFiebdc.py:139 +msgid "The file must have 'bc3' extension" +msgstr "El archivo debe tener extensión «bc3»" + +#: ../Gtk/importFiebdc.py:212 +msgid "Loading file ..." +msgstr "Cargando archivo ..." + +#: ../Gtk/importFiebdc.py:236 +msgid "Cancel" +msgstr "Cancelar" diff -r a1703c4f2990 -r 2ac1551ad2ab mo/pyArq-Presupuestos.pot --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/mo/pyArq-Presupuestos.pot Sun Oct 31 20:07:33 2010 +0100 @@ -0,0 +1,1067 @@ +# SOME DESCRIPTIVE TITLE. +# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER +# This file is distributed under the same license as the PACKAGE package. +# FIRST AUTHOR , YEAR. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2010-10-31 20:01+0100\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=CHARSET\n" +"Content-Transfer-Encoding: 8bit\n" + +#: ../Generic/base.py:358 +msgid "Invalid code: $1" +msgstr "" + +#: ../Generic/base.py:373 +msgid "Synonyms ($1) must be a list, code: $2" +msgstr "" + +#: ../Generic/base.py:377 +msgid "Invalid Code in synomyms list ($1) code: $2" +msgstr "" + +#: ../Generic/base.py:408 +msgid "Unit ($1) must be a string: $2" +msgstr "" + +#: ../Generic/base.py:422 +msgid "Summary ($1) must be a string: $1" +msgstr "" + +#: ../Generic/base.py:439 +msgid "Prices ($1) must be a list: $2" +msgstr "" + +#: ../Generic/base.py:460 +msgid "Price ($1) must be a list with two items: $2" +msgstr "" + +#: ../Generic/base.py:465 +msgid "Price must be a float number: $1" +msgstr "" + +#: ../Generic/base.py:475 +#, python-format +msgid "The record do not have this Price. Code: %s" +msgstr "" + +#: ../Generic/base.py:481 +msgid "The record do not have this Price" +msgstr "" + +#: ../Generic/base.py:496 +msgid "Parents ($1) must be a list: $2" +msgstr "" + +#: ../Generic/base.py:500 ../Generic/base.py:512 +msgid "Invalid parent code ($1) in the record: $2" +msgstr "" + +#: ../Generic/base.py:528 +msgid "children ($1) must be a list, record: $2" +msgstr "" + +#: ../Generic/base.py:532 +msgid "child ($1) must be a Decomposition object, record: $2" +msgstr "" + +#: ../Generic/base.py:580 +msgid "Text ($1) must be a string, record: $2" +msgstr "" + +#: ../Generic/base.py:593 +msgid "sheet must be a Sheet instance" +msgstr "" + +#: ../Generic/base.py:606 +msgid "files must be a list: $1" +msgstr "" + +#: ../Generic/base.py:617 ../Generic/base.py:632 +msgid "Incorrect path" +msgstr "" + +#: ../Generic/base.py:622 +msgid "file must be a list or a File object: $1" +msgstr "" + +#: ../Generic/base.py:651 +msgid "labels must be a list" +msgstr "" + +#: ../Generic/base.py:657 +msgid "label must be a string" +msgstr "" + +#: ../Generic/base.py:666 +msgid "Label must be a string" +msgstr "" + +#: ../Generic/base.py:894 +msgid "Position must be a integer" +msgstr "" + +#: ../Generic/base.py:904 +msgid "BudgetMeasures atribute must be a list" +msgstr "" + +#: ../Generic/base.py:907 +msgid "BudgetMeasures item must be a Measure " +msgstr "" + +#: ../Generic/base.py:914 +msgid "Certification atribute must be a list or None" +msgstr "" + +#: ../Generic/base.py:920 +msgid "Real cost atribute must be a list or None" +msgstr "" + +#: ../Generic/base.py:926 +msgid "Cost goals atribute must be a list or None" +msgstr "" + +#: ../Generic/base.py:932 +msgid "Cost Planned atribute must be a list or None" +msgstr "" + +#: ../Generic/base.py:1023 +msgid "Measure must be a float number. Type: $1" +msgstr "" + +#: ../Generic/base.py:1034 +msgid "Lines must be a list" +msgstr "" + +#: ../Generic/base.py:1037 +msgid "Line must be a MeasureLine objetc" +msgstr "" + +#: ../Generic/base.py:1045 +msgid "Factor must be a float number |$1|" +msgstr "" + +#: ../Generic/base.py:1057 +msgid "Yield must be a float number" +msgstr "" + +#: ../Generic/base.py:1068 +msgid "Fixed must be boolean object" +msgstr "" + +#: ../Generic/base.py:1137 +msgid "Type must be M or A. Type: $1" +msgstr "" + +#: ../Generic/base.py:1269 +msgid " Parcial Subtotal must be a float number. Parcial: $1" +msgstr "" + +#: ../Generic/base.py:1276 +msgid " Acumulated Subtotal must be a float number. Parcial: $1" +msgstr "" + +#: ../Generic/base.py:1311 +msgid "Invalid measure line type ($1)" +msgstr "" + +#: ../Generic/base.py:1316 +msgid "Measure Comment must be a string ($1)" +msgstr "" + +#: ../Generic/base.py:1322 +msgid "Invalid Measure Units ($1)" +msgstr "" + +#: ../Generic/base.py:1334 +msgid "Invalid Measure length ($1)" +msgstr "" + +#: ../Generic/base.py:1346 +msgid "Invalid Measure Width ($1)" +msgstr "" + +#: ../Generic/base.py:1358 +msgid "Invalid Measure Height ($1)" +msgstr "" + +#: ../Generic/base.py:1369 +msgid "Formula must be a string ($1)" +msgstr "" + +#: ../Generic/base.py:1372 +msgid "There is invalid charactersin formula ($1)" +msgstr "" + +#: ../Generic/base.py:1437 ../Generic/base.py:2406 +msgid "'a' value must be a float number" +msgstr "" + +#: ../Generic/base.py:1441 ../Generic/base.py:2410 +msgid "'b' value must be a float number" +msgstr "" + +#: ../Generic/base.py:1445 ../Generic/base.py:2414 +msgid "'c' value must be a float number" +msgstr "" + +#: ../Generic/base.py:1449 ../Generic/base.py:2418 +msgid "'d' value must be a float number" +msgstr "" + +#: ../Generic/base.py:1477 ../Generic/base.py:2446 +msgid "Invalid formula" +msgstr "" + +#: ../Generic/base.py:1727 +msgid "sheet_dict must be a dictionay" +msgstr "" + +#: ../Generic/base.py:1744 ../Generic/base.py:1750 +msgid "sheet field must be a string" +msgstr "" + +#: ../Generic/base.py:1746 +msgid "section_dict must be a dictionary" +msgstr "" + +#: ../Generic/base.py:1752 +msgid "sheet section must be a string" +msgstr "" + +#: ../Generic/base.py:1754 +msgid "sheet paragraph must be a string" +msgstr "" + +#: ../Generic/base.py:1992 +msgid "Owner must be a string" +msgstr "" + +#: ../Generic/base.py:2008 +#, python-format +msgid "Invalid Date: %s" +msgstr "" + +#: ../Generic/base.py:2019 +msgid "Comment must be a string" +msgstr "" + +#: ../Generic/base.py:2035 +msgid "Budget type must be 1, 2, 3 or 4." +msgstr "" + +#: ../Generic/base.py:2047 +msgid "Certificate order must be a integer." +msgstr "" + +#: ../Generic/base.py:2062 +msgid "Budget certificate Date must be a valid Date." +msgstr "" + +#: ../Generic/base.py:2076 +msgid "Invalid title list format" +msgstr "" + +#: ../Generic/base.py:2105 +msgid "Invalid Index Title" +msgstr "" + +#: ../Generic/base.py:2140 +msgid "Decimal Key error" +msgstr "" + +#: ../Generic/base.py:2176 +msgid "Invalid Percentage key" +msgstr "" + +#: ../Generic/base.py:2291 +msgid "Invalid parent code: $1" +msgstr "" + +#: ../Generic/base.py:2294 +msgid "Invalid child code: $1" +msgstr "" + +#: ../Generic/base.py:2297 +msgid "Invalid position in measure $1, in code $2" +msgstr "" + +#: ../Generic/base.py:2305 +msgid "" +"Circular Decomposition, parent code: $1, child code: $2, repeated code: $3" +msgstr "" + +#: ../Generic/base.py:2360 ../Generic/base.py:2372 +msgid "" +"Error: Invalid child position in decomposition. Parent code: $1 Child code: " +"$2 Position: $3" +msgstr "" + +#: ../Generic/base.py:2366 +msgid "Error: Empty child code. Parent code: $1 Position: $2" +msgstr "" + +#: ../Generic/base.py:2457 +msgid "Invalid code" +msgstr "" + +#: ../Generic/base.py:2467 +msgid "Invalid record: $1" +msgstr "" + +#: ../Generic/base.py:2530 +msgid "Only can be one root record" +msgstr "" + +#: ../Generic/base.py:2624 ../Generic/base.py:2630 +msgid "This record does not exits" +msgstr "" + +#: ../Generic/base.py:2627 +msgid "Path item must be a integer" +msgstr "" + +#: ../Generic/base.py:2632 +msgid "Path must be a not empty tuple: $1" +msgstr "" + +#: ../Generic/base.py:2683 +msgid "The sheet code must be a string" +msgstr "" + +#: ../Generic/base.py:2685 +msgid "The sheet title must be a string" +msgstr "" + +#: ../Generic/base.py:2693 +msgid "The sheet sections must be a dictionary" +msgstr "" + +#: ../Generic/base.py:2698 +msgid "The field code must be a string" +msgstr "" + +#: ../Generic/base.py:2700 +msgid "The field title must be a string" +msgstr "" + +#: ../Generic/base.py:2708 +msgid "The sheet field must be a dictionary" +msgstr "" + +#: ../Generic/base.py:2713 +msgid "The paragraph code must be a string" +msgstr "" + +#: ../Generic/base.py:2715 +msgid "The paragraph text must be a string" +msgstr "" + +#: ../Generic/base.py:2723 +msgid "The paragraph dict must be a dictionary" +msgstr "" + +#: ../Generic/base.py:2728 ../Generic/base.py:2752 ../Generic/base.py:2882 +#: ../Generic/base.py:2906 ../Generic/base.py:2928 +msgid "The record_code code must be a string" +msgstr "" + +#: ../Generic/base.py:2730 +msgid "The field must be a string" +msgstr "" + +#: ../Generic/base.py:2732 +msgid "The section dict must be a dictionary" +msgstr "" + +#: ../Generic/base.py:2736 +msgid "" +"Error: The budget do not have this record code and can not be added the " +"sheet text in the field $1. Record Code: $2" +msgstr "" + +#: ../Generic/base.py:2754 +msgid "The filename must be a string" +msgstr "" + +#: ../Generic/base.py:2758 +msgid "" +"Error: The budget do not have the record code $1 and can not be added the " +"file: $2" +msgstr "" + +#: ../Generic/base.py:2768 +msgid "The company code must be a string" +msgstr "" + +#: ../Generic/base.py:2770 +msgid "The summary must be a string" +msgstr "" + +#: ../Generic/base.py:2772 ../Generic/base.py:2800 +msgid "The name must be a string" +msgstr "" + +#: ../Generic/base.py:2774 +msgid "The name must be a list" +msgstr "" + +#: ../Generic/base.py:2778 +msgid "The office must be a list" +msgstr "" + +#: ../Generic/base.py:2780 +msgid "The office must be a 10 items list" +msgstr "" + +#: ../Generic/base.py:2783 +msgid "This office item must be a string" +msgstr "" + +#: ../Generic/base.py:2787 +msgid "This office item must be a list" +msgstr "" + +#: ../Generic/base.py:2802 +msgid "The web must be a string" +msgstr "" + +#: ../Generic/base.py:2804 +msgid "The email must be a string" +msgstr "" + +#: ../Generic/base.py:2814 +msgid "The tecnical info code must be a string" +msgstr "" + +#: ../Generic/base.py:2816 +msgid "The tecnical info description must be a string" +msgstr "" + +#: ../Generic/base.py:2819 +msgid "The tecnical info unit must be a string" +msgstr "" + +#: ../Generic/base.py:2866 +msgid "The label must be a string" +msgstr "" + +#: ../Generic/base.py:2884 +msgid "The parametric select comment must be a string" +msgstr "" + +#: ../Generic/base.py:2887 +msgid "" +"Error: The budget do not have the record code $1 and can not be added the " +"Parametric select comment: $2" +msgstr "" + +#: ../Generic/base.py:2894 +msgid "" +"Error: The Record $1 is not a Parametric Record and can not have Parametric " +"comment" +msgstr "" + +#: ../Generic/base.py:2908 +msgid "The summary record must be a string" +msgstr "" + +#: ../Generic/base.py:2910 +msgid "" +"Error: The budget do not have the record code $1 and can not be seted the " +"summary: $2" +msgstr "" + +#: ../Generic/base.py:2916 +msgid "" +"Error: The Record $1 is not a Parametric Record and can not have Parametric " +"summary" +msgstr "" + +#: ../Generic/base.py:2930 +msgid "The text record must be a string" +msgstr "" + +#: ../Generic/base.py:2932 +msgid "" +"Error: The budget do not have the record code $1 and can not be seted the " +"text: $2" +msgstr "" + +#: ../Generic/base.py:2938 +msgid "" +"Error: The Record $1 is not a Parametric Record and can not have Parametric " +"text" +msgstr "" + +#: ../Generic/base.py:3396 +msgid "Invalid Hierarchy ($1) The hierarchy must be -1, 0, 1 or 2" +msgstr "" + +#: ../Generic/base.py:3403 +msgid "Invalid type ($1),the type must be (empty string,0,1,2,3)" +msgstr "" + +#: ../Generic/base.py:3412 +msgid "" +"Invalid subtype ($1), The subtype must one in (empty string, EA, EU, EC, EF, " +"OB, PA, PU, H, Q, %, MC, MCr, MM, MS, ME, MCu, MAl, ML, M)" +msgstr "" + +#: ../Generic/fiebdc.py:151 +msgid "Invalid code, it must be a string" +msgstr "" + +#: ../Generic/fiebdc.py:157 +msgid "The code '$1' have invalid characters." +msgstr "" + +#: ../Generic/fiebdc.py:170 +msgid "The code '$1' contains special characters repeated." +msgstr "" + +#: ../Generic/fiebdc.py:391 +msgid "" +"The 'V' record (Property and Version) must be the first record in the file " +"but it is the number: $1" +msgstr "" + +#: ../Generic/fiebdc.py:394 +msgid "The default values were taken and this V record is ignored" +msgstr "" + +#: ../Generic/fiebdc.py:428 +#, python-format +msgid "FIEBDC format: %s" +msgstr "" + +#: ../Generic/fiebdc.py:437 +#, python-format +msgid "FIEBDC file generated by %s" +msgstr "" + +#: ../Generic/fiebdc.py:776 +msgid "Record C without a valid code" +msgstr "" + +#: ../Generic/fiebdc.py:852 ../Generic/fiebdc.py:864 ../Generic/fiebdc.py:892 +msgid "Incorrect type ($1) in the code $2" +msgstr "" + +#: ../Generic/fiebdc.py:943 +msgid "" +"ValueError loadig the descomposition of the record $1, the factor of the " +"child $2 must be a float number and can not be $3, seted default value 1.0" +msgstr "" + +#: ../Generic/fiebdc.py:954 +msgid "" +"ValueError loading the descomposition of the record $1, the yield of the " +"child $2, must be a float number and cannot be $3, seted default value 1.0" +msgstr "" + +#: ../Generic/fiebdc.py:1041 +msgid "Invalid codes in $1 record, codes $2" +msgstr "" + +#: ../Generic/fiebdc.py:1045 +msgid "Empty child code in $1 record, codes: $2" +msgstr "" + +#: ../Generic/fiebdc.py:1058 +msgid "Invalid path in $1 record, codes $2" +msgstr "" + +#: ../Generic/fiebdc.py:1069 +msgid "Invalid Total Measure value in $1 record, codes $2" +msgstr "" + +#: ../Generic/fiebdc.py:1089 +msgid "" +"The comment is not a formula or its have invalid characters, in the $1 " +"record, codes $2" +msgstr "" + +#: ../Generic/fiebdc.py:1403 ../Generic/fiebdc.py:1657 +msgid "The file $1 do not exist" +msgstr "" + +#: ../Generic/fiebdc.py:1763 ../Generic/fiebdc.py:1770 +msgid "PyArq hates parametric DLLs" +msgstr "" + +#: ../Generic/fiebdc.py:1980 +msgid "Loading file $1" +msgstr "" + +#: ../Generic/fiebdc.py:2000 +msgid "This codepage do not exist in FIEBDC3! Default codepage: $1" +msgstr "" + +#: ../Generic/fiebdc.py:2004 +msgid "This V record dot have a codepage! Default codepage: $1" +msgstr "" + +#: ../Generic/fiebdc.py:2008 +msgid "Not 'V' record in File! Default codepage: $1" +msgstr "" + +#: ../Generic/fiebdc.py:2049 +msgid "Cancelled process" +msgstr "" + +#: ../Generic/fiebdc.py:2052 +msgid "Time to load: $1 seconds" +msgstr "" + +#: ../Generic/fiebdc.py:2054 +msgid "Records/Valid Records: $1/$2" +msgstr "" + +#: ../Generic/fiebdc.py:2057 +msgid "$1 unsuported record type O: Comercial Relationship" +msgstr "" + +#: ../Generic/fiebdc.py:2060 +msgid "This file is not a valid FIBDC3 file" +msgstr "" + +#: ../Generic/fiebdc.py:2097 +msgid "Testing budget ..." +msgstr "" + +#: ../Generic/fiebdc.py:2115 +msgid "End Test" +msgstr "" + +#: ../Gtk/gui.py:69 +msgid "The icon file does not exist. '$1'" +msgstr "" + +#: ../Gtk/gui.py:75 +msgid "pyArq-Presupuestos running on $1" +msgstr "" + +#: ../Gtk/gui.py:164 +msgid "_File" +msgstr "" + +#: ../Gtk/gui.py:165 +msgid "_Import Fiebdc" +msgstr "" + +#: ../Gtk/gui.py:167 +msgid "_Close" +msgstr "" + +#: ../Gtk/gui.py:169 +msgid "_View" +msgstr "" + +#: ../Gtk/gui.py:170 +msgid "_Text" +msgstr "" + +#: ../Gtk/gui.py:172 +msgid "_Test" +msgstr "" + +#: ../Gtk/gui.py:174 +msgid "Import Fiebdc _price database" +msgstr "" + +#: ../Gtk/gui.py:176 +msgid "_Open price database" +msgstr "" + +#: ../Gtk/gui.py:248 +msgid "Saving file: $1" +msgstr "" + +#: ../Gtk/gui.py:253 +msgid "Saving time: $1 seconds" +msgstr "" + +#: ../Gtk/gui.py:321 +msgid "Loading file: $1:" +msgstr "" + +#: ../Gtk/gui.py:468 ../Gtk/importFiebdc.py:224 +msgid "Time: 0s" +msgstr "" + +#: ../Gtk/gui.py:532 ../Gtk/importFiebdc.py:303 +msgid "Time: $1" +msgstr "" + +#: ../Gtk/gui.py:809 +msgid "The value must be a list" +msgstr "" + +#: ../Gtk/gui.py:812 +msgid "Incorrect len" +msgstr "" + +#: ../Gtk/gui.py:840 +msgid "Incorrect item $1" +msgstr "" + +#: ../Gtk/gui.py:854 +msgid "The budget does not have the path record: $1" +msgstr "" + +#: ../Gtk/gui.py:989 +msgid "Decomposition" +msgstr "" + +#: ../Gtk/gui.py:990 ../Gtk/gui.py:2006 +msgid "Description" +msgstr "" + +#: ../Gtk/gui.py:991 ../Gtk/gui.py:2008 +msgid "Measure" +msgstr "" + +#: ../Gtk/gui.py:992 +msgid "Sheet of Conditions" +msgstr "" + +#: ../Gtk/gui.py:993 +msgid "Files" +msgstr "" + +#: ../Gtk/gui.py:994 +msgid "Companies" +msgstr "" + +#: ../Gtk/gui.py:1355 +msgid "The item must be a widget object." +msgstr "" + +#: ../Gtk/gui.py:1361 +msgid "Invalid orientation." +msgstr "" + +#: ../Gtk/gui.py:1736 ../Gtk/gui.py:2370 ../Gtk/gui.py:3780 +msgid "Argument must be a Budget object" +msgstr "" + +#: ../Gtk/gui.py:1745 +msgid "DecompositionList.__init__: Record path can not be None" +msgstr "" + +#: ../Gtk/gui.py:1776 +msgid "a" +msgstr "" + +#: ../Gtk/gui.py:2003 ../Gtk/gui.py:3840 +msgid "Code" +msgstr "" + +#: ../Gtk/gui.py:2004 +msgid "Unit" +msgstr "" + +#: ../Gtk/gui.py:2010 +msgid "Price" +msgstr "" + +#: ../Gtk/gui.py:2012 +msgid "Amount" +msgstr "" + +#: ../Gtk/gui.py:2024 ../Gtk/gui.py:2502 +msgid "Invalid path" +msgstr "" + +#: ../Gtk/gui.py:2375 +msgid "Record path must be a tuple" +msgstr "" + +#: ../Gtk/gui.py:2519 +msgid "measure must be a Measure object. Type: $1" +msgstr "" + +#: ../Gtk/gui.py:2530 ../Gtk/gui.py:3855 ../Gtk/gui.py:4182 +msgid "Type" +msgstr "" + +#: ../Gtk/gui.py:2531 +msgid "Comment" +msgstr "" + +#: ../Gtk/gui.py:2532 +msgid "" +"N\n" +"(a)" +msgstr "" + +#: ../Gtk/gui.py:2533 +msgid "" +"Length\n" +"(b)" +msgstr "" + +#: ../Gtk/gui.py:2534 +msgid "" +"Width\n" +"(c)" +msgstr "" + +#: ../Gtk/gui.py:2535 +msgid "" +"Height\n" +"(d)" +msgstr "" + +#: ../Gtk/gui.py:2536 +msgid "Formula" +msgstr "" + +#: ../Gtk/gui.py:2537 +#, python-format +msgid "" +"Parcial\n" +"[%s]" +msgstr "" + +#: ../Gtk/gui.py:2538 +msgid "Subtotal" +msgstr "" + +#: ../Gtk/gui.py:2946 ../Gtk/gui.py:2968 +msgid "Description text of the record $1" +msgstr "" + +#: ../Gtk/gui.py:3125 +msgid "Sheet of Conditions of the record $1" +msgstr "" + +#: ../Gtk/gui.py:3145 +msgid "Field" +msgstr "" + +#: ../Gtk/gui.py:3167 +msgid "Section" +msgstr "" + +#: ../Gtk/gui.py:3320 +msgid "Sheet2 of Conditions of the record $1" +msgstr "" + +#: ../Gtk/gui.py:3696 +msgid "$1 text" +msgstr "" + +#: ../Gtk/gui.py:3841 +msgid "Code that define the company" +msgstr "" + +#: ../Gtk/gui.py:3842 +msgid "Summary" +msgstr "" + +#: ../Gtk/gui.py:3843 +msgid "Summary of the company name" +msgstr "" + +#: ../Gtk/gui.py:3844 ../Gtk/gui.py:3860 +msgid "Name" +msgstr "" + +#: ../Gtk/gui.py:3845 +msgid "Complete name" +msgstr "" + +#: ../Gtk/gui.py:3846 +msgid "CIF" +msgstr "" + +#: ../Gtk/gui.py:3847 +msgid "Fiscal identifier number" +msgstr "" + +#: ../Gtk/gui.py:3848 +msgid "Web" +msgstr "" + +#: ../Gtk/gui.py:3849 +msgid "Company web page" +msgstr "" + +#: ../Gtk/gui.py:3850 +msgid "Email" +msgstr "" + +#: ../Gtk/gui.py:3851 +msgid "Company email" +msgstr "" + +#: ../Gtk/gui.py:3856 +msgid "" +"Type of Office:\n" +" C: Central office\n" +" D: Local office\n" +" R: Performer" +msgstr "" + +#: ../Gtk/gui.py:3861 +msgid "Office name" +msgstr "" + +#: ../Gtk/gui.py:3862 +msgid "Address" +msgstr "" + +#: ../Gtk/gui.py:3863 +msgid "Postal code" +msgstr "" + +#: ../Gtk/gui.py:3864 +msgid "Town" +msgstr "" + +#: ../Gtk/gui.py:3865 +msgid "Province" +msgstr "" + +#: ../Gtk/gui.py:3866 +msgid "Country" +msgstr "" + +#: ../Gtk/gui.py:3867 +msgid "Phone" +msgstr "" + +#: ../Gtk/gui.py:3868 +msgid "Phone numbers of the office" +msgstr "" + +#: ../Gtk/gui.py:3869 +msgid "Fax" +msgstr "" + +#: ../Gtk/gui.py:3870 +msgid "Fax numbers of the office" +msgstr "" + +#: ../Gtk/gui.py:3871 +msgid "Contact person" +msgstr "" + +#: ../Gtk/gui.py:3872 +msgid "Contact persons in the office" +msgstr "" + +#: ../Gtk/gui.py:3876 +msgid "Unknow Option Type" +msgstr "" + +#: ../Gtk/gui.py:4120 +msgid "Boolean" +msgstr "" + +#: ../Gtk/gui.py:4121 +msgid "Integer" +msgstr "" + +#: ../Gtk/gui.py:4122 +msgid "Text" +msgstr "" + +#: ../Gtk/gui.py:4123 +msgid "Color" +msgstr "" + +#: ../Gtk/gui.py:4124 +msgid "List" +msgstr "" + +#: ../Gtk/gui.py:4156 +msgid "Option name" +msgstr "" + +#: ../Gtk/gui.py:4169 +msgid "Value" +msgstr "" + +#: ../Gtk/gui.py:4207 +msgid "Description:" +msgstr "" + +#: ../Gtk/gui.py:4369 +msgid "Option values must be strings" +msgstr "" + +#: ../Gtk/gui.py:4371 +msgid "Option must be a tuple with 4 items" +msgstr "" + +#: ../Gtk/gui.py:4373 +msgid "Option list must be a list" +msgstr "" + +#: ../Gtk/gui.py:4393 +msgid "Icorrect type, must be boolean" +msgstr "" + +#: ../Gtk/gui.py:4398 +msgid "Icorrect type, must be integer" +msgstr "" + +#: ../Gtk/gui.py:4411 +msgid "Icorrect type, must be string" +msgstr "" + +#: ../Gtk/gui.py:4424 +msgid "Icorrect type, must be list" +msgstr "" + +#: ../Gtk/gui.py:4430 +msgid "Icorrect type, must be a parseable color" +msgstr "" + +#: ../Gtk/gui.py:4438 +msgid "Type must be boolean, integer, string or color" +msgstr "" + +#: ../Gtk/gui.py:4441 +msgid "Value must be in the option dict" +msgstr "" + +#: ../Gtk/gui.py:4443 +msgid "Values must be a dict" +msgstr "" + +#: ../Gtk/gui.py:4501 +msgid "No file selected" +msgstr "" + +#: ../Gtk/gui.py:4503 +msgid "The filename must have durus extension" +msgstr "" + +#: ../Gtk/importFiebdc.py:89 +msgid "Open File" +msgstr "" + +#: ../Gtk/importFiebdc.py:139 +msgid "The file must have 'bc3' extension" +msgstr "" + +#: ../Gtk/importFiebdc.py:212 +msgid "Loading file ..." +msgstr "" + +#: ../Gtk/importFiebdc.py:236 +msgid "Cancel" +msgstr "" diff -r a1703c4f2990 -r 2ac1551ad2ab pyArq-Presupuestos --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/pyArq-Presupuestos Sun Oct 31 20:07:33 2010 +0100 @@ -0,0 +1,50 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- +## File presupuestos.py +## This file is part of pyArq-Presupuestos. +## +## Copyright (C) 2010 Miguel Ángel Bárcena Rodríguez +## +## +## pyArq-Presupuestos is free software: you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation, either version 3 of the License, or +## (at your option) any later version. +## +## pyArq-Presupuestos is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. +## +## You should have received a copy of the GNU General Public License +## along with this program. If not, see . + +def _translate(): + """def translate() + + Translates the program using gettext module + """ + _app = "pyArq-Presupuestos" + _dir = globals.path["APPDATA"] + "/mo/" + print _dir + gettext.install(_app, _dir, unicode=1) + +def _run_gui(): + """def _run_gui + + Shows main window and starts the GTK+ event processing loop. + """ + _window = gui.MainWindow() + +# Run pyArq-Presupuestos +if __name__ == "__main__": + # Modules + import gettext + import sys + from Generic import globals + # take path to find mo file + _path = sys.path[0] + globals.path["APPDATA"]= _path + _translate() + from Gtk import gui + _run_gui()