From 01316a47c21565cd82b6c77856e8491b73d07ea5 Mon Sep 17 00:00:00 2001 From: Viggor Date: Wed, 18 Feb 2015 14:57:31 +0100 Subject: [PATCH 01/19] Initial commit --- .gitignore | 54 +++++ LICENSE | 662 +++++++++++++++++++++++++++++++++++++++++++++++++++++ README.md | 2 + 3 files changed, 718 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..db4561ea --- /dev/null +++ b/.gitignore @@ -0,0 +1,54 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] + +# C extensions +*.so + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +*.egg-info/ +.installed.cfg +*.egg + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.coverage +.cache +nosetests.xml +coverage.xml + +# Translations +*.mo +*.pot + +# Django stuff: +*.log + +# Sphinx documentation +docs/_build/ + +# PyBuilder +target/ diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..cebe0354 --- /dev/null +++ b/LICENSE @@ -0,0 +1,662 @@ +GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 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 Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +our General Public Licenses are 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. + + 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. + + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. + + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. + + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. + + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. + + 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 Affero 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. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. + + 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 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 work with which it is combined will remain governed by version +3 of the GNU General Public License. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU Affero 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 Affero 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 Affero 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 Affero 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 Affero 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 Affero General Public License for more details. + + You should have received a copy of the GNU Affero General Public License + along with this program. If not, see . + +Also add information on how to contact you by electronic and paper mail. + + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. + + 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 AGPL, see +. + diff --git a/README.md b/README.md new file mode 100644 index 00000000..183a6cff --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# file_exchange +WIP From 2fa7baf1b2380b03bd4dcf76f256faffe2723fde Mon Sep 17 00:00:00 2001 From: Valentin Chemiere Date: Wed, 18 Feb 2015 15:00:42 +0100 Subject: [PATCH 02/19] First commit Work in Progress --- ir_attachment_metadata/.__openerp__.py.swp | Bin 0 -> 12288 bytes ir_attachment_metadata/__init__.py | 24 +++++++ ir_attachment_metadata/__openerp__.py | 44 +++++++++++++ ir_attachment_metadata/attachment.py | 42 +++++++++++++ ir_attachment_metadata/attachment_view.xml | 18 ++++++ ir_exchange_file/.__openerp__.py.swp | Bin 0 -> 12288 bytes ir_exchange_file/.attachment.py.swp | Bin 0 -> 12288 bytes ir_exchange_file/.backend.py.swp | Bin 0 -> 12288 bytes ir_exchange_file/__init__.py | 26 ++++++++ ir_exchange_file/__openerp__.py | 46 ++++++++++++++ ir_exchange_file/attachment.py | 37 +++++++++++ ir_exchange_file/attachment_view.xml | 18 ++++++ ir_exchange_file/backend.py | 34 ++++++++++ ir_exchange_file/backends/.filestore.py.swp | Bin 0 -> 12288 bytes ir_exchange_file/backends/filestore.py | 66 ++++++++++++++++++++ ir_exchange_file/backends/ftp.py | 58 +++++++++++++++++ ir_exchange_file/backends/sftp.py | 59 +++++++++++++++++ ir_exchange_file/location.py | 39 ++++++++++++ ir_exchange_file/location_view.xml | 52 +++++++++++++++ ir_exchange_file/task.py | 48 ++++++++++++++ ir_exchange_file/task_view.xml | 47 ++++++++++++++ 21 files changed, 658 insertions(+) create mode 100644 ir_attachment_metadata/.__openerp__.py.swp create mode 100644 ir_attachment_metadata/__init__.py create mode 100644 ir_attachment_metadata/__openerp__.py create mode 100644 ir_attachment_metadata/attachment.py create mode 100644 ir_attachment_metadata/attachment_view.xml create mode 100644 ir_exchange_file/.__openerp__.py.swp create mode 100644 ir_exchange_file/.attachment.py.swp create mode 100644 ir_exchange_file/.backend.py.swp create mode 100644 ir_exchange_file/__init__.py create mode 100644 ir_exchange_file/__openerp__.py create mode 100644 ir_exchange_file/attachment.py create mode 100644 ir_exchange_file/attachment_view.xml create mode 100755 ir_exchange_file/backend.py create mode 100644 ir_exchange_file/backends/.filestore.py.swp create mode 100755 ir_exchange_file/backends/filestore.py create mode 100755 ir_exchange_file/backends/ftp.py create mode 100755 ir_exchange_file/backends/sftp.py create mode 100644 ir_exchange_file/location.py create mode 100644 ir_exchange_file/location_view.xml create mode 100644 ir_exchange_file/task.py create mode 100644 ir_exchange_file/task_view.xml diff --git a/ir_attachment_metadata/.__openerp__.py.swp b/ir_attachment_metadata/.__openerp__.py.swp new file mode 100644 index 0000000000000000000000000000000000000000..8d9e9bd5ea09629b38a3a52be894b2fc59ae923d GIT binary patch literal 12288 zcmeI2TW=dh6vrn#AZRI|_6r=62Pep0=TZs^Rn={9QcJlQUx+M(tg&}&k67=lW@hbJ zT>1%kMZELG6CZ#}h58u~5}yDGA;dcZ{yW|f3!#*UzEI6b|7?8D%-M5(bH-AX>2JPM zKcd%zn+)US{KsN^|14uuIa2!EaLLontDL3gk~z8SV>hv?BBiQR5)>OH*d@!Df z#P;MQ)oLoLy`D-%B6Qm81?l`TfxX)k@B}U=klA77jcaUYduubFxVHKV-MHSle2w>d z0-k^;;0bsFo`5Id33vjYz*9xQrYr0LDte}3Gj%qpPy&!t1Czz|NlS#`~Rnx82b$R6nX%?1zmxjgC4%f*oV+- z&hEz=?|m|??P1uoUjF|~A%-*1%zI#N*<3*#c85Ro7%+~Gmv zu(IQ{w~HsBk&+RA{q+QrZkQgDn_8C?$O20!iIl zDs>_(cNLa#HSoy}w#L?goX08|(M;Mg*|9W)YrZ4RCjn7?NQts#GD6Vpv9;-LwVIbX zO0q!dQMF)Vs&@hxsjD(FW0l1bjrmlN76T!tA|g%$mCi{G@diN$#~liXL!lKp(mbX{ z)<-ZpTtF@od+aQOH9F14NvnopTPPCQxkCDxrPF%*{z<1z;c=Hv!)6l!yKg#73SKc4i7N)z zqGO2?XIyKZ*t|#=qP>KYKsA!aYKg#@D;G=0xJZo{+>*2i9LLlblo>J1;&OqY08-9t zSjWV<$}}n0xf{fjImKvOO+`N~%1?8aW{mgr=Pt#4jnZ0bXpy6!eU&9>D70Uh1UCI* zIqeiX}w pmsf5SjpWY1>#`GkkJOrtJewywkYnZ53eHC)lhH1@%fPvX{RLiC&r1LR literal 0 HcmV?d00001 diff --git a/ir_attachment_metadata/__init__.py b/ir_attachment_metadata/__init__.py new file mode 100644 index 00000000..c5dc82e3 --- /dev/null +++ b/ir_attachment_metadata/__init__.py @@ -0,0 +1,24 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Module for OpenERP +# Copyright (C) 2014 Akretion (http://www.akretion.com). +# @author Sébastien BEAU +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################### + +import attachment + diff --git a/ir_attachment_metadata/__openerp__.py b/ir_attachment_metadata/__openerp__.py new file mode 100644 index 00000000..353b96f2 --- /dev/null +++ b/ir_attachment_metadata/__openerp__.py @@ -0,0 +1,44 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Module for OpenERP +# Copyright (C) 2014 Akretion (http://www.akretion.com). +# @author Sébastien BEAU +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################### + +{'name': 'ir_attachment_metadata', + 'version': '0.0.1', + 'author': 'Akretion', + 'website': 'www.akretion.com', + 'license': 'AGPL-3', + 'category': 'Generic Modules', + 'description': """ + + """, + 'depends': [ + 'base', + ], + 'data': [ + 'attachment_view.xml', + ], + 'installable': True, + 'application': True, +} + + + + diff --git a/ir_attachment_metadata/attachment.py b/ir_attachment_metadata/attachment.py new file mode 100644 index 00000000..4e4fccd2 --- /dev/null +++ b/ir_attachment_metadata/attachment.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Module for OpenERP +# Copyright (C) 2014 Akretion (http://www.akretion.com). +# @author Sébastien BEAU +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################### + +from openerp import models, fields, api, _ +from openerp.exceptions import Warning +import hashlib + + +class AttachmentMetadata(models.Model): + _inherit = 'ir.attachment' + + internal_hash = fields.Char(store=True, compute='_compute_hash') + external_hash = fields.Char() + + + @api.depends('datas') + def _compute_hash(self): + if self.datas: + print hashlib.md5(self.datas).hexdigest() + self.internal_hash = hashlib.md5(self.datas).hexdigest() + if self.external_hash and self.internal_hash != self.external_hash: + raise Warning(_('File corrupted'), _("Something was wrong with the retreived file, please relaunch the task.")) + diff --git a/ir_attachment_metadata/attachment_view.xml b/ir_attachment_metadata/attachment_view.xml new file mode 100644 index 00000000..04b10055 --- /dev/null +++ b/ir_attachment_metadata/attachment_view.xml @@ -0,0 +1,18 @@ + + + + + + ir.attachment + + + + + + + + + + + + diff --git a/ir_exchange_file/.__openerp__.py.swp b/ir_exchange_file/.__openerp__.py.swp new file mode 100644 index 0000000000000000000000000000000000000000..8d9e9bd5ea09629b38a3a52be894b2fc59ae923d GIT binary patch literal 12288 zcmeI2TW=dh6vrn#AZRI|_6r=62Pep0=TZs^Rn={9QcJlQUx+M(tg&}&k67=lW@hbJ zT>1%kMZELG6CZ#}h58u~5}yDGA;dcZ{yW|f3!#*UzEI6b|7?8D%-M5(bH-AX>2JPM zKcd%zn+)US{KsN^|14uuIa2!EaLLontDL3gk~z8SV>hv?BBiQR5)>OH*d@!Df z#P;MQ)oLoLy`D-%B6Qm81?l`TfxX)k@B}U=klA77jcaUYduubFxVHKV-MHSle2w>d z0-k^;;0bsFo`5Id33vjYz*9xQrYr0LDte}3Gj%qpPy&!t1Czz|NlS#`~Rnx82b$R6nX%?1zmxjgC4%f*oV+- z&hEz=?|m|??P1uoUjF|~A%-*1%zI#N*<3*#c85Ro7%+~Gmv zu(IQ{w~HsBk&+RA{q+QrZkQgDn_8C?$O20!iIl zDs>_(cNLa#HSoy}w#L?goX08|(M;Mg*|9W)YrZ4RCjn7?NQts#GD6Vpv9;-LwVIbX zO0q!dQMF)Vs&@hxsjD(FW0l1bjrmlN76T!tA|g%$mCi{G@diN$#~liXL!lKp(mbX{ z)<-ZpTtF@od+aQOH9F14NvnopTPPCQxkCDxrPF%*{z<1z;c=Hv!)6l!yKg#73SKc4i7N)z zqGO2?XIyKZ*t|#=qP>KYKsA!aYKg#@D;G=0xJZo{+>*2i9LLlblo>J1;&OqY08-9t zSjWV<$}}n0xf{fjImKvOO+`N~%1?8aW{mgr=Pt#4jnZ0bXpy6!eU&9>D70Uh1UCI* zIqeiX}w pmsf5SjpWY1>#`GkkJOrtJewywkYnZ53eHC)lhH1@%fPvX{RLiC&r1LR literal 0 HcmV?d00001 diff --git a/ir_exchange_file/.attachment.py.swp b/ir_exchange_file/.attachment.py.swp new file mode 100644 index 0000000000000000000000000000000000000000..dc65a5c0260fc54d033d6e50ac8148dff6dbdf8c GIT binary patch literal 12288 zcmeI2O^+Kj7{^^Mw1pPb-Z(wo1tme*NnZgi-Kyz!w~f?&QIc)7AQU+nPsZwGJlLMi zMxge{6*wR`apqJwa6lYD;xlmN!T}C^1O)s&nd}QHT9A6Fu%-XbiyuGs^LuP3(Yk;B zZflKR4=yv57a5B>KX)Izw9c;Ken$?KcJGfUh26q#W{$9XP#88;s8nM|6_<=_YZ_Ab zxV3x`k3?#FBVl>SE&r$2`=&uQIigEn?Fo1SjzDgsg*Q&IOBXMkcaQUP=jiO2EqCjS zC*TQq0-k^;;0bsFo`5Id3H(n8*z6d4h*+N~<1Kz1;Qm+=p=L;`tCGi z??YFhW6+nI!r1cSF5pE*D@j_k|+?nR_K%`y_lPrcp6S$%k<#ub4!mg zUflELd=}0Qw^}0SGm-sZ6ke$o2JXm!bsdCd@YxM&)5l3o5SGPL7J5)RKh3d_j+i7c@9yOw8 zEVqJ|kYAC6Cg_Mn46IaX_3feyGf4+M#B#voCZ4Pui4ySPEblp5j=3%^{+_zBtHQ!q zr9~Nk33jP+2(e=b0@RafEVQ&3Yeni{{~T+D4H9k)HTMp|u9atCt0NVP!~|>Zf-Es6 zN14)l(||=5!dKx8g-L#0`{bPk!#>ZY#iCAT^u`B+Z)di`Vzn`iR~o+k&E zr8Jn$YN+`rAZkUFDqF`Q5p*@SHd|~o#^Z4?O!GkLVIz?Pks4td*Mh>+w#v!GDo;Wz zh8?$*2h#=4u`^^7Qc?`pGm3;(bj#u432o+mIHNmLhvmSIw4+FA8X?IPA}bQv zD2B1N(q6vZT<!Aaxf*6^0X)YfLIPlWN%YCX`^o zXxb6l;MFwz%&{~v`sq)KA+BnK)=`ng7zM4WJjJ4ed7T9I;{E1ysmxFj0LBDE)VjSI zos7)C&L45}SO$?Bh$#oM=z^gEPccpQ;W!l7%^O8xLm5p<5Z8HbW4!GA@I{{+D?$Ip zN^^^@8c{w6eZlkV{60Qb911M>YsDazG4r$>#+ItfHM)5I!ewffMpQj8sSmcSl@m#Y kIX>0gEZe|BUTJT-(LC9LF)qBifc4>a5!Yi=x0Q@=cS;n4S z_(r4y-e z;!okWNS;WQ){F6+`KXx;kb%;`z;$ZZm)Modmo|N*vbM_4pSwqT$N(8217v^sN1Uujr7=wr40oVYG;567R*Yw`6iwYSa17v^1JX&4U?ys*v4(IeI!Pg-LqlLZ=Cjv9My`X42^PW0|-r|EH8)Ih)cOt@2~6EXlQ< zI6JzRL@R|2gdNR}@+Vp^8#AqV12;g)-)9$^MV{+XoZ_|e#7N;L-3|x$4=S{A$mXj9 z4Wrb@N0ZBi`@zE`11k;UzS`fvr>#%qtI*)DV;pBHb8%cAkZ-Goq3}6M@qt;xJ*@J> z>uhX`+=d&PTdDy+OLnW%4#&QCTeFY Nl4{6{vFmBYegW2uPb2^U literal 0 HcmV?d00001 diff --git a/ir_exchange_file/__init__.py b/ir_exchange_file/__init__.py new file mode 100644 index 00000000..046125d4 --- /dev/null +++ b/ir_exchange_file/__init__.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Module for OpenERP +# Copyright (C) 2014 Akretion (http://www.akretion.com). +# @author Sébastien BEAU +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################### + +import attachment +import location +import task + diff --git a/ir_exchange_file/__openerp__.py b/ir_exchange_file/__openerp__.py new file mode 100644 index 00000000..501f5ebf --- /dev/null +++ b/ir_exchange_file/__openerp__.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Module for OpenERP +# Copyright (C) 2014 Akretion (http://www.akretion.com). +# @author Sébastien BEAU +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################### + +{'name': 'ir_exchange_file', + 'version': '0.0.1', + 'author': 'Akretion', + 'website': 'www.akretion.com', + 'license': 'AGPL-3', + 'category': 'Generic Modules', + 'description': """ + + """, + 'depends': [ + 'base', + ], + 'data': [ + 'attachment_view.xml', + 'location_view.xml', + 'task_view.xml', + ], + 'installable': True, + 'application': True, +} + + + + diff --git a/ir_exchange_file/attachment.py b/ir_exchange_file/attachment.py new file mode 100644 index 00000000..fc1d479e --- /dev/null +++ b/ir_exchange_file/attachment.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Module for OpenERP +# Copyright (C) 2014 Akretion (http://www.akretion.com). +# @author Sébastien BEAU +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################### + +from openerp import models, fields, api, _ +from openerp.exceptions import Warning +import hashlib + + +class AttachmentMetadata(models.Model): + _inherit = 'ir.attachment' + + sync_date = fields.Datetime() + parse_state = fields.Selection([ + ('pending', 'Pending'), + ('failed', 'Failed'), + ('done', 'Done'), + ], readonly=True, required=True, default='pending') + diff --git a/ir_exchange_file/attachment_view.xml b/ir_exchange_file/attachment_view.xml new file mode 100644 index 00000000..69cd719e --- /dev/null +++ b/ir_exchange_file/attachment_view.xml @@ -0,0 +1,18 @@ + + + + + + ir.attachment + + + + + + + + + + + + diff --git a/ir_exchange_file/backend.py b/ir_exchange_file/backend.py new file mode 100755 index 00000000..7555f125 --- /dev/null +++ b/ir_exchange_file/backend.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + + +class AbstractConnection(object): + + def __init__(self, host, user, pwd, port=None, allow_dir_creation=False): + self.host = host + self.user = user + self.pwd = pwd + self.port = port + self.allow_dir_creation = allow_dir_creation + self.connection = None + + def connect(self): + return NotImplemented + + def close(self): + return NotImplemented + + def get(self, filename, path=None): + return NotImplemented + + def put(self, fileobject, filename, path=None): + return NotImplemented + + def search(self, filename, path=None): + return NotImplemented + + def move(self, filename, oldpath, newpath): + return NotImplemented + + def rename(self, oldfilename, newfilename, path=None): + return NotImplemented diff --git a/ir_exchange_file/backends/.filestore.py.swp b/ir_exchange_file/backends/.filestore.py.swp new file mode 100644 index 0000000000000000000000000000000000000000..0940500770feb6bb3b7b81a7066109dd0e57bc69 GIT binary patch literal 12288 zcmeI2J&YVR6vv&=gpUh|4@IJKHbv%OcJ~5N5)NIYB+^M&a7_*hA{cpRytk8^wI{ac zc9$q1r3rMAsA&)qD3lbm&>&GlLAp>-5Ty?t5`TL>c6019*Bpc5WvYT#8W!v{Du3vkc>wWrt>^gR3B4u}1x+-VN z=x(YaW(P4DLQl$WP!lxCwe71oxl8c=3TSW%oB}K0 zA!5D@egk*FXW&zC3ygu&M4bl{_K#4V=JR!G=80JnM&4W{JH}Xbgs%&1TbVDLRy>Kr$4;d(?4t`fZ@tx*JL9Y%H7)kdIA?oX``Ba*6m7=D`VQ1 zijHY2Xxu0lWvMoi;=0HTP5X_{w%7QB#*YO9)+ijb7Lrs2)*I`@qv6)ASl8O!#wyP= zUu~W!%1rOp^&8#mH(DJyJ!B@>BT+L7KHFEN8LN-xOvQt0h3hFp*Xds?T1m5|3f>r$ zg~Xk3XDkJ{wMX4DDhrTBikP zI?kjnkHnm9zi3Q(0-Tz&qjZsT~Gnbs)*0Ni(8C#~GxfWdP^USQRGaQ%N*Liu<<|{W>WTmu;biKYV z-YVO7_{28bQ{lp^Q>^XhlIt|y;6hIu#bUF?^p2gi%&>F9h#^3cCbmzRurKtQfx$BF z*DE=sh7WT)EiH39-NLg}Oik?6q-vD9{-!6o9M|boci-!QwOdn9akH1UUuz?&C8^k6 crS96VZ6PO)cMGk$y;O9$*roZz45T3c021e3DF6Tf literal 0 HcmV?d00001 diff --git a/ir_exchange_file/backends/filestore.py b/ir_exchange_file/backends/filestore.py new file mode 100755 index 00000000..febd353b --- /dev/null +++ b/ir_exchange_file/backends/filestore.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from ..backend import AbstractConnection +import sys +import os +from tempfile import TemporaryFile + +class AbtractTask() + + def __init__(self, cr, uid): + + + +class FileStoreConnection(AbstractTask): + _key = "filestore" + _name = "Filestore" + _synchronize_type = None + + def __init__(self, host, user, pwd, port=None, allow_dir_creation=False): + super(FilestoreConnection, self).__init__(host, user, pwd, port, allow_dir_creation) + + def connect(self): + return NotImplemented + + def close(self): + return NotImplemented + + def get(self, filename, path=None): + if path: + filepath = "{}/{}".format(path, filename) + else: + filepath = filename + return open(filepath, 'r+b') + + def put(self, fileobject, filename, path=None): + if path: + filepath = "{}/{}".format(path, filename) + else: + filepath = filename + output = open(filepath, 'w+b') + return True + + def search(self, filename, path=None): + if path: + filepath = "{}/{}".format(path, filename) + else: + filepath = filename + connection_list_result = os.listdir(filepath) + return [x for x in connection_list_result if filename in x] + + def move(self, filename, oldpath, newpath): + os.rename( + os.path.join(oldpath, filename), + os.path.join(newpath, filename) + ) + + def rename(self, oldfilename, newfilename, path=None): + return NotImplemented + +class ImportFileStore(FileStoreConnection): + _synchronize_type = "import" + + + def run(): + diff --git a/ir_exchange_file/backends/ftp.py b/ir_exchange_file/backends/ftp.py new file mode 100755 index 00000000..7316b81f --- /dev/null +++ b/ir_exchange_file/backends/ftp.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + + +import sys +import os +from tempfile import TemporaryFile +from ftplib import FTP + +class FTPConnection(object): + + def __init__(self, host, user, pwd, port=None, allow_dir_creation=False): + super(FTPConnection, self).__init__(host, user, pwd, port, allow_dir_creation) + if not port: + self.port = 21 + self.protocol = "FTP" + + def connect(self): + self.connection = FTP(self.location, self.port) + self.connection.login(self.user, self.pwd) + + def close(self): + self.connection.close() + + def get(self, filename, path=None): + if path: + filepath = "{}/{}".format(path, filename) + else: + filepath = filename + outfile = TemporaryFile('w+b') + self.connection.retrbinary('RETR ' + filepath, outfile.write) + return outfile + + def put(self, fileobject, filename, path=None): + if path: + filepath = "{}/{}".format(path, filename) + else: + filepath = filename + self.connection.storbinary('STOR ' + filepath, fileobject) + return True + + def search(self, filename, path=None): + if path: + filepath = "{}/{}".format(path, filename) + else: + filepath = filename + connection_list_result = self.connection.nlst() + return [x for x in connection_list_result if filename in x] + + + def move(self, filename, oldpath, newpath): + self.connection.rename( + os.path.join(oldpath, filename), + os.path.join(newpath, filename) + ) + + def rename(self, oldfilename, newfilename, path=None): + return NotImplemented diff --git a/ir_exchange_file/backends/sftp.py b/ir_exchange_file/backends/sftp.py new file mode 100755 index 00000000..4d40b896 --- /dev/null +++ b/ir_exchange_file/backends/sftp.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import sys +import paramiko +import os +from tempfile import TemporaryFile + + +class SFTPConnection(AbstractConnection): + + def __init__(self, host, user, pwd, port=None, allow_dir_creation=False): + super(SFTPConnection, self).__init__(host, user, pwd, port, allow_dir_creation) + if not port: + self.port = 22 + self.protocol = "STFP" + + def connect(self): + self.ssh = paramiko.SSHClient() + self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + self.ssh.connect(self.host, self.port, self.user, self.pwd, compress=True) + self.connection = self.ssh.open_sftp() + + def close(self): + self.connection.close() + + def get(self, filename, path=None): + if path: + remotefile = "{}/{}".format(path, filename) + else: + remotefile = filename + localfile = filename + newfile = open(filename, 'w') + self.connection.getfo(remotefile, newfile) + return newfile + + def put(self, fileobject, filename, path=None): + if path: + remotefile = "{}/{}".format(path, filename) + else: + remotefile = filename + if self.allow_dir_creation: + self.connection.mkdirs(path) + oldfile = open(fileobj, 'r') + self.connection.putfo(oldfile, remotefile) + + def search(self, filename, path=None): + if path: + self.connection.chdir(path) + file_list = self.connection.listdir() + return [x for x in file_list if filename in x] + + def move(self, filename, oldpath, newpath): + self.connection.rename(os.path.join(oldpath, filename), os.path.join(newpath, filename)) + + def rename(self, oldfilename, newfilename, path=None): + if not path: + path = '' + self.connection.rename(os.path.join(path, oldfilename), os.path.join(path, newfilename)) diff --git a/ir_exchange_file/location.py b/ir_exchange_file/location.py new file mode 100644 index 00000000..d82ef52b --- /dev/null +++ b/ir_exchange_file/location.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Module for OpenERP +# Copyright (C) 2014 Akretion (http://www.akretion.com). +# @author Sébastien BEAU +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################### + +from openerp import models, fields + +class Location(models.Model): + _name = 'ir.location' + _description = 'Description' + + name = fields.Char(string='Name') + protocol = fields.Selection(selection='_get_protocol') + address = fields.Char(string='Address') + port = fields.Integer() + login = fields.Char() + password = fields.Char() + + + def _get_protocol(self): + return [('ftp', 'FTP'), ('sftp', 'SFTP'), ('filestore', 'Filestore')] + diff --git a/ir_exchange_file/location_view.xml b/ir_exchange_file/location_view.xml new file mode 100644 index 00000000..c4d4ffcf --- /dev/null +++ b/ir_exchange_file/location_view.xml @@ -0,0 +1,52 @@ + + + + + + ir.location + +
+ + + + + + + + + + + +
+
+
+ + + ir.location + + + + + + + + + + + + + + Locations + ir.actions.act_window + ir.location + form + + + + + +
+
diff --git a/ir_exchange_file/task.py b/ir_exchange_file/task.py new file mode 100644 index 00000000..c632ec6c --- /dev/null +++ b/ir_exchange_file/task.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Module for OpenERP +# Copyright (C) 2014 Akretion (http://www.akretion.com). +# @author Sébastien BEAU +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################### + +import sys +from openerp import models, fields + +class Task(models.Model): + _name = 'ir.location.task' + _description = 'Description' + + name = fields.Char() + method = fields.Selection([ + ('ftp_import', 'FTP import'), + ('ftp_export', 'FTP export'), + ('sftp_import', 'SFTP import'), + ('sftp_export', 'SFTP export'), + ('filestore_import', 'Filestore import'), + ('filestore_export', 'Filestore export'), + ]) + filename = fields.Char() + filepath = fields.Char() + location_id = fields.Many2one('ir.location', string='Location') + + def run(self): + connection_class = ... + + method_class = getattr(sys.modules[__name__], self.method) + conn = method_class(config) + conn.run() diff --git a/ir_exchange_file/task_view.xml b/ir_exchange_file/task_view.xml new file mode 100644 index 00000000..b8e928b1 --- /dev/null +++ b/ir_exchange_file/task_view.xml @@ -0,0 +1,47 @@ + + + + + + ir.location.task + +
+ + + + + + + + +
+
+
+ + + ir.location.task + + + + + + + + + + + + Tasks + ir.actions.act_window + ir.location.task + form + + + + + +
+
From 8173c751a737c7ed1cfd233f92d96eaa4a1d07eb Mon Sep 17 00:00:00 2001 From: Valentin Chemiere Date: Wed, 18 Feb 2015 15:03:37 +0100 Subject: [PATCH 03/19] Update readme --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 183a6cff..bcded749 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,2 @@ -# file_exchange -WIP +# ir_attachment_exchange +Work in Progress From 1a8e242bb3642cf7cb575231533f1ad80d7da9ec Mon Sep 17 00:00:00 2001 From: Valentin Chemiere Date: Wed, 18 Feb 2015 15:45:36 +0100 Subject: [PATCH 04/19] Module name --- external_file_location/.__openerp__.py.swp | Bin 0 -> 12288 bytes external_file_location/.attachment.py.swp | Bin 0 -> 12288 bytes external_file_location/.backend.py.swp | Bin 0 -> 12288 bytes external_file_location/__init__.py | 26 +++++++ external_file_location/__openerp__.py | 46 ++++++++++++ external_file_location/attachment.py | 37 ++++++++++ external_file_location/attachment_view.xml | 18 +++++ external_file_location/backend.py | 34 +++++++++ .../backends/.filestore.py.swp | Bin 0 -> 12288 bytes external_file_location/backends/filestore.py | 66 ++++++++++++++++++ external_file_location/backends/ftp.py | 58 +++++++++++++++ external_file_location/backends/sftp.py | 59 ++++++++++++++++ external_file_location/location.py | 39 +++++++++++ external_file_location/location_view.xml | 52 ++++++++++++++ external_file_location/task.py | 48 +++++++++++++ external_file_location/task_view.xml | 47 +++++++++++++ 16 files changed, 530 insertions(+) create mode 100644 external_file_location/.__openerp__.py.swp create mode 100644 external_file_location/.attachment.py.swp create mode 100644 external_file_location/.backend.py.swp create mode 100644 external_file_location/__init__.py create mode 100644 external_file_location/__openerp__.py create mode 100644 external_file_location/attachment.py create mode 100644 external_file_location/attachment_view.xml create mode 100755 external_file_location/backend.py create mode 100644 external_file_location/backends/.filestore.py.swp create mode 100755 external_file_location/backends/filestore.py create mode 100755 external_file_location/backends/ftp.py create mode 100755 external_file_location/backends/sftp.py create mode 100644 external_file_location/location.py create mode 100644 external_file_location/location_view.xml create mode 100644 external_file_location/task.py create mode 100644 external_file_location/task_view.xml diff --git a/external_file_location/.__openerp__.py.swp b/external_file_location/.__openerp__.py.swp new file mode 100644 index 0000000000000000000000000000000000000000..8d9e9bd5ea09629b38a3a52be894b2fc59ae923d GIT binary patch literal 12288 zcmeI2TW=dh6vrn#AZRI|_6r=62Pep0=TZs^Rn={9QcJlQUx+M(tg&}&k67=lW@hbJ zT>1%kMZELG6CZ#}h58u~5}yDGA;dcZ{yW|f3!#*UzEI6b|7?8D%-M5(bH-AX>2JPM zKcd%zn+)US{KsN^|14uuIa2!EaLLontDL3gk~z8SV>hv?BBiQR5)>OH*d@!Df z#P;MQ)oLoLy`D-%B6Qm81?l`TfxX)k@B}U=klA77jcaUYduubFxVHKV-MHSle2w>d z0-k^;;0bsFo`5Id33vjYz*9xQrYr0LDte}3Gj%qpPy&!t1Czz|NlS#`~Rnx82b$R6nX%?1zmxjgC4%f*oV+- z&hEz=?|m|??P1uoUjF|~A%-*1%zI#N*<3*#c85Ro7%+~Gmv zu(IQ{w~HsBk&+RA{q+QrZkQgDn_8C?$O20!iIl zDs>_(cNLa#HSoy}w#L?goX08|(M;Mg*|9W)YrZ4RCjn7?NQts#GD6Vpv9;-LwVIbX zO0q!dQMF)Vs&@hxsjD(FW0l1bjrmlN76T!tA|g%$mCi{G@diN$#~liXL!lKp(mbX{ z)<-ZpTtF@od+aQOH9F14NvnopTPPCQxkCDxrPF%*{z<1z;c=Hv!)6l!yKg#73SKc4i7N)z zqGO2?XIyKZ*t|#=qP>KYKsA!aYKg#@D;G=0xJZo{+>*2i9LLlblo>J1;&OqY08-9t zSjWV<$}}n0xf{fjImKvOO+`N~%1?8aW{mgr=Pt#4jnZ0bXpy6!eU&9>D70Uh1UCI* zIqeiX}w pmsf5SjpWY1>#`GkkJOrtJewywkYnZ53eHC)lhH1@%fPvX{RLiC&r1LR literal 0 HcmV?d00001 diff --git a/external_file_location/.attachment.py.swp b/external_file_location/.attachment.py.swp new file mode 100644 index 0000000000000000000000000000000000000000..dc65a5c0260fc54d033d6e50ac8148dff6dbdf8c GIT binary patch literal 12288 zcmeI2O^+Kj7{^^Mw1pPb-Z(wo1tme*NnZgi-Kyz!w~f?&QIc)7AQU+nPsZwGJlLMi zMxge{6*wR`apqJwa6lYD;xlmN!T}C^1O)s&nd}QHT9A6Fu%-XbiyuGs^LuP3(Yk;B zZflKR4=yv57a5B>KX)Izw9c;Ken$?KcJGfUh26q#W{$9XP#88;s8nM|6_<=_YZ_Ab zxV3x`k3?#FBVl>SE&r$2`=&uQIigEn?Fo1SjzDgsg*Q&IOBXMkcaQUP=jiO2EqCjS zC*TQq0-k^;;0bsFo`5Id3H(n8*z6d4h*+N~<1Kz1;Qm+=p=L;`tCGi z??YFhW6+nI!r1cSF5pE*D@j_k|+?nR_K%`y_lPrcp6S$%k<#ub4!mg zUflELd=}0Qw^}0SGm-sZ6ke$o2JXm!bsdCd@YxM&)5l3o5SGPL7J5)RKh3d_j+i7c@9yOw8 zEVqJ|kYAC6Cg_Mn46IaX_3feyGf4+M#B#voCZ4Pui4ySPEblp5j=3%^{+_zBtHQ!q zr9~Nk33jP+2(e=b0@RafEVQ&3Yeni{{~T+D4H9k)HTMp|u9atCt0NVP!~|>Zf-Es6 zN14)l(||=5!dKx8g-L#0`{bPk!#>ZY#iCAT^u`B+Z)di`Vzn`iR~o+k&E zr8Jn$YN+`rAZkUFDqF`Q5p*@SHd|~o#^Z4?O!GkLVIz?Pks4td*Mh>+w#v!GDo;Wz zh8?$*2h#=4u`^^7Qc?`pGm3;(bj#u432o+mIHNmLhvmSIw4+FA8X?IPA}bQv zD2B1N(q6vZT<!Aaxf*6^0X)YfLIPlWN%YCX`^o zXxb6l;MFwz%&{~v`sq)KA+BnK)=`ng7zM4WJjJ4ed7T9I;{E1ysmxFj0LBDE)VjSI zos7)C&L45}SO$?Bh$#oM=z^gEPccpQ;W!l7%^O8xLm5p<5Z8HbW4!GA@I{{+D?$Ip zN^^^@8c{w6eZlkV{60Qb911M>YsDazG4r$>#+ItfHM)5I!ewffMpQj8sSmcSl@m#Y kIX>0gEZe|BUTJT-(LC9LF)qBifc4>a5!Yi=x0Q@=cS;n4S z_(r4y-e z;!okWNS;WQ){F6+`KXx;kb%;`z;$ZZm)Modmo|N*vbM_4pSwqT$N(8217v^sN1Uujr7=wr40oVYG;567R*Yw`6iwYSa17v^1JX&4U?ys*v4(IeI!Pg-LqlLZ=Cjv9My`X42^PW0|-r|EH8)Ih)cOt@2~6EXlQ< zI6JzRL@R|2gdNR}@+Vp^8#AqV12;g)-)9$^MV{+XoZ_|e#7N;L-3|x$4=S{A$mXj9 z4Wrb@N0ZBi`@zE`11k;UzS`fvr>#%qtI*)DV;pBHb8%cAkZ-Goq3}6M@qt;xJ*@J> z>uhX`+=d&PTdDy+OLnW%4#&QCTeFY Nl4{6{vFmBYegW2uPb2^U literal 0 HcmV?d00001 diff --git a/external_file_location/__init__.py b/external_file_location/__init__.py new file mode 100644 index 00000000..046125d4 --- /dev/null +++ b/external_file_location/__init__.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Module for OpenERP +# Copyright (C) 2014 Akretion (http://www.akretion.com). +# @author Sébastien BEAU +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################### + +import attachment +import location +import task + diff --git a/external_file_location/__openerp__.py b/external_file_location/__openerp__.py new file mode 100644 index 00000000..e2b4624f --- /dev/null +++ b/external_file_location/__openerp__.py @@ -0,0 +1,46 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Module for OpenERP +# Copyright (C) 2014 Akretion (http://www.akretion.com). +# @author Sébastien BEAU +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################### + +{'name': 'external_file_location', + 'version': '0.0.1', + 'author': 'Akretion', + 'website': 'www.akretion.com', + 'license': 'AGPL-3', + 'category': 'Generic Modules', + 'description': """ + + """, + 'depends': [ + 'base', + ], + 'data': [ + 'attachment_view.xml', + 'location_view.xml', + 'task_view.xml', + ], + 'installable': True, + 'application': True, +} + + + + diff --git a/external_file_location/attachment.py b/external_file_location/attachment.py new file mode 100644 index 00000000..fc1d479e --- /dev/null +++ b/external_file_location/attachment.py @@ -0,0 +1,37 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Module for OpenERP +# Copyright (C) 2014 Akretion (http://www.akretion.com). +# @author Sébastien BEAU +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################### + +from openerp import models, fields, api, _ +from openerp.exceptions import Warning +import hashlib + + +class AttachmentMetadata(models.Model): + _inherit = 'ir.attachment' + + sync_date = fields.Datetime() + parse_state = fields.Selection([ + ('pending', 'Pending'), + ('failed', 'Failed'), + ('done', 'Done'), + ], readonly=True, required=True, default='pending') + diff --git a/external_file_location/attachment_view.xml b/external_file_location/attachment_view.xml new file mode 100644 index 00000000..69cd719e --- /dev/null +++ b/external_file_location/attachment_view.xml @@ -0,0 +1,18 @@ + + + + + + ir.attachment + + + + + + + + + + + + diff --git a/external_file_location/backend.py b/external_file_location/backend.py new file mode 100755 index 00000000..7555f125 --- /dev/null +++ b/external_file_location/backend.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + + +class AbstractConnection(object): + + def __init__(self, host, user, pwd, port=None, allow_dir_creation=False): + self.host = host + self.user = user + self.pwd = pwd + self.port = port + self.allow_dir_creation = allow_dir_creation + self.connection = None + + def connect(self): + return NotImplemented + + def close(self): + return NotImplemented + + def get(self, filename, path=None): + return NotImplemented + + def put(self, fileobject, filename, path=None): + return NotImplemented + + def search(self, filename, path=None): + return NotImplemented + + def move(self, filename, oldpath, newpath): + return NotImplemented + + def rename(self, oldfilename, newfilename, path=None): + return NotImplemented diff --git a/external_file_location/backends/.filestore.py.swp b/external_file_location/backends/.filestore.py.swp new file mode 100644 index 0000000000000000000000000000000000000000..0940500770feb6bb3b7b81a7066109dd0e57bc69 GIT binary patch literal 12288 zcmeI2J&YVR6vv&=gpUh|4@IJKHbv%OcJ~5N5)NIYB+^M&a7_*hA{cpRytk8^wI{ac zc9$q1r3rMAsA&)qD3lbm&>&GlLAp>-5Ty?t5`TL>c6019*Bpc5WvYT#8W!v{Du3vkc>wWrt>^gR3B4u}1x+-VN z=x(YaW(P4DLQl$WP!lxCwe71oxl8c=3TSW%oB}K0 zA!5D@egk*FXW&zC3ygu&M4bl{_K#4V=JR!G=80JnM&4W{JH}Xbgs%&1TbVDLRy>Kr$4;d(?4t`fZ@tx*JL9Y%H7)kdIA?oX``Ba*6m7=D`VQ1 zijHY2Xxu0lWvMoi;=0HTP5X_{w%7QB#*YO9)+ijb7Lrs2)*I`@qv6)ASl8O!#wyP= zUu~W!%1rOp^&8#mH(DJyJ!B@>BT+L7KHFEN8LN-xOvQt0h3hFp*Xds?T1m5|3f>r$ zg~Xk3XDkJ{wMX4DDhrTBikP zI?kjnkHnm9zi3Q(0-Tz&qjZsT~Gnbs)*0Ni(8C#~GxfWdP^USQRGaQ%N*Liu<<|{W>WTmu;biKYV z-YVO7_{28bQ{lp^Q>^XhlIt|y;6hIu#bUF?^p2gi%&>F9h#^3cCbmzRurKtQfx$BF z*DE=sh7WT)EiH39-NLg}Oik?6q-vD9{-!6o9M|boci-!QwOdn9akH1UUuz?&C8^k6 crS96VZ6PO)cMGk$y;O9$*roZz45T3c021e3DF6Tf literal 0 HcmV?d00001 diff --git a/external_file_location/backends/filestore.py b/external_file_location/backends/filestore.py new file mode 100755 index 00000000..febd353b --- /dev/null +++ b/external_file_location/backends/filestore.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from ..backend import AbstractConnection +import sys +import os +from tempfile import TemporaryFile + +class AbtractTask() + + def __init__(self, cr, uid): + + + +class FileStoreConnection(AbstractTask): + _key = "filestore" + _name = "Filestore" + _synchronize_type = None + + def __init__(self, host, user, pwd, port=None, allow_dir_creation=False): + super(FilestoreConnection, self).__init__(host, user, pwd, port, allow_dir_creation) + + def connect(self): + return NotImplemented + + def close(self): + return NotImplemented + + def get(self, filename, path=None): + if path: + filepath = "{}/{}".format(path, filename) + else: + filepath = filename + return open(filepath, 'r+b') + + def put(self, fileobject, filename, path=None): + if path: + filepath = "{}/{}".format(path, filename) + else: + filepath = filename + output = open(filepath, 'w+b') + return True + + def search(self, filename, path=None): + if path: + filepath = "{}/{}".format(path, filename) + else: + filepath = filename + connection_list_result = os.listdir(filepath) + return [x for x in connection_list_result if filename in x] + + def move(self, filename, oldpath, newpath): + os.rename( + os.path.join(oldpath, filename), + os.path.join(newpath, filename) + ) + + def rename(self, oldfilename, newfilename, path=None): + return NotImplemented + +class ImportFileStore(FileStoreConnection): + _synchronize_type = "import" + + + def run(): + diff --git a/external_file_location/backends/ftp.py b/external_file_location/backends/ftp.py new file mode 100755 index 00000000..7316b81f --- /dev/null +++ b/external_file_location/backends/ftp.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + + +import sys +import os +from tempfile import TemporaryFile +from ftplib import FTP + +class FTPConnection(object): + + def __init__(self, host, user, pwd, port=None, allow_dir_creation=False): + super(FTPConnection, self).__init__(host, user, pwd, port, allow_dir_creation) + if not port: + self.port = 21 + self.protocol = "FTP" + + def connect(self): + self.connection = FTP(self.location, self.port) + self.connection.login(self.user, self.pwd) + + def close(self): + self.connection.close() + + def get(self, filename, path=None): + if path: + filepath = "{}/{}".format(path, filename) + else: + filepath = filename + outfile = TemporaryFile('w+b') + self.connection.retrbinary('RETR ' + filepath, outfile.write) + return outfile + + def put(self, fileobject, filename, path=None): + if path: + filepath = "{}/{}".format(path, filename) + else: + filepath = filename + self.connection.storbinary('STOR ' + filepath, fileobject) + return True + + def search(self, filename, path=None): + if path: + filepath = "{}/{}".format(path, filename) + else: + filepath = filename + connection_list_result = self.connection.nlst() + return [x for x in connection_list_result if filename in x] + + + def move(self, filename, oldpath, newpath): + self.connection.rename( + os.path.join(oldpath, filename), + os.path.join(newpath, filename) + ) + + def rename(self, oldfilename, newfilename, path=None): + return NotImplemented diff --git a/external_file_location/backends/sftp.py b/external_file_location/backends/sftp.py new file mode 100755 index 00000000..4d40b896 --- /dev/null +++ b/external_file_location/backends/sftp.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +import sys +import paramiko +import os +from tempfile import TemporaryFile + + +class SFTPConnection(AbstractConnection): + + def __init__(self, host, user, pwd, port=None, allow_dir_creation=False): + super(SFTPConnection, self).__init__(host, user, pwd, port, allow_dir_creation) + if not port: + self.port = 22 + self.protocol = "STFP" + + def connect(self): + self.ssh = paramiko.SSHClient() + self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) + self.ssh.connect(self.host, self.port, self.user, self.pwd, compress=True) + self.connection = self.ssh.open_sftp() + + def close(self): + self.connection.close() + + def get(self, filename, path=None): + if path: + remotefile = "{}/{}".format(path, filename) + else: + remotefile = filename + localfile = filename + newfile = open(filename, 'w') + self.connection.getfo(remotefile, newfile) + return newfile + + def put(self, fileobject, filename, path=None): + if path: + remotefile = "{}/{}".format(path, filename) + else: + remotefile = filename + if self.allow_dir_creation: + self.connection.mkdirs(path) + oldfile = open(fileobj, 'r') + self.connection.putfo(oldfile, remotefile) + + def search(self, filename, path=None): + if path: + self.connection.chdir(path) + file_list = self.connection.listdir() + return [x for x in file_list if filename in x] + + def move(self, filename, oldpath, newpath): + self.connection.rename(os.path.join(oldpath, filename), os.path.join(newpath, filename)) + + def rename(self, oldfilename, newfilename, path=None): + if not path: + path = '' + self.connection.rename(os.path.join(path, oldfilename), os.path.join(path, newfilename)) diff --git a/external_file_location/location.py b/external_file_location/location.py new file mode 100644 index 00000000..d82ef52b --- /dev/null +++ b/external_file_location/location.py @@ -0,0 +1,39 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Module for OpenERP +# Copyright (C) 2014 Akretion (http://www.akretion.com). +# @author Sébastien BEAU +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################### + +from openerp import models, fields + +class Location(models.Model): + _name = 'ir.location' + _description = 'Description' + + name = fields.Char(string='Name') + protocol = fields.Selection(selection='_get_protocol') + address = fields.Char(string='Address') + port = fields.Integer() + login = fields.Char() + password = fields.Char() + + + def _get_protocol(self): + return [('ftp', 'FTP'), ('sftp', 'SFTP'), ('filestore', 'Filestore')] + diff --git a/external_file_location/location_view.xml b/external_file_location/location_view.xml new file mode 100644 index 00000000..c4d4ffcf --- /dev/null +++ b/external_file_location/location_view.xml @@ -0,0 +1,52 @@ + + + + + + ir.location + +
+ + + + + + + + + + + +
+
+
+ + + ir.location + + + + + + + + + + + + + + Locations + ir.actions.act_window + ir.location + form + + + + + +
+
diff --git a/external_file_location/task.py b/external_file_location/task.py new file mode 100644 index 00000000..c632ec6c --- /dev/null +++ b/external_file_location/task.py @@ -0,0 +1,48 @@ +# -*- coding: utf-8 -*- +############################################################################### +# +# Module for OpenERP +# Copyright (C) 2014 Akretion (http://www.akretion.com). +# @author Sébastien BEAU +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################### + +import sys +from openerp import models, fields + +class Task(models.Model): + _name = 'ir.location.task' + _description = 'Description' + + name = fields.Char() + method = fields.Selection([ + ('ftp_import', 'FTP import'), + ('ftp_export', 'FTP export'), + ('sftp_import', 'SFTP import'), + ('sftp_export', 'SFTP export'), + ('filestore_import', 'Filestore import'), + ('filestore_export', 'Filestore export'), + ]) + filename = fields.Char() + filepath = fields.Char() + location_id = fields.Many2one('ir.location', string='Location') + + def run(self): + connection_class = ... + + method_class = getattr(sys.modules[__name__], self.method) + conn = method_class(config) + conn.run() diff --git a/external_file_location/task_view.xml b/external_file_location/task_view.xml new file mode 100644 index 00000000..b8e928b1 --- /dev/null +++ b/external_file_location/task_view.xml @@ -0,0 +1,47 @@ + + + + + + ir.location.task + +
+ + + + + + + + +
+
+
+ + + ir.location.task + + + + + + + + + + + + Tasks + ir.actions.act_window + ir.location.task + form + + + + + +
+
From 1071c1628b12e28d573f44c775acf228eee6d2f3 Mon Sep 17 00:00:00 2001 From: Valentin Chemiere Date: Wed, 18 Feb 2015 15:49:54 +0100 Subject: [PATCH 05/19] Remove old module ir_exchange_file/task_view.xml --- ir_exchange_file/.__openerp__.py.swp | Bin 12288 -> 0 bytes ir_exchange_file/.attachment.py.swp | Bin 12288 -> 0 bytes ir_exchange_file/.backend.py.swp | Bin 12288 -> 0 bytes ir_exchange_file/__init__.py | 26 -------- ir_exchange_file/__openerp__.py | 46 -------------- ir_exchange_file/attachment.py | 37 ----------- ir_exchange_file/attachment_view.xml | 18 ------ ir_exchange_file/backend.py | 34 ---------- ir_exchange_file/backends/.filestore.py.swp | Bin 12288 -> 0 bytes ir_exchange_file/backends/filestore.py | 66 -------------------- ir_exchange_file/backends/ftp.py | 58 ----------------- ir_exchange_file/backends/sftp.py | 59 ----------------- ir_exchange_file/location.py | 39 ------------ ir_exchange_file/location_view.xml | 52 --------------- ir_exchange_file/task.py | 48 -------------- ir_exchange_file/task_view.xml | 47 -------------- 16 files changed, 530 deletions(-) delete mode 100644 ir_exchange_file/.__openerp__.py.swp delete mode 100644 ir_exchange_file/.attachment.py.swp delete mode 100644 ir_exchange_file/.backend.py.swp delete mode 100644 ir_exchange_file/__init__.py delete mode 100644 ir_exchange_file/__openerp__.py delete mode 100644 ir_exchange_file/attachment.py delete mode 100644 ir_exchange_file/attachment_view.xml delete mode 100755 ir_exchange_file/backend.py delete mode 100644 ir_exchange_file/backends/.filestore.py.swp delete mode 100755 ir_exchange_file/backends/filestore.py delete mode 100755 ir_exchange_file/backends/ftp.py delete mode 100755 ir_exchange_file/backends/sftp.py delete mode 100644 ir_exchange_file/location.py delete mode 100644 ir_exchange_file/location_view.xml delete mode 100644 ir_exchange_file/task.py delete mode 100644 ir_exchange_file/task_view.xml diff --git a/ir_exchange_file/.__openerp__.py.swp b/ir_exchange_file/.__openerp__.py.swp deleted file mode 100644 index 8d9e9bd5ea09629b38a3a52be894b2fc59ae923d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12288 zcmeI2TW=dh6vrn#AZRI|_6r=62Pep0=TZs^Rn={9QcJlQUx+M(tg&}&k67=lW@hbJ zT>1%kMZELG6CZ#}h58u~5}yDGA;dcZ{yW|f3!#*UzEI6b|7?8D%-M5(bH-AX>2JPM zKcd%zn+)US{KsN^|14uuIa2!EaLLontDL3gk~z8SV>hv?BBiQR5)>OH*d@!Df z#P;MQ)oLoLy`D-%B6Qm81?l`TfxX)k@B}U=klA77jcaUYduubFxVHKV-MHSle2w>d z0-k^;;0bsFo`5Id33vjYz*9xQrYr0LDte}3Gj%qpPy&!t1Czz|NlS#`~Rnx82b$R6nX%?1zmxjgC4%f*oV+- z&hEz=?|m|??P1uoUjF|~A%-*1%zI#N*<3*#c85Ro7%+~Gmv zu(IQ{w~HsBk&+RA{q+QrZkQgDn_8C?$O20!iIl zDs>_(cNLa#HSoy}w#L?goX08|(M;Mg*|9W)YrZ4RCjn7?NQts#GD6Vpv9;-LwVIbX zO0q!dQMF)Vs&@hxsjD(FW0l1bjrmlN76T!tA|g%$mCi{G@diN$#~liXL!lKp(mbX{ z)<-ZpTtF@od+aQOH9F14NvnopTPPCQxkCDxrPF%*{z<1z;c=Hv!)6l!yKg#73SKc4i7N)z zqGO2?XIyKZ*t|#=qP>KYKsA!aYKg#@D;G=0xJZo{+>*2i9LLlblo>J1;&OqY08-9t zSjWV<$}}n0xf{fjImKvOO+`N~%1?8aW{mgr=Pt#4jnZ0bXpy6!eU&9>D70Uh1UCI* zIqeiX}w pmsf5SjpWY1>#`GkkJOrtJewywkYnZ53eHC)lhH1@%fPvX{RLiC&r1LR diff --git a/ir_exchange_file/.attachment.py.swp b/ir_exchange_file/.attachment.py.swp deleted file mode 100644 index dc65a5c0260fc54d033d6e50ac8148dff6dbdf8c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12288 zcmeI2O^+Kj7{^^Mw1pPb-Z(wo1tme*NnZgi-Kyz!w~f?&QIc)7AQU+nPsZwGJlLMi zMxge{6*wR`apqJwa6lYD;xlmN!T}C^1O)s&nd}QHT9A6Fu%-XbiyuGs^LuP3(Yk;B zZflKR4=yv57a5B>KX)Izw9c;Ken$?KcJGfUh26q#W{$9XP#88;s8nM|6_<=_YZ_Ab zxV3x`k3?#FBVl>SE&r$2`=&uQIigEn?Fo1SjzDgsg*Q&IOBXMkcaQUP=jiO2EqCjS zC*TQq0-k^;;0bsFo`5Id3H(n8*z6d4h*+N~<1Kz1;Qm+=p=L;`tCGi z??YFhW6+nI!r1cSF5pE*D@j_k|+?nR_K%`y_lPrcp6S$%k<#ub4!mg zUflELd=}0Qw^}0SGm-sZ6ke$o2JXm!bsdCd@YxM&)5l3o5SGPL7J5)RKh3d_j+i7c@9yOw8 zEVqJ|kYAC6Cg_Mn46IaX_3feyGf4+M#B#voCZ4Pui4ySPEblp5j=3%^{+_zBtHQ!q zr9~Nk33jP+2(e=b0@RafEVQ&3Yeni{{~T+D4H9k)HTMp|u9atCt0NVP!~|>Zf-Es6 zN14)l(||=5!dKx8g-L#0`{bPk!#>ZY#iCAT^u`B+Z)di`Vzn`iR~o+k&E zr8Jn$YN+`rAZkUFDqF`Q5p*@SHd|~o#^Z4?O!GkLVIz?Pks4td*Mh>+w#v!GDo;Wz zh8?$*2h#=4u`^^7Qc?`pGm3;(bj#u432o+mIHNmLhvmSIw4+FA8X?IPA}bQv zD2B1N(q6vZT<!Aaxf*6^0X)YfLIPlWN%YCX`^o zXxb6l;MFwz%&{~v`sq)KA+BnK)=`ng7zM4WJjJ4ed7T9I;{E1ysmxFj0LBDE)VjSI zos7)C&L45}SO$?Bh$#oM=z^gEPccpQ;W!l7%^O8xLm5p<5Z8HbW4!GA@I{{+D?$Ip zN^^^@8c{w6eZlkV{60Qb911M>YsDazG4r$>#+ItfHM)5I!ewffMpQj8sSmcSl@m#Y kIX>0gEZe|BUTJT-(LC9LF)qBifc4>a5!Yi=x0Q@=cS;n4S z_(r4y-e z;!okWNS;WQ){F6+`KXx;kb%;`z;$ZZm)Modmo|N*vbM_4pSwqT$N(8217v^sN1Uujr7=wr40oVYG;567R*Yw`6iwYSa17v^1JX&4U?ys*v4(IeI!Pg-LqlLZ=Cjv9My`X42^PW0|-r|EH8)Ih)cOt@2~6EXlQ< zI6JzRL@R|2gdNR}@+Vp^8#AqV12;g)-)9$^MV{+XoZ_|e#7N;L-3|x$4=S{A$mXj9 z4Wrb@N0ZBi`@zE`11k;UzS`fvr>#%qtI*)DV;pBHb8%cAkZ-Goq3}6M@qt;xJ*@J> z>uhX`+=d&PTdDy+OLnW%4#&QCTeFY Nl4{6{vFmBYegW2uPb2^U diff --git a/ir_exchange_file/__init__.py b/ir_exchange_file/__init__.py deleted file mode 100644 index 046125d4..00000000 --- a/ir_exchange_file/__init__.py +++ /dev/null @@ -1,26 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################### -# -# Module for OpenERP -# Copyright (C) 2014 Akretion (http://www.akretion.com). -# @author Sébastien BEAU -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero 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 Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################### - -import attachment -import location -import task - diff --git a/ir_exchange_file/__openerp__.py b/ir_exchange_file/__openerp__.py deleted file mode 100644 index 501f5ebf..00000000 --- a/ir_exchange_file/__openerp__.py +++ /dev/null @@ -1,46 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################### -# -# Module for OpenERP -# Copyright (C) 2014 Akretion (http://www.akretion.com). -# @author Sébastien BEAU -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero 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 Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################### - -{'name': 'ir_exchange_file', - 'version': '0.0.1', - 'author': 'Akretion', - 'website': 'www.akretion.com', - 'license': 'AGPL-3', - 'category': 'Generic Modules', - 'description': """ - - """, - 'depends': [ - 'base', - ], - 'data': [ - 'attachment_view.xml', - 'location_view.xml', - 'task_view.xml', - ], - 'installable': True, - 'application': True, -} - - - - diff --git a/ir_exchange_file/attachment.py b/ir_exchange_file/attachment.py deleted file mode 100644 index fc1d479e..00000000 --- a/ir_exchange_file/attachment.py +++ /dev/null @@ -1,37 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################### -# -# Module for OpenERP -# Copyright (C) 2014 Akretion (http://www.akretion.com). -# @author Sébastien BEAU -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero 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 Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################### - -from openerp import models, fields, api, _ -from openerp.exceptions import Warning -import hashlib - - -class AttachmentMetadata(models.Model): - _inherit = 'ir.attachment' - - sync_date = fields.Datetime() - parse_state = fields.Selection([ - ('pending', 'Pending'), - ('failed', 'Failed'), - ('done', 'Done'), - ], readonly=True, required=True, default='pending') - diff --git a/ir_exchange_file/attachment_view.xml b/ir_exchange_file/attachment_view.xml deleted file mode 100644 index 69cd719e..00000000 --- a/ir_exchange_file/attachment_view.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - ir.attachment - - - - - - - - - - - - diff --git a/ir_exchange_file/backend.py b/ir_exchange_file/backend.py deleted file mode 100755 index 7555f125..00000000 --- a/ir_exchange_file/backend.py +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - - -class AbstractConnection(object): - - def __init__(self, host, user, pwd, port=None, allow_dir_creation=False): - self.host = host - self.user = user - self.pwd = pwd - self.port = port - self.allow_dir_creation = allow_dir_creation - self.connection = None - - def connect(self): - return NotImplemented - - def close(self): - return NotImplemented - - def get(self, filename, path=None): - return NotImplemented - - def put(self, fileobject, filename, path=None): - return NotImplemented - - def search(self, filename, path=None): - return NotImplemented - - def move(self, filename, oldpath, newpath): - return NotImplemented - - def rename(self, oldfilename, newfilename, path=None): - return NotImplemented diff --git a/ir_exchange_file/backends/.filestore.py.swp b/ir_exchange_file/backends/.filestore.py.swp deleted file mode 100644 index 0940500770feb6bb3b7b81a7066109dd0e57bc69..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12288 zcmeI2J&YVR6vv&=gpUh|4@IJKHbv%OcJ~5N5)NIYB+^M&a7_*hA{cpRytk8^wI{ac zc9$q1r3rMAsA&)qD3lbm&>&GlLAp>-5Ty?t5`TL>c6019*Bpc5WvYT#8W!v{Du3vkc>wWrt>^gR3B4u}1x+-VN z=x(YaW(P4DLQl$WP!lxCwe71oxl8c=3TSW%oB}K0 zA!5D@egk*FXW&zC3ygu&M4bl{_K#4V=JR!G=80JnM&4W{JH}Xbgs%&1TbVDLRy>Kr$4;d(?4t`fZ@tx*JL9Y%H7)kdIA?oX``Ba*6m7=D`VQ1 zijHY2Xxu0lWvMoi;=0HTP5X_{w%7QB#*YO9)+ijb7Lrs2)*I`@qv6)ASl8O!#wyP= zUu~W!%1rOp^&8#mH(DJyJ!B@>BT+L7KHFEN8LN-xOvQt0h3hFp*Xds?T1m5|3f>r$ zg~Xk3XDkJ{wMX4DDhrTBikP zI?kjnkHnm9zi3Q(0-Tz&qjZsT~Gnbs)*0Ni(8C#~GxfWdP^USQRGaQ%N*Liu<<|{W>WTmu;biKYV z-YVO7_{28bQ{lp^Q>^XhlIt|y;6hIu#bUF?^p2gi%&>F9h#^3cCbmzRurKtQfx$BF z*DE=sh7WT)EiH39-NLg}Oik?6q-vD9{-!6o9M|boci-!QwOdn9akH1UUuz?&C8^k6 crS96VZ6PO)cMGk$y;O9$*roZz45T3c021e3DF6Tf diff --git a/ir_exchange_file/backends/filestore.py b/ir_exchange_file/backends/filestore.py deleted file mode 100755 index febd353b..00000000 --- a/ir_exchange_file/backends/filestore.py +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -from ..backend import AbstractConnection -import sys -import os -from tempfile import TemporaryFile - -class AbtractTask() - - def __init__(self, cr, uid): - - - -class FileStoreConnection(AbstractTask): - _key = "filestore" - _name = "Filestore" - _synchronize_type = None - - def __init__(self, host, user, pwd, port=None, allow_dir_creation=False): - super(FilestoreConnection, self).__init__(host, user, pwd, port, allow_dir_creation) - - def connect(self): - return NotImplemented - - def close(self): - return NotImplemented - - def get(self, filename, path=None): - if path: - filepath = "{}/{}".format(path, filename) - else: - filepath = filename - return open(filepath, 'r+b') - - def put(self, fileobject, filename, path=None): - if path: - filepath = "{}/{}".format(path, filename) - else: - filepath = filename - output = open(filepath, 'w+b') - return True - - def search(self, filename, path=None): - if path: - filepath = "{}/{}".format(path, filename) - else: - filepath = filename - connection_list_result = os.listdir(filepath) - return [x for x in connection_list_result if filename in x] - - def move(self, filename, oldpath, newpath): - os.rename( - os.path.join(oldpath, filename), - os.path.join(newpath, filename) - ) - - def rename(self, oldfilename, newfilename, path=None): - return NotImplemented - -class ImportFileStore(FileStoreConnection): - _synchronize_type = "import" - - - def run(): - diff --git a/ir_exchange_file/backends/ftp.py b/ir_exchange_file/backends/ftp.py deleted file mode 100755 index 7316b81f..00000000 --- a/ir_exchange_file/backends/ftp.py +++ /dev/null @@ -1,58 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - - -import sys -import os -from tempfile import TemporaryFile -from ftplib import FTP - -class FTPConnection(object): - - def __init__(self, host, user, pwd, port=None, allow_dir_creation=False): - super(FTPConnection, self).__init__(host, user, pwd, port, allow_dir_creation) - if not port: - self.port = 21 - self.protocol = "FTP" - - def connect(self): - self.connection = FTP(self.location, self.port) - self.connection.login(self.user, self.pwd) - - def close(self): - self.connection.close() - - def get(self, filename, path=None): - if path: - filepath = "{}/{}".format(path, filename) - else: - filepath = filename - outfile = TemporaryFile('w+b') - self.connection.retrbinary('RETR ' + filepath, outfile.write) - return outfile - - def put(self, fileobject, filename, path=None): - if path: - filepath = "{}/{}".format(path, filename) - else: - filepath = filename - self.connection.storbinary('STOR ' + filepath, fileobject) - return True - - def search(self, filename, path=None): - if path: - filepath = "{}/{}".format(path, filename) - else: - filepath = filename - connection_list_result = self.connection.nlst() - return [x for x in connection_list_result if filename in x] - - - def move(self, filename, oldpath, newpath): - self.connection.rename( - os.path.join(oldpath, filename), - os.path.join(newpath, filename) - ) - - def rename(self, oldfilename, newfilename, path=None): - return NotImplemented diff --git a/ir_exchange_file/backends/sftp.py b/ir_exchange_file/backends/sftp.py deleted file mode 100755 index 4d40b896..00000000 --- a/ir_exchange_file/backends/sftp.py +++ /dev/null @@ -1,59 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - -import sys -import paramiko -import os -from tempfile import TemporaryFile - - -class SFTPConnection(AbstractConnection): - - def __init__(self, host, user, pwd, port=None, allow_dir_creation=False): - super(SFTPConnection, self).__init__(host, user, pwd, port, allow_dir_creation) - if not port: - self.port = 22 - self.protocol = "STFP" - - def connect(self): - self.ssh = paramiko.SSHClient() - self.ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy()) - self.ssh.connect(self.host, self.port, self.user, self.pwd, compress=True) - self.connection = self.ssh.open_sftp() - - def close(self): - self.connection.close() - - def get(self, filename, path=None): - if path: - remotefile = "{}/{}".format(path, filename) - else: - remotefile = filename - localfile = filename - newfile = open(filename, 'w') - self.connection.getfo(remotefile, newfile) - return newfile - - def put(self, fileobject, filename, path=None): - if path: - remotefile = "{}/{}".format(path, filename) - else: - remotefile = filename - if self.allow_dir_creation: - self.connection.mkdirs(path) - oldfile = open(fileobj, 'r') - self.connection.putfo(oldfile, remotefile) - - def search(self, filename, path=None): - if path: - self.connection.chdir(path) - file_list = self.connection.listdir() - return [x for x in file_list if filename in x] - - def move(self, filename, oldpath, newpath): - self.connection.rename(os.path.join(oldpath, filename), os.path.join(newpath, filename)) - - def rename(self, oldfilename, newfilename, path=None): - if not path: - path = '' - self.connection.rename(os.path.join(path, oldfilename), os.path.join(path, newfilename)) diff --git a/ir_exchange_file/location.py b/ir_exchange_file/location.py deleted file mode 100644 index d82ef52b..00000000 --- a/ir_exchange_file/location.py +++ /dev/null @@ -1,39 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################### -# -# Module for OpenERP -# Copyright (C) 2014 Akretion (http://www.akretion.com). -# @author Sébastien BEAU -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero 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 Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################### - -from openerp import models, fields - -class Location(models.Model): - _name = 'ir.location' - _description = 'Description' - - name = fields.Char(string='Name') - protocol = fields.Selection(selection='_get_protocol') - address = fields.Char(string='Address') - port = fields.Integer() - login = fields.Char() - password = fields.Char() - - - def _get_protocol(self): - return [('ftp', 'FTP'), ('sftp', 'SFTP'), ('filestore', 'Filestore')] - diff --git a/ir_exchange_file/location_view.xml b/ir_exchange_file/location_view.xml deleted file mode 100644 index c4d4ffcf..00000000 --- a/ir_exchange_file/location_view.xml +++ /dev/null @@ -1,52 +0,0 @@ - - - - - - ir.location - -
- - - - - - - - - - - -
-
-
- - - ir.location - - - - - - - - - - - - - - Locations - ir.actions.act_window - ir.location - form - - - - - -
-
diff --git a/ir_exchange_file/task.py b/ir_exchange_file/task.py deleted file mode 100644 index c632ec6c..00000000 --- a/ir_exchange_file/task.py +++ /dev/null @@ -1,48 +0,0 @@ -# -*- coding: utf-8 -*- -############################################################################### -# -# Module for OpenERP -# Copyright (C) 2014 Akretion (http://www.akretion.com). -# @author Sébastien BEAU -# -# This program is free software: you can redistribute it and/or modify -# it under the terms of the GNU Affero 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 Affero General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with this program. If not, see . -# -############################################################################### - -import sys -from openerp import models, fields - -class Task(models.Model): - _name = 'ir.location.task' - _description = 'Description' - - name = fields.Char() - method = fields.Selection([ - ('ftp_import', 'FTP import'), - ('ftp_export', 'FTP export'), - ('sftp_import', 'SFTP import'), - ('sftp_export', 'SFTP export'), - ('filestore_import', 'Filestore import'), - ('filestore_export', 'Filestore export'), - ]) - filename = fields.Char() - filepath = fields.Char() - location_id = fields.Many2one('ir.location', string='Location') - - def run(self): - connection_class = ... - - method_class = getattr(sys.modules[__name__], self.method) - conn = method_class(config) - conn.run() diff --git a/ir_exchange_file/task_view.xml b/ir_exchange_file/task_view.xml deleted file mode 100644 index b8e928b1..00000000 --- a/ir_exchange_file/task_view.xml +++ /dev/null @@ -1,47 +0,0 @@ - - - - - - ir.location.task - -
- - - - - - - - -
-
-
- - - ir.location.task - - - - - - - - - - - - Tasks - ir.actions.act_window - ir.location.task - form - - - - - -
-
From 6fc9c12ecd2a9b88c1d1899c8bbff7d5d901f30f Mon Sep 17 00:00:00 2001 From: Valentin Chemiere Date: Wed, 18 Feb 2015 15:52:32 +0100 Subject: [PATCH 06/19] Delete vim swap files --- external_file_location/.__openerp__.py.swp | Bin 12288 -> 0 bytes external_file_location/.attachment.py.swp | Bin 12288 -> 0 bytes external_file_location/.backend.py.swp | Bin 12288 -> 0 bytes ir_attachment_metadata/.__openerp__.py.swp | Bin 12288 -> 0 bytes 4 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 external_file_location/.__openerp__.py.swp delete mode 100644 external_file_location/.attachment.py.swp delete mode 100644 external_file_location/.backend.py.swp delete mode 100644 ir_attachment_metadata/.__openerp__.py.swp diff --git a/external_file_location/.__openerp__.py.swp b/external_file_location/.__openerp__.py.swp deleted file mode 100644 index 8d9e9bd5ea09629b38a3a52be894b2fc59ae923d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12288 zcmeI2TW=dh6vrn#AZRI|_6r=62Pep0=TZs^Rn={9QcJlQUx+M(tg&}&k67=lW@hbJ zT>1%kMZELG6CZ#}h58u~5}yDGA;dcZ{yW|f3!#*UzEI6b|7?8D%-M5(bH-AX>2JPM zKcd%zn+)US{KsN^|14uuIa2!EaLLontDL3gk~z8SV>hv?BBiQR5)>OH*d@!Df z#P;MQ)oLoLy`D-%B6Qm81?l`TfxX)k@B}U=klA77jcaUYduubFxVHKV-MHSle2w>d z0-k^;;0bsFo`5Id33vjYz*9xQrYr0LDte}3Gj%qpPy&!t1Czz|NlS#`~Rnx82b$R6nX%?1zmxjgC4%f*oV+- z&hEz=?|m|??P1uoUjF|~A%-*1%zI#N*<3*#c85Ro7%+~Gmv zu(IQ{w~HsBk&+RA{q+QrZkQgDn_8C?$O20!iIl zDs>_(cNLa#HSoy}w#L?goX08|(M;Mg*|9W)YrZ4RCjn7?NQts#GD6Vpv9;-LwVIbX zO0q!dQMF)Vs&@hxsjD(FW0l1bjrmlN76T!tA|g%$mCi{G@diN$#~liXL!lKp(mbX{ z)<-ZpTtF@od+aQOH9F14NvnopTPPCQxkCDxrPF%*{z<1z;c=Hv!)6l!yKg#73SKc4i7N)z zqGO2?XIyKZ*t|#=qP>KYKsA!aYKg#@D;G=0xJZo{+>*2i9LLlblo>J1;&OqY08-9t zSjWV<$}}n0xf{fjImKvOO+`N~%1?8aW{mgr=Pt#4jnZ0bXpy6!eU&9>D70Uh1UCI* zIqeiX}w pmsf5SjpWY1>#`GkkJOrtJewywkYnZ53eHC)lhH1@%fPvX{RLiC&r1LR diff --git a/external_file_location/.attachment.py.swp b/external_file_location/.attachment.py.swp deleted file mode 100644 index dc65a5c0260fc54d033d6e50ac8148dff6dbdf8c..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12288 zcmeI2O^+Kj7{^^Mw1pPb-Z(wo1tme*NnZgi-Kyz!w~f?&QIc)7AQU+nPsZwGJlLMi zMxge{6*wR`apqJwa6lYD;xlmN!T}C^1O)s&nd}QHT9A6Fu%-XbiyuGs^LuP3(Yk;B zZflKR4=yv57a5B>KX)Izw9c;Ken$?KcJGfUh26q#W{$9XP#88;s8nM|6_<=_YZ_Ab zxV3x`k3?#FBVl>SE&r$2`=&uQIigEn?Fo1SjzDgsg*Q&IOBXMkcaQUP=jiO2EqCjS zC*TQq0-k^;;0bsFo`5Id3H(n8*z6d4h*+N~<1Kz1;Qm+=p=L;`tCGi z??YFhW6+nI!r1cSF5pE*D@j_k|+?nR_K%`y_lPrcp6S$%k<#ub4!mg zUflELd=}0Qw^}0SGm-sZ6ke$o2JXm!bsdCd@YxM&)5l3o5SGPL7J5)RKh3d_j+i7c@9yOw8 zEVqJ|kYAC6Cg_Mn46IaX_3feyGf4+M#B#voCZ4Pui4ySPEblp5j=3%^{+_zBtHQ!q zr9~Nk33jP+2(e=b0@RafEVQ&3Yeni{{~T+D4H9k)HTMp|u9atCt0NVP!~|>Zf-Es6 zN14)l(||=5!dKx8g-L#0`{bPk!#>ZY#iCAT^u`B+Z)di`Vzn`iR~o+k&E zr8Jn$YN+`rAZkUFDqF`Q5p*@SHd|~o#^Z4?O!GkLVIz?Pks4td*Mh>+w#v!GDo;Wz zh8?$*2h#=4u`^^7Qc?`pGm3;(bj#u432o+mIHNmLhvmSIw4+FA8X?IPA}bQv zD2B1N(q6vZT<!Aaxf*6^0X)YfLIPlWN%YCX`^o zXxb6l;MFwz%&{~v`sq)KA+BnK)=`ng7zM4WJjJ4ed7T9I;{E1ysmxFj0LBDE)VjSI zos7)C&L45}SO$?Bh$#oM=z^gEPccpQ;W!l7%^O8xLm5p<5Z8HbW4!GA@I{{+D?$Ip zN^^^@8c{w6eZlkV{60Qb911M>YsDazG4r$>#+ItfHM)5I!ewffMpQj8sSmcSl@m#Y kIX>0gEZe|BUTJT-(LC9LF)qBifc4>a5!Yi=x0Q@=cS;n4S z_(r4y-e z;!okWNS;WQ){F6+`KXx;kb%;`z;$ZZm)Modmo|N*vbM_4pSwqT$N(8217v^sN1Uujr7=wr40oVYG;567R*Yw`6iwYSa17v^1JX&4U?ys*v4(IeI!Pg-LqlLZ=Cjv9My`X42^PW0|-r|EH8)Ih)cOt@2~6EXlQ< zI6JzRL@R|2gdNR}@+Vp^8#AqV12;g)-)9$^MV{+XoZ_|e#7N;L-3|x$4=S{A$mXj9 z4Wrb@N0ZBi`@zE`11k;UzS`fvr>#%qtI*)DV;pBHb8%cAkZ-Goq3}6M@qt;xJ*@J> z>uhX`+=d&PTdDy+OLnW%4#&QCTeFY Nl4{6{vFmBYegW2uPb2^U diff --git a/ir_attachment_metadata/.__openerp__.py.swp b/ir_attachment_metadata/.__openerp__.py.swp deleted file mode 100644 index 8d9e9bd5ea09629b38a3a52be894b2fc59ae923d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12288 zcmeI2TW=dh6vrn#AZRI|_6r=62Pep0=TZs^Rn={9QcJlQUx+M(tg&}&k67=lW@hbJ zT>1%kMZELG6CZ#}h58u~5}yDGA;dcZ{yW|f3!#*UzEI6b|7?8D%-M5(bH-AX>2JPM zKcd%zn+)US{KsN^|14uuIa2!EaLLontDL3gk~z8SV>hv?BBiQR5)>OH*d@!Df z#P;MQ)oLoLy`D-%B6Qm81?l`TfxX)k@B}U=klA77jcaUYduubFxVHKV-MHSle2w>d z0-k^;;0bsFo`5Id33vjYz*9xQrYr0LDte}3Gj%qpPy&!t1Czz|NlS#`~Rnx82b$R6nX%?1zmxjgC4%f*oV+- z&hEz=?|m|??P1uoUjF|~A%-*1%zI#N*<3*#c85Ro7%+~Gmv zu(IQ{w~HsBk&+RA{q+QrZkQgDn_8C?$O20!iIl zDs>_(cNLa#HSoy}w#L?goX08|(M;Mg*|9W)YrZ4RCjn7?NQts#GD6Vpv9;-LwVIbX zO0q!dQMF)Vs&@hxsjD(FW0l1bjrmlN76T!tA|g%$mCi{G@diN$#~liXL!lKp(mbX{ z)<-ZpTtF@od+aQOH9F14NvnopTPPCQxkCDxrPF%*{z<1z;c=Hv!)6l!yKg#73SKc4i7N)z zqGO2?XIyKZ*t|#=qP>KYKsA!aYKg#@D;G=0xJZo{+>*2i9LLlblo>J1;&OqY08-9t zSjWV<$}}n0xf{fjImKvOO+`N~%1?8aW{mgr=Pt#4jnZ0bXpy6!eU&9>D70Uh1UCI* zIqeiX}w pmsf5SjpWY1>#`GkkJOrtJewywkYnZ53eHC)lhH1@%fPvX{RLiC&r1LR From 6732a07e8a5b9728de83cb002f8e7a1cd6960544 Mon Sep 17 00:00:00 2001 From: Valentin Chemiere Date: Thu, 19 Feb 2015 18:00:00 +0100 Subject: [PATCH 07/19] WIP --- external_file_location/.location.py.swp | Bin 0 -> 12288 bytes external_file_location/.task.py.swp | Bin 0 -> 12288 bytes external_file_location/backend.py | 60 +++++++------- .../backends/.filestore.py.swo | Bin 0 -> 12288 bytes external_file_location/backends/filestore.py | 67 +++++++++------- external_file_location/location.py | 51 +++++++++++- external_file_location/task.py | 74 ++++++++++++++++-- 7 files changed, 183 insertions(+), 69 deletions(-) create mode 100644 external_file_location/.location.py.swp create mode 100644 external_file_location/.task.py.swp create mode 100644 external_file_location/backends/.filestore.py.swo diff --git a/external_file_location/.location.py.swp b/external_file_location/.location.py.swp new file mode 100644 index 0000000000000000000000000000000000000000..5edb09b8df114ac745293cb315961e388119fa77 GIT binary patch literal 12288 zcmeHNO>88^6|Nk)q+Mf-PJSA_Efd1 zs%;O7!Wof60D-uWDBN-)N{$;ThvXC~Iph*?MSzfy$RVdl5#+1tw#PQ!EJ9pXbW7iK zch##`?|tv5+j0*Vp6PDTW5FW~$Nh|b`r$|YzufUW`(?t|ZZcBZjo;$%a`XF{xrN-% zy0~G@m0~G@m0~G@m0~G@m0~G_` zA_i=BhP@1b?c-H$^e}> zrin-+GWn35nD{!kH&=H0Osurqni5F~zG%W_m~vx;spGOq10#fNEFQD$bVXh=;8Elj zHVzjgu~#pZbz69$LfeszvB1o>aCfvsVQPZG05_#QgFzibg<^b#B{EC>X(l>aE4@f_ z6!RoCq+~j!y|IvPJtc-LWDbqFx#~i%)zg_B-c}f5gsm6T@TC}p-c2RNOty^F%TkMG@M@3p5w0&%I>VG@_cg_E<_+MB|nNFm2&K+7W)tmtN!F2NeVrGH>5NGeW9!GGU5BHM1}!bowV_gJ5uc}p z6q;MaMC}Sq$OBhhWonU%iPI?A_1O=Xny%y`RLYp*L?d^Uj)V@przbW8qXm&JPY{fg z%>eUdGgxw&4?dY!LpZdsxt2K1GT9Bg17iWtG9e=uvq2(b^`#nd>E&loYei(Qi)qu1 z20jUoFDNtJ&zh+W$6CqcB{6VWJ*&t6lL|v)H4kl{o9jpV8Vtqu*9w-M{{!UhAk)gK zP^EPv()iG4mo0ms5({G2x=pY;=DO?=sTzUw(`GWI?QEhetr&@eRcM>Az?pDVBP>ni zXsOmNW@`l#2S&|=dm=?8;`38yXK}SuJLW*S)YbHCnYa32k4;QLin*u_B4I+EWKJc- zYiqOHqTTqoxS2!;2VcC_SW!{y<)^UqiHZbbxZzHYyI89U4S9GS*+9v})6qT>jY2>n zS%l6G1PUe1k`{a#_{|<{j?KYgo+>#i`l%hGxr5@7=97S^>lj;+lWTdlpl zyd zE?wECHf^=H``y)@_4YPx?QCyd>2=_24^F~5@5nH>^lZ0(`N~e8+M8GD+4lA}6kdJ8 z)1)vfT%U2yAeL$3o=043F0FIYtLPu0(mjeJG&!9>F=SCAZs{1|#s$aAfrVnUGd$jM zBjP-5mK2nPG@TE12u@Xw?&ou}L0nEL<#=x)v_pe*E!@u>mu8H8hEu=KB@NelDz>mu z&?S}2$bYZhB!P$MY&kt#M#wt=n;?i;XDT+C;Qit&#*er;m4nzBVoF06oilivyWTk? z$5Fv$)Pldr{v@83L_EcFJH}w|&%YgVV-v8y(rNF|lSY*H!BFu0DSjOd*-;Uy$+9vuyOv!^Qeyq N-ENWm$G~oc{SOeWqp<)0 literal 0 HcmV?d00001 diff --git a/external_file_location/.task.py.swp b/external_file_location/.task.py.swp new file mode 100644 index 0000000000000000000000000000000000000000..5b9a195722af86955c6466f72bdac5ab8c8dc829 GIT binary patch literal 12288 zcmeHNO>87b6|T(!34!pFT!;|T+qG6RlO6YXH%o#{Z0*<{XD70~mS;?~GES?fyLzUx zJzbruYI`REBBZ@=KthUy;KBjHfn3N5DdG?$PMi>kBR3KOXOJKQ0bf=3{Ma6|IphNB zmcE(ks#mYx``&w1<=yHneWkrk&pJ04p3g9*-+Qln^w3w?cOu4)qP~!({Q-}aTfCR5 z2grTH3N}O_MB_-PKK*>PKK*>PK zK*>PKK*_-WJp($uz+Q)!4;7xiTzp?R^6Vj0&UuL3Tx1AGDa+as_Ucm;R~I0Rk*t^!X1e|(s+-vK`aBybb> zEb#8f7<(Id1NbIz2)qEafm^@=@Oj`O@b*U;`!;YN2!U@UD?fH#1bfNkJe;1cjT;IH^cnO^|k2krxXU<-H#xCq#oebAhv zQ_m>kaX>2TvDuDLNYh49LJ=-s;HMvZN^w=i&pNq^^Q5+XV#s_&R?_i;zznQS21KD% zp9pQHutK@EnTat#O{Xw#v_gKY9M{E5VUO!p(UdF3g%O@=>W)(0l2XWJTAki5X9kA`O{|G3Tq!^=dhq+F@;lA&Tp2-VI;!M(EuU37^Uqj85Gy8Y&4tUQ~J% zbJ$~Pt&^eRigRsgNml5JF>_*`^diNlUR5h6BSOPWY3f0h7souH<46yvVn@R=%dm

LB%jqsgvV46`iGV@%zyBuCA`q0fvJ4#2a$gB@-q~<(d2t zy_dPKYlpND2w!uhm+5M2YpcE6X?G^d7O2lPHMh2?I-#OA=7Ri8N?ZU!$O0Zl2@mYJ zb^F<}pBwgb&3?jSN@bL2S|dypCqIAZun=S7v!)8e`%}>FUX7SK(-_Mk_0H&=VrgZarZRLX$DNFsNH47hZxr>7&2^4@BTv`>GHNqyCu^p|4LAnUUtHq@EsBmQi_e=$NZu z=AGHDnU9L;FJ$}niti`6{gR7gG6IlhDDMuB76=jcw4 zgSpZOz%dA)Ga^%5&KnYoew56S9|$$qAS||$Bg_+jbBaOyYm=tg5TFCqh>Lf!sz%Bg ziXe-*IvCgbVLfL&A5uFRV3azp;^m=)9@L_p!la4`y$O{`3MwASbtV&nd6TATmvX!` zx52nhi{VpHTRkIQ~t{Nr&K#C-KmAhs!tLM!o^yi73oB*rkTt(CM z{J$(EJ*B1R>n`pj7ORSJ435IogS<)&DK40Hb0$xaGm}sl5Sl$%Ion<`x{k8KiZ7k9 z1lDLw6(#+Z%2sg`vXc8TE{Umv^?@gg@*7vh+|JyfX;+8Gd0>r=`LC7AjOoRM3pRF# z`Rw^i)s~;aQp9n8)0Rw~Dw`-t+eSrOEljd( zm7K5hq{{O(8o8-LBX?Tg`HUVg!n=ey^p%gP5=2+z-M!fTQk*`>+LO;_byE z>F0+OJ-~@6m7*`bp+nTRrxml>aC&Z_b-LVW93LM${UmdQ>^I`b=LwFWjaA2Lx+5}D z1CinQI`ED-ip}TPzDP%A-_rw5&+qJ0GYq*Dl<$)`7vX z%*N)a&9O2!W4pDteyh3DZLYPq+TA$cbTx0-vjyT7-4 zyVHWR9XJW=tRuZl(~Iryt=s!uYVO>j7n^%~Pb@4B`_<=8)$}=_T4Y zX;pM*s5Ix>04E0-K`~^p+nb>SL>@mpoVYa<N;T z)38QZj41Z-#{l7sp^%xm`>Ekl4KPn{WartGaIGUk4I4RaiYy82)>h+bnVCK=+;{sH4ho|?-+Xbe$>A{-O|~a8wX+)5~x~ zk<|I+PkNrx5!hdAHTUT`#fx{R$Gz;PcNaUFp~4r#RcpjLGEYYRfu`zujjk_UyFtxD th^jM5Jv*{iB$C~DZI<0ek+$}BjWr+c0~;5gT10&W*g=-bUIsQT>_33pg*^ZO literal 0 HcmV?d00001 diff --git a/external_file_location/backend.py b/external_file_location/backend.py index 7555f125..d01c4793 100755 --- a/external_file_location/backend.py +++ b/external_file_location/backend.py @@ -2,33 +2,37 @@ # -*- coding: utf-8 -*- -class AbstractConnection(object): +#class AbstractConnection(object): +# +# def __init__(self, host, user, pwd, port=None, allow_dir_creation=False): +# self.host = host +# self.user = user +# self.pwd = pwd +# self.port = port +# self.allow_dir_creation = allow_dir_creation +# self.connection = None +# +# def connect(self): +# return NotImplemented +# +# def close(self): +# return NotImplemented +# +# def get(self, filename, path=None): +# return NotImplemented +# +# def put(self, fileobject, filename, path=None): +# return NotImplemented +# +# def search(self, filename, path=None): +# return NotImplemented +# +# def move(self, filename, oldpath, newpath): +# return NotImplemented +# +# def rename(self, oldfilename, newfilename, path=None): +# return NotImplemented - def __init__(self, host, user, pwd, port=None, allow_dir_creation=False): - self.host = host - self.user = user - self.pwd = pwd - self.port = port - self.allow_dir_creation = allow_dir_creation - self.connection = None +class AbstractTask(): - def connect(self): - return NotImplemented - - def close(self): - return NotImplemented - - def get(self, filename, path=None): - return NotImplemented - - def put(self, fileobject, filename, path=None): - return NotImplemented - - def search(self, filename, path=None): - return NotImplemented - - def move(self, filename, oldpath, newpath): - return NotImplemented - - def rename(self, oldfilename, newfilename, path=None): - return NotImplemented + diff --git a/external_file_location/backends/.filestore.py.swo b/external_file_location/backends/.filestore.py.swo new file mode 100644 index 0000000000000000000000000000000000000000..65c54ea1df472a615683a515122d86b055c4d486 GIT binary patch literal 12288 zcmeI2&ub(_6vt~ftpdhc65 zhLGx?esbk8esA|KhH?XAFaQ42>L*8^VXt|NZA5EQQTsl{+AWS#y^r0C*03i-DSI2z zK2l#9-HVjx8{-GVkw}au#=+1})F6{{LV8{r}7LjC}>Z1n+^jz#Cu#WWa#);6ZRJm;%SZ&&L^i6f`| z!`SOUfMsw$Xo0K2Rp8g_82bWz4n73$fB|?4EP>--2K;#~V?Tno!6ooKcof_N?gqDj zDe&1eV;_J^;5l$JI0a6EKc*P_4txu~0iS~R!7D%m4_p8bfK%WkxCu;y%h2d+@G&60 z-UJ~Cz~f*R{01$*2k!z`oB~b(r+`!7a4EoxqD4ID2C>wly}))hvR$=ClWv-sw(Wp! zxh4Kcpo@dLHP|;a)q<;yd{BqI_E(xMN7>ukPA&iWtyQl{M(P+PpB|YcA~1ypp%`$L zCGCY}tXQ(~^dt#}N+!_@!ZVXp@H4z6#;H`M#e&$^nm=M|XE94p&|SoK^^H2AKBy>R zGL>+eOJ*DJ(YQRw0%O>BYhv#bD4ef2_Gq*-_7z*JBHkT)6=)Is(_=o6ijR4e@F15B z*D<{~(#BIlXR+bYz@|vcm8{0kR(<@9!x=7hmwE^z)u!B2-wd}m3uRljma0&l2B^9g zu@=i!t>)6YxYfJ3)#@VhkzdG3V{~$f%g(lkL<&}1GRk|UMWKbSf}!1z4b!@+vh8qW zX0YfGtxQEy<<#Nxo45Dpx5uf)X~!(1sgn6OQY&{zLtga^)PFOBa-TG0a84Sm@mSEs zVi8(hX*%;mnKVCuPjOPzy{grMDaR@wL~C6NXr30(Jf?leobQ_Cnt3_*O)h>M%S{hk z#0!+Lm!NCTdkvbpp`BlIq^3sG&9H8{88#VaTBw>OH8q(IrLGT8O*PXwuCr7qdlcEr z-49nsO}=Lrx}Fy$k@37Xxpz2?*Wu}Ah!3RG;d|A{w;j!VR`orR2o( zGnucVE|sq)P>> list(itersubclasses(int)) == [bool] + True + >>> class A(object): pass + >>> class B(A): pass + >>> class C(A): pass + >>> class D(B,C): pass + >>> class E(D): pass + >>> + >>> for cls in itersubclasses(A): + ... print(cls.__name__) + B + D + E + C + >>> # get ALL (new-style) classes currently defined + >>> [cls.__name__ for cls in itersubclasses(object)] #doctest: +ELLIPSIS + ['type', ...'tuple', ...] + """ + if not isinstance(cls, type): + raise TypeError('itersubclasses must be called with ' + 'new-style classes, not %.100r' % cls + ) + if _seen is None: + _seen = set() + try: + subs = cls.__subclasses__() + except TypeError: # fails only when cls is type + subs = cls.__subclasses__(cls) + for sub in subs: + if sub not in _seen: + _seen.add(sub) + yield sub + for sub in itersubclasses(sub, _seen): + yield sub diff --git a/external_file_location/task.py b/external_file_location/task.py index c632ec6c..51500cda 100644 --- a/external_file_location/task.py +++ b/external_file_location/task.py @@ -28,21 +28,79 @@ class Task(models.Model): _description = 'Description' name = fields.Char() - method = fields.Selection([ - ('ftp_import', 'FTP import'), - ('ftp_export', 'FTP export'), - ('sftp_import', 'SFTP import'), - ('sftp_export', 'SFTP export'), - ('filestore_import', 'Filestore import'), - ('filestore_export', 'Filestore export'), - ]) + # method = fields.Selection([ + # ('ftp_import', 'FTP import'), + # ('ftp_export', 'FTP export'), + # ('sftp_import', 'SFTP import'), + # ('sftp_export', 'SFTP export'), + # ('filestore_import', 'Filestore import'), + # ('filestore_export', 'Filestore export'), + # ]) + method = fields.Selection(selection='_get_method') filename = fields.Char() filepath = fields.Char() location_id = fields.Many2one('ir.location', string='Location') + def _get_method(self): + res = [] + for cls in itersubclasses(AbstractTask): + if cls._synchronize_type: + cls_info = (cls._key + cls._synchronize_type, cls._name + cls._synchronize_type) + res.append(cls_info) + return res + def run(self): connection_class = ... method_class = getattr(sys.modules[__name__], self.method) + config = { + 'host': self.location_id.address, + 'user': self.location_id.login, + 'pwd': self.location_id.password, + 'port': self.location_id.port, + 'allow_dir_creation': False, + 'filename': self.filename, + 'path': self.filepath + } conn = method_class(config) conn.run() + + +def itersubclasses(cls, _seen=None): + """ + itersubclasses(cls) + Generator over all subclasses of a given class, in depth first order. + >>> list(itersubclasses(int)) == [bool] + True + >>> class A(object): pass + >>> class B(A): pass + >>> class C(A): pass + >>> class D(B,C): pass + >>> class E(D): pass + >>> + >>> for cls in itersubclasses(A): + ... print(cls.__name__) + B + D + E + C + >>> # get ALL (new-style) classes currently defined + >>> [cls.__name__ for cls in itersubclasses(object)] #doctest: +ELLIPSIS + ['type', ...'tuple', ...] + """ + if not isinstance(cls, type): + raise TypeError('itersubclasses must be called with ' + 'new-style classes, not %.100r' % cls + ) + if _seen is None: + _seen = set() + try: + subs = cls.__subclasses__() + except TypeError: # fails only when cls is type + subs = cls.__subclasses__(cls) + for sub in subs: + if sub not in _seen: + _seen.add(sub) + yield sub + for sub in itersubclasses(sub, _seen): + yield sub From af6f43c6efdcf2510e05775d11096ebab83c03ae Mon Sep 17 00:00:00 2001 From: Valentin Chemiere Date: Fri, 20 Feb 2015 16:31:14 +0100 Subject: [PATCH 08/19] WIP reprise du fichier ftp du projet connector_flow https://github.com/initOS/connector-interfaces --- external_file_location/.location.py.swp | Bin 12288 -> 0 bytes external_file_location/.task.py.swp | Bin 12288 -> 0 bytes .../backends/.filestore.py.swo | Bin 12288 -> 0 bytes external_file_location/backends/ftp.py | 187 +++++++++++++----- external_file_location/backends/ftp_backup.py | 58 ++++++ 5 files changed, 200 insertions(+), 45 deletions(-) delete mode 100644 external_file_location/.location.py.swp delete mode 100644 external_file_location/.task.py.swp delete mode 100644 external_file_location/backends/.filestore.py.swo mode change 100755 => 100644 external_file_location/backends/ftp.py create mode 100755 external_file_location/backends/ftp_backup.py diff --git a/external_file_location/.location.py.swp b/external_file_location/.location.py.swp deleted file mode 100644 index 5edb09b8df114ac745293cb315961e388119fa77..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12288 zcmeHNO>88^6|Nk)q+Mf-PJSA_Efd1 zs%;O7!Wof60D-uWDBN-)N{$;ThvXC~Iph*?MSzfy$RVdl5#+1tw#PQ!EJ9pXbW7iK zch##`?|tv5+j0*Vp6PDTW5FW~$Nh|b`r$|YzufUW`(?t|ZZcBZjo;$%a`XF{xrN-% zy0~G@m0~G@m0~G@m0~G@m0~G_` zA_i=BhP@1b?c-H$^e}> zrin-+GWn35nD{!kH&=H0Osurqni5F~zG%W_m~vx;spGOq10#fNEFQD$bVXh=;8Elj zHVzjgu~#pZbz69$LfeszvB1o>aCfvsVQPZG05_#QgFzibg<^b#B{EC>X(l>aE4@f_ z6!RoCq+~j!y|IvPJtc-LWDbqFx#~i%)zg_B-c}f5gsm6T@TC}p-c2RNOty^F%TkMG@M@3p5w0&%I>VG@_cg_E<_+MB|nNFm2&K+7W)tmtN!F2NeVrGH>5NGeW9!GGU5BHM1}!bowV_gJ5uc}p z6q;MaMC}Sq$OBhhWonU%iPI?A_1O=Xny%y`RLYp*L?d^Uj)V@przbW8qXm&JPY{fg z%>eUdGgxw&4?dY!LpZdsxt2K1GT9Bg17iWtG9e=uvq2(b^`#nd>E&loYei(Qi)qu1 z20jUoFDNtJ&zh+W$6CqcB{6VWJ*&t6lL|v)H4kl{o9jpV8Vtqu*9w-M{{!UhAk)gK zP^EPv()iG4mo0ms5({G2x=pY;=DO?=sTzUw(`GWI?QEhetr&@eRcM>Az?pDVBP>ni zXsOmNW@`l#2S&|=dm=?8;`38yXK}SuJLW*S)YbHCnYa32k4;QLin*u_B4I+EWKJc- zYiqOHqTTqoxS2!;2VcC_SW!{y<)^UqiHZbbxZzHYyI89U4S9GS*+9v})6qT>jY2>n zS%l6G1PUe1k`{a#_{|<{j?KYgo+>#i`l%hGxr5@7=97S^>lj;+lWTdlpl zyd zE?wECHf^=H``y)@_4YPx?QCyd>2=_24^F~5@5nH>^lZ0(`N~e8+M8GD+4lA}6kdJ8 z)1)vfT%U2yAeL$3o=043F0FIYtLPu0(mjeJG&!9>F=SCAZs{1|#s$aAfrVnUGd$jM zBjP-5mK2nPG@TE12u@Xw?&ou}L0nEL<#=x)v_pe*E!@u>mu8H8hEu=KB@NelDz>mu z&?S}2$bYZhB!P$MY&kt#M#wt=n;?i;XDT+C;Qit&#*er;m4nzBVoF06oilivyWTk? z$5Fv$)Pldr{v@83L_EcFJH}w|&%YgVV-v8y(rNF|lSY*H!BFu0DSjOd*-;Uy$+9vuyOv!^Qeyq N-ENWm$G~oc{SOeWqp<)0 diff --git a/external_file_location/.task.py.swp b/external_file_location/.task.py.swp deleted file mode 100644 index 5b9a195722af86955c6466f72bdac5ab8c8dc829..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12288 zcmeHNO>87b6|T(!34!pFT!;|T+qG6RlO6YXH%o#{Z0*<{XD70~mS;?~GES?fyLzUx zJzbruYI`REBBZ@=KthUy;KBjHfn3N5DdG?$PMi>kBR3KOXOJKQ0bf=3{Ma6|IphNB zmcE(ks#mYx``&w1<=yHneWkrk&pJ04p3g9*-+Qln^w3w?cOu4)qP~!({Q-}aTfCR5 z2grTH3N}O_MB_-PKK*>PKK*>PK zK*>PKK*_-WJp($uz+Q)!4;7xiTzp?R^6Vj0&UuL3Tx1AGDa+as_Ucm;R~I0Rk*t^!X1e|(s+-vK`aBybb> zEb#8f7<(Id1NbIz2)qEafm^@=@Oj`O@b*U;`!;YN2!U@UD?fH#1bfNkJe;1cjT;IH^cnO^|k2krxXU<-H#xCq#oebAhv zQ_m>kaX>2TvDuDLNYh49LJ=-s;HMvZN^w=i&pNq^^Q5+XV#s_&R?_i;zznQS21KD% zp9pQHutK@EnTat#O{Xw#v_gKY9M{E5VUO!p(UdF3g%O@=>W)(0l2XWJTAki5X9kA`O{|G3Tq!^=dhq+F@;lA&Tp2-VI;!M(EuU37^Uqj85Gy8Y&4tUQ~J% zbJ$~Pt&^eRigRsgNml5JF>_*`^diNlUR5h6BSOPWY3f0h7souH<46yvVn@R=%dm

LB%jqsgvV46`iGV@%zyBuCA`q0fvJ4#2a$gB@-q~<(d2t zy_dPKYlpND2w!uhm+5M2YpcE6X?G^d7O2lPHMh2?I-#OA=7Ri8N?ZU!$O0Zl2@mYJ zb^F<}pBwgb&3?jSN@bL2S|dypCqIAZun=S7v!)8e`%}>FUX7SK(-_Mk_0H&=VrgZarZRLX$DNFsNH47hZxr>7&2^4@BTv`>GHNqyCu^p|4LAnUUtHq@EsBmQi_e=$NZu z=AGHDnU9L;FJ$}niti`6{gR7gG6IlhDDMuB76=jcw4 zgSpZOz%dA)Ga^%5&KnYoew56S9|$$qAS||$Bg_+jbBaOyYm=tg5TFCqh>Lf!sz%Bg ziXe-*IvCgbVLfL&A5uFRV3azp;^m=)9@L_p!la4`y$O{`3MwASbtV&nd6TATmvX!` zx52nhi{VpHTRkIQ~t{Nr&K#C-KmAhs!tLM!o^yi73oB*rkTt(CM z{J$(EJ*B1R>n`pj7ORSJ435IogS<)&DK40Hb0$xaGm}sl5Sl$%Ion<`x{k8KiZ7k9 z1lDLw6(#+Z%2sg`vXc8TE{Umv^?@gg@*7vh+|JyfX;+8Gd0>r=`LC7AjOoRM3pRF# z`Rw^i)s~;aQp9n8)0Rw~Dw`-t+eSrOEljd( zm7K5hq{{O(8o8-LBX?Tg`HUVg!n=ey^p%gP5=2+z-M!fTQk*`>+LO;_byE z>F0+OJ-~@6m7*`bp+nTRrxml>aC&Z_b-LVW93LM${UmdQ>^I`b=LwFWjaA2Lx+5}D z1CinQI`ED-ip}TPzDP%A-_rw5&+qJ0GYq*Dl<$)`7vX z%*N)a&9O2!W4pDteyh3DZLYPq+TA$cbTx0-vjyT7-4 zyVHWR9XJW=tRuZl(~Iryt=s!uYVO>j7n^%~Pb@4B`_<=8)$}=_T4Y zX;pM*s5Ix>04E0-K`~^p+nb>SL>@mpoVYa<N;T z)38QZj41Z-#{l7sp^%xm`>Ekl4KPn{WartGaIGUk4I4RaiYy82)>h+bnVCK=+;{sH4ho|?-+Xbe$>A{-O|~a8wX+)5~x~ zk<|I+PkNrx5!hdAHTUT`#fx{R$Gz;PcNaUFp~4r#RcpjLGEYYRfu`zujjk_UyFtxD th^jM5Jv*{iB$C~DZI<0ek+$}BjWr+c0~;5gT10&W*g=-bUIsQT>_33pg*^ZO diff --git a/external_file_location/backends/.filestore.py.swo b/external_file_location/backends/.filestore.py.swo deleted file mode 100644 index 65c54ea1df472a615683a515122d86b055c4d486..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 12288 zcmeI2&ub(_6vt~ftpdhc65 zhLGx?esbk8esA|KhH?XAFaQ42>L*8^VXt|NZA5EQQTsl{+AWS#y^r0C*03i-DSI2z zK2l#9-HVjx8{-GVkw}au#=+1})F6{{LV8{r}7LjC}>Z1n+^jz#Cu#WWa#);6ZRJm;%SZ&&L^i6f`| z!`SOUfMsw$Xo0K2Rp8g_82bWz4n73$fB|?4EP>--2K;#~V?Tno!6ooKcof_N?gqDj zDe&1eV;_J^;5l$JI0a6EKc*P_4txu~0iS~R!7D%m4_p8bfK%WkxCu;y%h2d+@G&60 z-UJ~Cz~f*R{01$*2k!z`oB~b(r+`!7a4EoxqD4ID2C>wly}))hvR$=ClWv-sw(Wp! zxh4Kcpo@dLHP|;a)q<;yd{BqI_E(xMN7>ukPA&iWtyQl{M(P+PpB|YcA~1ypp%`$L zCGCY}tXQ(~^dt#}N+!_@!ZVXp@H4z6#;H`M#e&$^nm=M|XE94p&|SoK^^H2AKBy>R zGL>+eOJ*DJ(YQRw0%O>BYhv#bD4ef2_Gq*-_7z*JBHkT)6=)Is(_=o6ijR4e@F15B z*D<{~(#BIlXR+bYz@|vcm8{0kR(<@9!x=7hmwE^z)u!B2-wd}m3uRljma0&l2B^9g zu@=i!t>)6YxYfJ3)#@VhkzdG3V{~$f%g(lkL<&}1GRk|UMWKbSf}!1z4b!@+vh8qW zX0YfGtxQEy<<#Nxo45Dpx5uf)X~!(1sgn6OQY&{zLtga^)PFOBa-TG0a84Sm@mSEs zVi8(hX*%;mnKVCuPjOPzy{grMDaR@wL~C6NXr30(Jf?leobQ_Cnt3_*O)h>M%S{hk z#0!+Lm!NCTdkvbpp`BlIq^3sG&9H8{88#VaTBw>OH8q(IrLGT8O*PXwuCr7qdlcEr z-49nsO}=Lrx}Fy$k@37Xxpz2?*Wu}Ah!3RG;d|A{w;j!VR`orR2o( zGnucVE|sq)P). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + +from openerp.osv import orm, fields +from ..AbstractTask import AbstractTask +from base64 import b64decode +import ftputil +import ftputil.session +import logging +_logger = logging.getLogger(__name__) + +class FtpTask(AbstractTask): + + def __init__(self, env, config): + self.env = env + self.host = config.get('host', '') + self.user = config.get('user', '') + self.pwd = config.get('pwd', '') + self.port = config.get('port', '') + self.allow_dir_creation = config.get('allow_dir_creation', '') + self.file_name = config.get('file_name', '') + self.path = config.get('path', '') + self.move_path = config.get('move_path', '') + self.delete_file = config.get('delete_file', False) -import sys -import os -from tempfile import TemporaryFile -from ftplib import FTP +class FtpImportTask(FtpTask): + """FTP Configuration options: + - host, user, password, port + - download_directory: directory on the FTP server where files are + downloaded from + - move_directory: If present, files will be moved to this directory + on the FTP server after download. + - delete_files: If true, files will be deleted on the FTP server + after download. + """ -class FTPConnection(object): + def _handle_new_source(self, ftp_conn, download_directory, file_name, + move_directory): + """open and read given file into create_file method, + move file if move_directory is given""" + with ftp_conn.open(self._source_name(download_directory, file_name), + "rb") as fileobj: + data = fileobj.read() + return self.create_file(file_name, data) - def __init__(self, host, user, pwd, port=None, allow_dir_creation=False): - super(FTPConnection, self).__init__(host, user, pwd, port, allow_dir_creation) - if not port: - self.port = 21 - self.protocol = "FTP" + def _source_name(self, download_directory, file_name): + """helper to get the full name""" + return download_directory + '/' + file_name - def connect(self): - self.connection = FTP(self.location, self.port) - self.connection.login(self.user, self.pwd) + def _move_file(self, ftp_conn, source, target): + """Moves a file on the FTP server""" + _logger.info('Moving file %s %s' % (source, target)) + ftp_conn.rename(source, target) - def close(self): - self.connection.close() + def _delete_file(self, ftp_conn, source): + """Deletes a file from the FTP server""" + _logger.info('Deleting file %s' % source) + ftp_conn.remove(source) - def get(self, filename, path=None): - if path: - filepath = "{}/{}".format(path, filename) - else: - filepath = filename - outfile = TemporaryFile('w+b') - self.connection.retrbinary('RETR ' + filepath, outfile.write) - return outfile + def run(self): + port_session_factory = ftputil.session.session_factory( + port=self.port) + with ftputil.FTPHost(self.host, self.user, + self.pwd, + session_factory=port_session_factory) as ftp_conn: - def put(self, fileobject, filename, path=None): - if path: - filepath = "{}/{}".format(path, filename) - else: - filepath = filename - self.connection.storbinary('STOR ' + filepath, fileobject) - return True + file_list = ftp_conn.listdir(path) + downloaded_files = [] + for ftpfile in file_list: + if ftp_conn.path.isfile(self._source_name(self.path, + self.file_name)): + file_id = self._handle_new_source(ftp_conn, + self.path, + self.file_name, + self.move_path) + self.run_successor_tasks(file_id=file_id, async=async) + downloaded_files.append(self.file_name) - def search(self, filename, path=None): - if path: - filepath = "{}/{}".format(path, filename) - else: - filepath = filename - connection_list_result = self.connection.nlst() - return [x for x in connection_list_result if filename in x] + # Move/delete files only after all files have been processed. + if self.delete_file: + for ftpfile in downloaded_files: + self._delete_file(ftp_conn, + self._source_name(self.path, + ftpfile)) + elif self.move_path: + if not ftp_conn.path.exists(self.move_path): + ftp_conn.mkdir(self.move_path) + for ftpfile in downloaded_files: + self._move_file( + ftp_conn, + self._source_name(self.path, ftpfile), + self._source_name(self.move_path, ftpfile)) - def move(self, filename, oldpath, newpath): - self.connection.rename( - os.path.join(oldpath, filename), - os.path.join(newpath, filename) - ) - def rename(self, oldfilename, newfilename, path=None): - return NotImplemented +class FtpExportTask(FtpTask): + """FTP Configuration options: + - host, user, password, port + - upload_directory: directory on the FTP server where files are + uploaded to + """ + + def _handle_existing_target(self, ftp_conn, target_name, filedata): + raise Exception("%s already exists" % target_name) + + def _handle_new_target(self, ftp_conn, target_name, filedata): + with ftp_conn.open(target_name, mode='wb') as fileobj: + fileobj.write(filedata) + _logger.info('wrote %s, size %d', target_name, len(filedata)) + + def _target_name(self, ftp_conn, upload_directory, filename): + return upload_directory + '/' + filename + + def _upload_file(self, config, filename, filedata): + ftp_config = config['ftp'] + upload_directory = ftp_config.get('upload_directory', '') + port_session_factory = ftputil.session.session_factory( + port=int(ftp_config.get('port', 21)) + ) + with ftputil.FTPHost(ftp_config['host'], ftp_config['user'], + ftp_config['password'], + session_factory=port_session_factory) as ftp_conn: + target_name = self._target_name(ftp_conn, + upload_directory, + filename) + if ftp_conn.path.isfile(target_name): + self._handle_existing_target(ftp_conn, target_name, filedata) + else: + self._handle_new_target(ftp_conn, target_name, filedata) + + def run(self, config=None, file_id=None, async=True): + #TODO change when object was made + f = self.env.get('impexp.file') \ + .browse(self.env.cr, self.env.uid, file_id) + self._upload_file(config, f.attachment_id.datas_fname, + b64decode(f.attachment_id.datas)) + diff --git a/external_file_location/backends/ftp_backup.py b/external_file_location/backends/ftp_backup.py new file mode 100755 index 00000000..7316b81f --- /dev/null +++ b/external_file_location/backends/ftp_backup.py @@ -0,0 +1,58 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + + +import sys +import os +from tempfile import TemporaryFile +from ftplib import FTP + +class FTPConnection(object): + + def __init__(self, host, user, pwd, port=None, allow_dir_creation=False): + super(FTPConnection, self).__init__(host, user, pwd, port, allow_dir_creation) + if not port: + self.port = 21 + self.protocol = "FTP" + + def connect(self): + self.connection = FTP(self.location, self.port) + self.connection.login(self.user, self.pwd) + + def close(self): + self.connection.close() + + def get(self, filename, path=None): + if path: + filepath = "{}/{}".format(path, filename) + else: + filepath = filename + outfile = TemporaryFile('w+b') + self.connection.retrbinary('RETR ' + filepath, outfile.write) + return outfile + + def put(self, fileobject, filename, path=None): + if path: + filepath = "{}/{}".format(path, filename) + else: + filepath = filename + self.connection.storbinary('STOR ' + filepath, fileobject) + return True + + def search(self, filename, path=None): + if path: + filepath = "{}/{}".format(path, filename) + else: + filepath = filename + connection_list_result = self.connection.nlst() + return [x for x in connection_list_result if filename in x] + + + def move(self, filename, oldpath, newpath): + self.connection.rename( + os.path.join(oldpath, filename), + os.path.join(newpath, filename) + ) + + def rename(self, oldfilename, newfilename, path=None): + return NotImplemented From 193465c4ea95c515f780a02a7e87a76494fef746 Mon Sep 17 00:00:00 2001 From: Valentin Chemiere Date: Tue, 24 Feb 2015 18:11:58 +0100 Subject: [PATCH 09/19] FTP import working with subclass discovery --- external_file_location/__init__.py | 7 +- external_file_location/abstract_task.py | 23 ++++++ external_file_location/attachment.py | 7 +- external_file_location/backend.py | 38 --------- external_file_location/helper.py | 60 ++++++++++++++ external_file_location/location.py | 44 +--------- external_file_location/location_view.xml | 6 +- external_file_location/task.py | 78 ++++-------------- external_file_location/task_view.xml | 2 + .../{backends => tasks}/.filestore.py.swp | Bin external_file_location/tasks/__init__.py | 23 ++++++ .../{backends => tasks}/filestore.py | 0 .../{backends => tasks}/ftp.py | 31 ++++--- .../{backends => tasks}/ftp_backup.py | 0 .../{backends => tasks}/sftp.py | 0 ir_attachment_metadata/__init__.py | 7 +- ir_attachment_metadata/__openerp__.py | 9 +- ir_attachment_metadata/attachment.py | 15 ++-- 18 files changed, 173 insertions(+), 177 deletions(-) create mode 100755 external_file_location/abstract_task.py delete mode 100755 external_file_location/backend.py create mode 100644 external_file_location/helper.py rename external_file_location/{backends => tasks}/.filestore.py.swp (100%) create mode 100644 external_file_location/tasks/__init__.py rename external_file_location/{backends => tasks}/filestore.py (100%) rename external_file_location/{backends => tasks}/ftp.py (90%) rename external_file_location/{backends => tasks}/ftp_backup.py (100%) rename external_file_location/{backends => tasks}/sftp.py (100%) diff --git a/external_file_location/__init__.py b/external_file_location/__init__.py index 046125d4..258d23ad 100644 --- a/external_file_location/__init__.py +++ b/external_file_location/__init__.py @@ -20,7 +20,8 @@ # ############################################################################### -import attachment -import location -import task +from . import attachment +from . import location +from . import task +from . import tasks diff --git a/external_file_location/abstract_task.py b/external_file_location/abstract_task.py new file mode 100755 index 00000000..322f0062 --- /dev/null +++ b/external_file_location/abstract_task.py @@ -0,0 +1,23 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +from base64 import b64encode + + +class AbstractTask(object): + + def create_file(self, filename, data): + ir_attachment_id = self.env['ir.attachment'].create( + { + 'name': filename, + 'datas': b64encode(data), + 'datas_fname': filename + } + ) + return ir_attachment_id + + # def load_file(self, file_id): + # f = self.session.browse('impexp.file', file_id) + # if not f.attachment_id.datas: + # return None + # return b64decode(f.attachment_id.datas) diff --git a/external_file_location/attachment.py b/external_file_location/attachment.py index fc1d479e..cb23810d 100644 --- a/external_file_location/attachment.py +++ b/external_file_location/attachment.py @@ -20,12 +20,10 @@ # ############################################################################### -from openerp import models, fields, api, _ -from openerp.exceptions import Warning -import hashlib +from openerp import models, fields -class AttachmentMetadata(models.Model): +class IrAttachment(models.Model): _inherit = 'ir.attachment' sync_date = fields.Datetime() @@ -34,4 +32,3 @@ class AttachmentMetadata(models.Model): ('failed', 'Failed'), ('done', 'Done'), ], readonly=True, required=True, default='pending') - diff --git a/external_file_location/backend.py b/external_file_location/backend.py deleted file mode 100755 index d01c4793..00000000 --- a/external_file_location/backend.py +++ /dev/null @@ -1,38 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- - - -#class AbstractConnection(object): -# -# def __init__(self, host, user, pwd, port=None, allow_dir_creation=False): -# self.host = host -# self.user = user -# self.pwd = pwd -# self.port = port -# self.allow_dir_creation = allow_dir_creation -# self.connection = None -# -# def connect(self): -# return NotImplemented -# -# def close(self): -# return NotImplemented -# -# def get(self, filename, path=None): -# return NotImplemented -# -# def put(self, fileobject, filename, path=None): -# return NotImplemented -# -# def search(self, filename, path=None): -# return NotImplemented -# -# def move(self, filename, oldpath, newpath): -# return NotImplemented -# -# def rename(self, oldfilename, newfilename, path=None): -# return NotImplemented - -class AbstractTask(): - - diff --git a/external_file_location/helper.py b/external_file_location/helper.py new file mode 100644 index 00000000..ad5fd0a8 --- /dev/null +++ b/external_file_location/helper.py @@ -0,0 +1,60 @@ +# -*- coding: utf-8 -*- +############################################################################## +# +# Copyright (C) 2014 initOS GmbH & Co. KG (). +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero 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 Affero General Public License for more details. +# +# You should have received a copy of the GNU Affero General Public License +# along with this program. If not, see . +# +############################################################################## + + +def itersubclasses(cls, _seen=None): + """ + itersubclasses(cls) + Generator over all subclasses of a given class, in depth first order. + >>> list(itersubclasses(int)) == [bool] + True + >>> class A(object): pass + >>> class B(A): pass + >>> class C(A): pass + >>> class D(B,C): pass + >>> class E(D): pass + >>> + >>> for cls in itersubclasses(A): + ... print(cls.__name__) + B + D + E + C + >>> # get ALL (new-style) classes currently defined + >>> [cls.__name__ for cls in itersubclasses(object)] #doctest: +ELLIPSIS + ['type', ...'tuple', ...] + """ + #import pdb; pdb.set_trace() + if not isinstance(cls, type): + raise TypeError('itersubclasses must be called with ' + 'new-style classes, not %.100r' % cls + ) + if _seen is None: + _seen = set() + try: + subs = cls.__subclasses__() + except TypeError: # fails only when cls is type + subs = cls.__subclasses__(cls) + for sub in subs: + if sub not in _seen: + _seen.add(sub) + yield sub + for sub in itersubclasses(sub, _seen): + yield sub diff --git a/external_file_location/location.py b/external_file_location/location.py index ea0a6499..74ef9eef 100644 --- a/external_file_location/location.py +++ b/external_file_location/location.py @@ -21,10 +21,11 @@ ############################################################################### from openerp import models, fields -from backend import AbstractTask +from abstract_task import AbstractTask +from helper import itersubclasses class Location(models.Model): - _name = 'ir.location' + _name = 'external.file.location' _description = 'Description' name = fields.Char(string='Name') @@ -43,42 +44,3 @@ class Location(models.Model): res.append(cls_info) return res - -def itersubclasses(cls, _seen=None): - """ - itersubclasses(cls) - Generator over all subclasses of a given class, in depth first order. - >>> list(itersubclasses(int)) == [bool] - True - >>> class A(object): pass - >>> class B(A): pass - >>> class C(A): pass - >>> class D(B,C): pass - >>> class E(D): pass - >>> - >>> for cls in itersubclasses(A): - ... print(cls.__name__) - B - D - E - C - >>> # get ALL (new-style) classes currently defined - >>> [cls.__name__ for cls in itersubclasses(object)] #doctest: +ELLIPSIS - ['type', ...'tuple', ...] - """ - if not isinstance(cls, type): - raise TypeError('itersubclasses must be called with ' - 'new-style classes, not %.100r' % cls - ) - if _seen is None: - _seen = set() - try: - subs = cls.__subclasses__() - except TypeError: # fails only when cls is type - subs = cls.__subclasses__(cls) - for sub in subs: - if sub not in _seen: - _seen.add(sub) - yield sub - for sub in itersubclasses(sub, _seen): - yield sub diff --git a/external_file_location/location_view.xml b/external_file_location/location_view.xml index c4d4ffcf..71adbf82 100644 --- a/external_file_location/location_view.xml +++ b/external_file_location/location_view.xml @@ -3,7 +3,7 @@ - ir.location + external.file.location

@@ -22,7 +22,7 @@ - ir.location + external.file.location @@ -38,7 +38,7 @@ Locations ir.actions.act_window - ir.location + external.file.location form diff --git a/external_file_location/task.py b/external_file_location/task.py index 51500cda..3cbf9994 100644 --- a/external_file_location/task.py +++ b/external_file_location/task.py @@ -2,8 +2,8 @@ ############################################################################### # # Module for OpenERP -# Copyright (C) 2014 Akretion (http://www.akretion.com). -# @author Sébastien BEAU +# Copyright (C) 2015 Akretion (http://www.akretion.com). +# @author Valentin CHEMIERE # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU Affero General Public License as @@ -20,87 +20,45 @@ # ############################################################################### -import sys -from openerp import models, fields +from openerp import models, fields, api +from helper import itersubclasses +from abstract_task import AbstractTask + class Task(models.Model): _name = 'ir.location.task' _description = 'Description' - + name = fields.Char() - # method = fields.Selection([ - # ('ftp_import', 'FTP import'), - # ('ftp_export', 'FTP export'), - # ('sftp_import', 'SFTP import'), - # ('sftp_export', 'SFTP export'), - # ('filestore_import', 'Filestore import'), - # ('filestore_export', 'Filestore export'), - # ]) method = fields.Selection(selection='_get_method') filename = fields.Char() filepath = fields.Char() - location_id = fields.Many2one('ir.location', string='Location') + location_id = fields.Many2one('external.file.location', string='Location') def _get_method(self): res = [] for cls in itersubclasses(AbstractTask): if cls._synchronize_type: - cls_info = (cls._key + cls._synchronize_type, cls._name + cls._synchronize_type) + cls_info = (cls._key + '_' + cls._synchronize_type, + cls._name + ' ' + cls._synchronize_type) res.append(cls_info) return res + @api.multi def run(self): - connection_class = ... - - method_class = getattr(sys.modules[__name__], self.method) + for cls in itersubclasses(AbstractTask): + if cls._synchronize_type and \ + cls._key + '_' + cls._synchronize_type == self.method: + method_class = cls config = { 'host': self.location_id.address, 'user': self.location_id.login, 'pwd': self.location_id.password, 'port': self.location_id.port, 'allow_dir_creation': False, - 'filename': self.filename, + 'file_name': self.filename, 'path': self.filepath } - conn = method_class(config) - conn.run() + conn = method_class(self.env, config) + file_id = conn.run() - -def itersubclasses(cls, _seen=None): - """ - itersubclasses(cls) - Generator over all subclasses of a given class, in depth first order. - >>> list(itersubclasses(int)) == [bool] - True - >>> class A(object): pass - >>> class B(A): pass - >>> class C(A): pass - >>> class D(B,C): pass - >>> class E(D): pass - >>> - >>> for cls in itersubclasses(A): - ... print(cls.__name__) - B - D - E - C - >>> # get ALL (new-style) classes currently defined - >>> [cls.__name__ for cls in itersubclasses(object)] #doctest: +ELLIPSIS - ['type', ...'tuple', ...] - """ - if not isinstance(cls, type): - raise TypeError('itersubclasses must be called with ' - 'new-style classes, not %.100r' % cls - ) - if _seen is None: - _seen = set() - try: - subs = cls.__subclasses__() - except TypeError: # fails only when cls is type - subs = cls.__subclasses__(cls) - for sub in subs: - if sub not in _seen: - _seen.add(sub) - yield sub - for sub in itersubclasses(sub, _seen): - yield sub diff --git a/external_file_location/task_view.xml b/external_file_location/task_view.xml index b8e928b1..ddbfded9 100644 --- a/external_file_location/task_view.xml +++ b/external_file_location/task_view.xml @@ -10,8 +10,10 @@ + +