Repository: dreamRs/datamods Branch: master Commit: 4178781ad4ac Files: 126 Total size: 502.5 KB Directory structure: gitextract_a77i5218/ ├── .Rbuildignore ├── .github/ │ ├── .gitignore │ └── workflows/ │ ├── R-CMD-check.yaml │ ├── pkgdown.yaml │ └── test-coverage.yaml ├── .gitignore ├── DESCRIPTION ├── LICENSE.md ├── NAMESPACE ├── NEWS.md ├── R/ │ ├── create-column.R │ ├── cut-variable.R │ ├── data.R │ ├── datagrid-infos.R │ ├── edit-data-utils.R │ ├── edit-data.R │ ├── filter-data.R │ ├── i18n.R │ ├── import-copypaste.R │ ├── import-file.R │ ├── import-globalenv.R │ ├── import-googlesheets.R │ ├── import-modal.R │ ├── import-url.R │ ├── onLoad.R │ ├── sample-data.R │ ├── select-group.R │ ├── show_data.R │ ├── update-factor.R │ ├── update-variables.R │ ├── utils-shiny.R │ ├── utils.R │ ├── validation.R │ └── zzz.R ├── README.Rmd ├── README.md ├── _pkgdown.yml ├── cran-comments.md ├── data/ │ └── demo_edit.rda ├── data-raw/ │ └── demo_edit.R ├── examples/ │ ├── create_column.R │ ├── cut_variable.R │ ├── edit_data-callback.R │ ├── edit_data-callback_add.R │ ├── edit_data-callback_delete.R │ ├── edit_data-callback_update-row_style.R │ ├── edit_data-callback_update.R │ ├── edit_data.R │ ├── filter_data-basic.R │ ├── filter_data.R │ ├── from-copypaste.R │ ├── from-file.R │ ├── from-globalenv.R │ ├── from-googlesheets.R │ ├── from-url.R │ ├── i18n.R │ ├── modal-validation.R │ ├── modal.R │ ├── sample.R │ ├── select-group-default.R │ ├── select-group-selected.R │ ├── select-group-subset.R │ ├── select-group-vars.R │ ├── show_data.R │ ├── update_factor.R │ ├── validation.R │ └── variables.R ├── inst/ │ ├── assets/ │ │ ├── css/ │ │ │ └── datamods.css │ │ └── js/ │ │ └── datamods.js │ ├── extdata/ │ │ ├── mtcars.csv │ │ ├── mtcars.json │ │ ├── mtcars_fr.csv │ │ ├── pop-fra-dep.txt │ │ ├── pop-fra-reg-dep.xls │ │ └── rules.yaml │ └── i18n/ │ ├── al.csv │ ├── cn.csv │ ├── de.csv │ ├── es.csv │ ├── extract_labels.R │ ├── fr.csv │ ├── it.csv │ ├── ja.csv │ ├── kr.csv │ ├── maj_csv.R │ ├── mk.csv │ ├── pl.csv │ ├── pt.csv │ └── tr.csv ├── man/ │ ├── create-column.Rd │ ├── cut-variable.Rd │ ├── demo_edit.Rd │ ├── edit-data.Rd │ ├── filter-data.Rd │ ├── get_data_packages.Rd │ ├── i18n.Rd │ ├── import-copypaste.Rd │ ├── import-file.Rd │ ├── import-globalenv.Rd │ ├── import-googlesheets.Rd │ ├── import-modal.Rd │ ├── import-url.Rd │ ├── list_pkg_data.Rd │ ├── module-sample.Rd │ ├── select-group.Rd │ ├── show_data.Rd │ ├── update-factor.Rd │ ├── update-variables.Rd │ └── validation.Rd ├── man-roxygen/ │ └── module-import.R ├── tests/ │ ├── testthat/ │ │ ├── test-edit-data.R │ │ ├── test-filter-data.R │ │ ├── test-i18n.R │ │ ├── test-import-copypaste.R │ │ ├── test-import-file.R │ │ ├── test-import-globalenv.R │ │ ├── test-import-googlesheets.R │ │ ├── test-import-modal.R │ │ ├── test-import-url.R │ │ ├── test-onLoad.R │ │ ├── test-update-variables.R │ │ └── test-validation.R │ └── testthat.R └── vignettes/ ├── .gitignore ├── datamods.Rmd └── i18n.Rmd ================================================ FILE CONTENTS ================================================ ================================================ FILE: .Rbuildignore ================================================ ^datamods\.Rproj$ ^\.Rproj\.user$ ^LICENSE\.md$ ^examples$ ^doc$ ^Meta$ ^\.github$ ^dev$ ^cran-comments\.md$ ^CRAN-RELEASE$ ^_pkgdown\.yml$ ^docs$ ^pkgdown$ ^man-roxygen$ ^README\.Rmd$ ^CRAN-SUBMISSION$ ^data-raw$ ================================================ FILE: .github/.gitignore ================================================ *.html ================================================ FILE: .github/workflows/R-CMD-check.yaml ================================================ # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help on: push: branches: [main, master] pull_request: branches: [main, master] name: R-CMD-check jobs: R-CMD-check: runs-on: ${{ matrix.config.os }} name: ${{ matrix.config.os }} (${{ matrix.config.r }}) strategy: fail-fast: false matrix: config: - {os: macos-latest, r: 'release'} - {os: windows-latest, r: 'release'} - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} - {os: ubuntu-latest, r: 'release'} - {os: ubuntu-latest, r: 'oldrel-1'} env: GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} R_KEEP_PKG_SOURCE: yes steps: - uses: actions/checkout@v3 - uses: r-lib/actions/setup-pandoc@v2 - uses: r-lib/actions/setup-r@v2 with: r-version: ${{ matrix.config.r }} http-user-agent: ${{ matrix.config.http-user-agent }} use-public-rspm: true - uses: r-lib/actions/setup-r-dependencies@v2 with: extra-packages: any::rcmdcheck needs: check - uses: r-lib/actions/check-r-package@v2 with: upload-snapshots: true ================================================ FILE: .github/workflows/pkgdown.yaml ================================================ # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help on: push: branches: [main, master] pull_request: branches: [main, master] release: types: [published] workflow_dispatch: name: pkgdown jobs: pkgdown: runs-on: ubuntu-latest # Only restrict concurrency for non-PR jobs concurrency: group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }} env: GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@v3 - uses: r-lib/actions/setup-pandoc@v2 - uses: r-lib/actions/setup-r@v2 with: use-public-rspm: true - uses: r-lib/actions/setup-r-dependencies@v2 with: extra-packages: any::pkgdown, local::. needs: website - name: Build site run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE) shell: Rscript {0} - name: Deploy to GitHub pages 🚀 if: github.event_name != 'pull_request' uses: JamesIves/github-pages-deploy-action@v4.4.1 with: clean: false branch: gh-pages folder: docs ================================================ FILE: .github/workflows/test-coverage.yaml ================================================ # Workflow derived from https://github.com/r-lib/actions/tree/v2/examples # Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help on: push: branches: [main, master] pull_request: name: test-coverage.yaml permissions: read-all jobs: test-coverage: runs-on: ubuntu-latest env: GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} steps: - uses: actions/checkout@v4 - uses: r-lib/actions/setup-r@v2 with: use-public-rspm: true - uses: r-lib/actions/setup-r-dependencies@v2 with: extra-packages: any::covr, any::xml2 needs: coverage - name: Test coverage run: | cov <- covr::package_coverage( quiet = FALSE, clean = FALSE, install_path = file.path(normalizePath(Sys.getenv("RUNNER_TEMP"), winslash = "/"), "package") ) covr::to_cobertura(cov) shell: Rscript {0} - uses: codecov/codecov-action@v4 with: # Fail if error if not on PR, or if on PR and token is given fail_ci_if_error: ${{ github.event_name != 'pull_request' || secrets.CODECOV_TOKEN }} file: ./cobertura.xml plugin: noop disable_search: true token: ${{ secrets.CODECOV_TOKEN }} - name: Show testthat output if: always() run: | ## -------------------------------------------------------------------- find '${{ runner.temp }}/package' -name 'testthat.Rout*' -exec cat '{}' \; || true shell: bash - name: Upload test results if: failure() uses: actions/upload-artifact@v4 with: name: coverage-test-failures path: ${{ runner.temp }}/package ================================================ FILE: .gitignore ================================================ .Rproj.user .Rhistory .RData .Ruserdata *.Rproj inst/doc doc Meta dev/ docs CRAN-SUBMISSION ================================================ FILE: DESCRIPTION ================================================ Package: datamods Title: Modules to Import and Manipulate Data in 'Shiny' Version: 1.5.3.9200 Authors@R: c(person(given = "Victor", family = "Perrier", role = c("aut", "cre", "cph"), email = "victor.perrier@dreamrs.fr"), person(given = "Fanny", family = "Meyer", role = "aut"), person(given = "Samra", family = "Goumri", role = "aut"), person(given = "Zauad Shahreer", family = "Abeer", role = "aut", email = "shahreyar.abeer@gmail.com"), person(given = "Eduard", family = "Szöcs", role = "ctb", email = "eduardszoecs@gmail.com") ) Description: 'Shiny' modules to import data into an application or 'addin' from various sources, and to manipulate them after that. License: GPL-3 URL: https://github.com/dreamRs/datamods, https://dreamrs.github.io/datamods/ BugReports: https://github.com/dreamRs/datamods/issues Encoding: UTF-8 Roxygen: list(markdown = TRUE) RoxygenNote: 7.3.2 Imports: bslib, classInt, data.table, htmltools, phosphoricons, reactable, readxl, rio, rlang, shiny (>= 1.5.0), shinyWidgets (>= 0.8.4), tibble, toastui (>= 0.3.3), tools, shinybusy, writexl Suggests: ggplot2, htmlwidgets, jsonlite, knitr, MASS, rmarkdown, testthat, validate VignetteBuilder: knitr Depends: R (>= 2.10) LazyData: true ================================================ FILE: LICENSE.md ================================================ GNU General Public License ========================== _Version 3, 29 June 2007_ _Copyright © 2007 Free Software Foundation, Inc. <>_ Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. ## Preamble The GNU General Public License is a free, copyleft license for software and other kinds of works. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, the GNU General Public License is intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free software for all its users. We, the Free Software Foundation, use the GNU General Public License for most of our software; it applies also to any other work released this way by its authors. You can apply it to your programs, too. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you have the freedom to distribute copies of free software (and charge for them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. To protect your rights, we need to prevent others from denying you these rights or asking you to surrender the rights. Therefore, you have certain responsibilities if you distribute copies of the software, or if you modify it: responsibilities to respect the freedom of others. For example, if you distribute copies of such a program, whether gratis or for a fee, you must pass on to the recipients the same freedoms that you received. You must make sure that they, too, receive or can get the source code. And you must show them these terms so they know their rights. Developers that use the GNU GPL protect your rights with two steps: **(1)** assert copyright on the software, and **(2)** offer you this License giving you legal permission to copy, distribute and/or modify it. For the developers' and authors' protection, the GPL clearly explains that there is no warranty for this free software. For both users' and authors' sake, the GPL requires that modified versions be marked as changed, so that their problems will not be attributed erroneously to authors of previous versions. Some devices are designed to deny users access to install or run modified versions of the software inside them, although the manufacturer can do so. This is fundamentally incompatible with the aim of protecting users' freedom to change the software. The systematic pattern of such abuse occurs in the area of products for individuals to use, which is precisely where it is most unacceptable. Therefore, we have designed this version of the GPL to prohibit the practice for those products. If such problems arise substantially in other domains, we stand ready to extend this provision to those domains in future versions of the GPL, as needed to protect the freedom of users. Finally, every program is threatened constantly by software patents. States should not allow patents to restrict development and use of software on general-purpose computers, but in those that do, we wish to avoid the special danger that patents applied to a free program could make it effectively proprietary. To prevent this, the GPL assures that patents cannot be used to render the program non-free. The precise terms and conditions for copying, distribution and modification follow. ## TERMS AND CONDITIONS ### 0. Definitions “This License” refers to version 3 of the GNU General Public License. “Copyright” also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. “The Program” refers to any copyrightable work licensed under this License. Each licensee is addressed as “you”. “Licensees” and “recipients” may be individuals or organizations. To “modify” a work means to copy from or adapt all or part of the work in a fashion requiring copyright permission, other than the making of an exact copy. The resulting work is called a “modified version” of the earlier work or a work “based on” the earlier work. A “covered work” means either the unmodified Program or a work based on the Program. To “propagate” a work means to do anything with it that, without permission, would make you directly or secondarily liable for infringement under applicable copyright law, except executing it on a computer or modifying a private copy. Propagation includes copying, distribution (with or without modification), making available to the public, and in some countries other activities as well. To “convey” a work means any kind of propagation that enables other parties to make or receive copies. Mere interaction with a user through a computer network, with no transfer of a copy, is not conveying. An interactive user interface displays “Appropriate Legal Notices” to the extent that it includes a convenient and prominently visible feature that **(1)** displays an appropriate copyright notice, and **(2)** tells the user that there is no warranty for the work (except to the extent that warranties are provided), that licensees may convey the work under this License, and how to view a copy of this License. If the interface presents a list of user commands or options, such as a menu, a prominent item in the list meets this criterion. ### 1. Source Code The “source code” for a work means the preferred form of the work for making modifications to it. “Object code” means any non-source form of a work. A “Standard Interface” means an interface that either is an official standard defined by a recognized standards body, or, in the case of interfaces specified for a particular programming language, one that is widely used among developers working in that language. The “System Libraries” of an executable work include anything, other than the work as a whole, that **(a)** is included in the normal form of packaging a Major Component, but which is not part of that Major Component, and **(b)** serves only to enable use of the work with that Major Component, or to implement a Standard Interface for which an implementation is available to the public in source code form. A “Major Component”, in this context, means a major essential component (kernel, window system, and so on) of the specific operating system (if any) on which the executable work runs, or a compiler used to produce the work, or an object code interpreter used to run it. The “Corresponding Source” for a work in object code form means all the source code needed to generate, install, and (for an executable work) run the object code and to modify the work, including scripts to control those activities. However, it does not include the work's System Libraries, or general-purpose tools or generally available free programs which are used unmodified in performing those activities but which are not part of the work. For example, Corresponding Source includes interface definition files associated with source files for the work, and the source code for shared libraries and dynamically linked subprograms that the work is specifically designed to require, such as by intimate data communication or control flow between those subprograms and other parts of the work. The Corresponding Source need not include anything that users can regenerate automatically from other parts of the Corresponding Source. The Corresponding Source for a work in source code form is that same work. ### 2. Basic Permissions All rights granted under this License are granted for the term of copyright on the Program, and are irrevocable provided the stated conditions are met. This License explicitly affirms your unlimited permission to run the unmodified Program. The output from running a covered work is covered by this License only if the output, given its content, constitutes a covered work. This License acknowledges your rights of fair use or other equivalent, as provided by copyright law. You may make, run and propagate covered works that you do not convey, without conditions so long as your license otherwise remains in force. You may convey covered works to others for the sole purpose of having them make modifications exclusively for you, or provide you with facilities for running those works, provided that you comply with the terms of this License in conveying all material for which you do not control copyright. Those thus making or running the covered works for you must do so exclusively on your behalf, under your direction and control, on terms that prohibit them from making any copies of your copyrighted material outside their relationship with you. Conveying under any other circumstances is permitted solely under the conditions stated below. Sublicensing is not allowed; section 10 makes it unnecessary. ### 3. Protecting Users' Legal Rights From Anti-Circumvention Law No covered work shall be deemed part of an effective technological measure under any applicable law fulfilling obligations under article 11 of the WIPO copyright treaty adopted on 20 December 1996, or similar laws prohibiting or restricting circumvention of such measures. When you convey a covered work, you waive any legal power to forbid circumvention of technological measures to the extent such circumvention is effected by exercising rights under this License with respect to the covered work, and you disclaim any intention to limit operation or modification of the work as a means of enforcing, against the work's users, your or third parties' legal rights to forbid circumvention of technological measures. ### 4. Conveying Verbatim Copies You may convey verbatim copies of the Program's source code as you receive it, in any medium, provided that you conspicuously and appropriately publish on each copy an appropriate copyright notice; keep intact all notices stating that this License and any non-permissive terms added in accord with section 7 apply to the code; keep intact all notices of the absence of any warranty; and give all recipients a copy of this License along with the Program. You may charge any price or no price for each copy that you convey, and you may offer support or warranty protection for a fee. ### 5. Conveying Modified Source Versions You may convey a work based on the Program, or the modifications to produce it from the Program, in the form of source code under the terms of section 4, provided that you also meet all of these conditions: * **a)** The work must carry prominent notices stating that you modified it, and giving a relevant date. * **b)** The work must carry prominent notices stating that it is released under this License and any conditions added under section 7. This requirement modifies the requirement in section 4 to “keep intact all notices”. * **c)** You must license the entire work, as a whole, under this License to anyone who comes into possession of a copy. This License will therefore apply, along with any applicable section 7 additional terms, to the whole of the work, and all its parts, regardless of how they are packaged. This License gives no permission to license the work in any other way, but it does not invalidate such permission if you have separately received it. * **d)** If the work has interactive user interfaces, each must display Appropriate Legal Notices; however, if the Program has interactive interfaces that do not display Appropriate Legal Notices, your work need not make them do so. A compilation of a covered work with other separate and independent works, which are not by their nature extensions of the covered work, and which are not combined with it such as to form a larger program, in or on a volume of a storage or distribution medium, is called an “aggregate” if the compilation and its resulting copyright are not used to limit the access or legal rights of the compilation's users beyond what the individual works permit. Inclusion of a covered work in an aggregate does not cause this License to apply to the other parts of the aggregate. ### 6. Conveying Non-Source Forms You may convey a covered work in object code form under the terms of sections 4 and 5, provided that you also convey the machine-readable Corresponding Source under the terms of this License, in one of these ways: * **a)** Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by the Corresponding Source fixed on a durable physical medium customarily used for software interchange. * **b)** Convey the object code in, or embodied in, a physical product (including a physical distribution medium), accompanied by a written offer, valid for at least three years and valid for as long as you offer spare parts or customer support for that product model, to give anyone who possesses the object code either **(1)** a copy of the Corresponding Source for all the software in the product that is covered by this License, on a durable physical medium customarily used for software interchange, for a price no more than your reasonable cost of physically performing this conveying of source, or **(2)** access to copy the Corresponding Source from a network server at no charge. * **c)** Convey individual copies of the object code with a copy of the written offer to provide the Corresponding Source. This alternative is allowed only occasionally and noncommercially, and only if you received the object code with such an offer, in accord with subsection 6b. * **d)** Convey the object code by offering access from a designated place (gratis or for a charge), and offer equivalent access to the Corresponding Source in the same way through the same place at no further charge. You need not require recipients to copy the Corresponding Source along with the object code. If the place to copy the object code is a network server, the Corresponding Source may be on a different server (operated by you or a third party) that supports equivalent copying facilities, provided you maintain clear directions next to the object code saying where to find the Corresponding Source. Regardless of what server hosts the Corresponding Source, you remain obligated to ensure that it is available for as long as needed to satisfy these requirements. * **e)** Convey the object code using peer-to-peer transmission, provided you inform other peers where the object code and Corresponding Source of the work are being offered to the general public at no charge under subsection 6d. A separable portion of the object code, whose source code is excluded from the Corresponding Source as a System Library, need not be included in conveying the object code work. A “User Product” is either **(1)** a “consumer product”, which means any tangible personal property which is normally used for personal, family, or household purposes, or **(2)** anything designed or sold for incorporation into a dwelling. In determining whether a product is a consumer product, doubtful cases shall be resolved in favor of coverage. For a particular product received by a particular user, “normally used” refers to a typical or common use of that class of product, regardless of the status of the particular user or of the way in which the particular user actually uses, or expects or is expected to use, the product. A product is a consumer product regardless of whether the product has substantial commercial, industrial or non-consumer uses, unless such uses represent the only significant mode of use of the product. “Installation Information” for a User Product means any methods, procedures, authorization keys, or other information required to install and execute modified versions of a covered work in that User Product from a modified version of its Corresponding Source. The information must suffice to ensure that the continued functioning of the modified object code is in no case prevented or interfered with solely because modification has been made. If you convey an object code work under this section in, or with, or specifically for use in, a User Product, and the conveying occurs as part of a transaction in which the right of possession and use of the User Product is transferred to the recipient in perpetuity or for a fixed term (regardless of how the transaction is characterized), the Corresponding Source conveyed under this section must be accompanied by the Installation Information. But this requirement does not apply if neither you nor any third party retains the ability to install modified object code on the User Product (for example, the work has been installed in ROM). The requirement to provide Installation Information does not include a requirement to continue to provide support service, warranty, or updates for a work that has been modified or installed by the recipient, or for the User Product in which it has been modified or installed. Access to a network may be denied when the modification itself materially and adversely affects the operation of the network or violates the rules and protocols for communication across the network. Corresponding Source conveyed, and Installation Information provided, in accord with this section must be in a format that is publicly documented (and with an implementation available to the public in source code form), and must require no special password or key for unpacking, reading or copying. ### 7. Additional Terms “Additional permissions” are terms that supplement the terms of this License by making exceptions from one or more of its conditions. Additional permissions that are applicable to the entire Program shall be treated as though they were included in this License, to the extent that they are valid under applicable law. If additional permissions apply only to part of the Program, that part may be used separately under those permissions, but the entire Program remains governed by this License without regard to the additional permissions. When you convey a copy of a covered work, you may at your option remove any additional permissions from that copy, or from any part of it. (Additional permissions may be written to require their own removal in certain cases when you modify the work.) You may place additional permissions on material, added by you to a covered work, for which you have or can give appropriate copyright permission. Notwithstanding any other provision of this License, for material you add to a covered work, you may (if authorized by the copyright holders of that material) supplement the terms of this License with terms: * **a)** Disclaiming warranty or limiting liability differently from the terms of sections 15 and 16 of this License; or * **b)** Requiring preservation of specified reasonable legal notices or author attributions in that material or in the Appropriate Legal Notices displayed by works containing it; or * **c)** Prohibiting misrepresentation of the origin of that material, or requiring that modified versions of such material be marked in reasonable ways as different from the original version; or * **d)** Limiting the use for publicity purposes of names of licensors or authors of the material; or * **e)** Declining to grant rights under trademark law for use of some trade names, trademarks, or service marks; or * **f)** Requiring indemnification of licensors and authors of that material by anyone who conveys the material (or modified versions of it) with contractual assumptions of liability to the recipient, for any liability that these contractual assumptions directly impose on those licensors and authors. All other non-permissive additional terms are considered “further restrictions” within the meaning of section 10. If the Program as you received it, or any part of it, contains a notice stating that it is governed by this License along with a term that is a further restriction, you may remove that term. If a license document contains a further restriction but permits relicensing or conveying under this License, you may add to a covered work material governed by the terms of that license document, provided that the further restriction does not survive such relicensing or conveying. If you add terms to a covered work in accord with this section, you must place, in the relevant source files, a statement of the additional terms that apply to those files, or a notice indicating where to find the applicable terms. Additional terms, permissive or non-permissive, may be stated in the form of a separately written license, or stated as exceptions; the above requirements apply either way. ### 8. Termination You may not propagate or modify a covered work except as expressly provided under this License. Any attempt otherwise to propagate or modify it is void, and will automatically terminate your rights under this License (including any patent licenses granted under the third paragraph of section 11). However, if you cease all violation of this License, then your license from a particular copyright holder is reinstated **(a)** provisionally, unless and until the copyright holder explicitly and finally terminates your license, and **(b)** permanently, if the copyright holder fails to notify you of the violation by some reasonable means prior to 60 days after the cessation. Moreover, your license from a particular copyright holder is reinstated permanently if the copyright holder notifies you of the violation by some reasonable means, this is the first time you have received notice of violation of this License (for any work) from that copyright holder, and you cure the violation prior to 30 days after your receipt of the notice. Termination of your rights under this section does not terminate the licenses of parties who have received copies or rights from you under this License. If your rights have been terminated and not permanently reinstated, you do not qualify to receive new licenses for the same material under section 10. ### 9. Acceptance Not Required for Having Copies You are not required to accept this License in order to receive or run a copy of the Program. Ancillary propagation of a covered work occurring solely as a consequence of using peer-to-peer transmission to receive a copy likewise does not require acceptance. However, nothing other than this License grants you permission to propagate or modify any covered work. These actions infringe copyright if you do not accept this License. Therefore, by modifying or propagating a covered work, you indicate your acceptance of this License to do so. ### 10. Automatic Licensing of Downstream Recipients Each time you convey a covered work, the recipient automatically receives a license from the original licensors, to run, modify and propagate that work, subject to this License. You are not responsible for enforcing compliance by third parties with this License. An “entity transaction” is a transaction transferring control of an organization, or substantially all assets of one, or subdividing an organization, or merging organizations. If propagation of a covered work results from an entity transaction, each party to that transaction who receives a copy of the work also receives whatever licenses to the work the party's predecessor in interest had or could give under the previous paragraph, plus a right to possession of the Corresponding Source of the work from the predecessor in interest, if the predecessor has it or can get it with reasonable efforts. You may not impose any further restrictions on the exercise of the rights granted or affirmed under this License. For example, you may not impose a license fee, royalty, or other charge for exercise of rights granted under this License, and you may not initiate litigation (including a cross-claim or counterclaim in a lawsuit) alleging that any patent claim is infringed by making, using, selling, offering for sale, or importing the Program or any portion of it. ### 11. Patents A “contributor” is a copyright holder who authorizes use under this License of the Program or a work on which the Program is based. The work thus licensed is called the contributor's “contributor version”. A contributor's “essential patent claims” are all patent claims owned or controlled by the contributor, whether already acquired or hereafter acquired, that would be infringed by some manner, permitted by this License, of making, using, or selling its contributor version, but do not include claims that would be infringed only as a consequence of further modification of the contributor version. For purposes of this definition, “control” includes the right to grant patent sublicenses in a manner consistent with the requirements of this License. Each contributor grants you a non-exclusive, worldwide, royalty-free patent license under the contributor's essential patent claims, to make, use, sell, offer for sale, import and otherwise run, modify and propagate the contents of its contributor version. In the following three paragraphs, a “patent license” is any express agreement or commitment, however denominated, not to enforce a patent (such as an express permission to practice a patent or covenant not to sue for patent infringement). To “grant” such a patent license to a party means to make such an agreement or commitment not to enforce a patent against the party. If you convey a covered work, knowingly relying on a patent license, and the Corresponding Source of the work is not available for anyone to copy, free of charge and under the terms of this License, through a publicly available network server or other readily accessible means, then you must either **(1)** cause the Corresponding Source to be so available, or **(2)** arrange to deprive yourself of the benefit of the patent license for this particular work, or **(3)** arrange, in a manner consistent with the requirements of this License, to extend the patent license to downstream recipients. “Knowingly relying” means you have actual knowledge that, but for the patent license, your conveying the covered work in a country, or your recipient's use of the covered work in a country, would infringe one or more identifiable patents in that country that you have reason to believe are valid. If, pursuant to or in connection with a single transaction or arrangement, you convey, or propagate by procuring conveyance of, a covered work, and grant a patent license to some of the parties receiving the covered work authorizing them to use, propagate, modify or convey a specific copy of the covered work, then the patent license you grant is automatically extended to all recipients of the covered work and works based on it. A patent license is “discriminatory” if it does not include within the scope of its coverage, prohibits the exercise of, or is conditioned on the non-exercise of one or more of the rights that are specifically granted under this License. You may not convey a covered work if you are a party to an arrangement with a third party that is in the business of distributing software, under which you make payment to the third party based on the extent of your activity of conveying the work, and under which the third party grants, to any of the parties who would receive the covered work from you, a discriminatory patent license **(a)** in connection with copies of the covered work conveyed by you (or copies made from those copies), or **(b)** primarily for and in connection with specific products or compilations that contain the covered work, unless you entered into that arrangement, or that patent license was granted, prior to 28 March 2007. Nothing in this License shall be construed as excluding or limiting any implied license or other defenses to infringement that may otherwise be available to you under applicable patent law. ### 12. No Surrender of Others' Freedom If conditions are imposed on you (whether by court order, agreement or otherwise) that contradict the conditions of this License, they do not excuse you from the conditions of this License. If you cannot convey a covered work so as to satisfy simultaneously your obligations under this License and any other pertinent obligations, then as a consequence you may not convey it at all. For example, if you agree to terms that obligate you to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. ### 13. Use with the GNU Affero General Public License Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed under version 3 of the GNU Affero General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, but the special requirements of the GNU Affero General Public License, section 13, concerning interaction through a network will apply to the combination as such. ### 14. Revised Versions of this License The Free Software Foundation may publish revised and/or new versions of the GNU General Public License from time to time. Such new versions will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the Program specifies that a certain numbered version of the GNU General Public License “or any later version” applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the GNU General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future versions of the GNU General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. Later license versions may give you additional or different permissions. However, no additional obligations are imposed on any author or copyright holder as a result of your choosing to follow a later version. ### 15. Disclaimer of Warranty THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM “AS IS” WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. ### 16. Limitation of Liability IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. ### 17. Interpretation of Sections 15 and 16 If the disclaimer of warranty and limitation of liability provided above cannot be given local legal effect according to their terms, reviewing courts shall apply local law that most closely approximates an absolute waiver of all civil liability in connection with the Program, unless a warranty or assumption of liability accompanies a copy of the Program in return for a fee. _END OF TERMS AND CONDITIONS_ ## How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively state the exclusion of warranty; and each file should have at least the “copyright” line and a pointer to where the full notice is found. datamods: Modules to Import and Manipulate Data in 'Shiny' Applications Copyright (C) 2020 Victor PERRIER This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. If the program does terminal interaction, make it output a short notice like this when it starts in an interactive mode: datamods Copyright (C) 2020 Victor PERRIER This program comes with ABSOLUTELY NO WARRANTY; for details type 'show w'. This is free software, and you are welcome to redistribute it under certain conditions; type 'show c' for details. The hypothetical commands `show w` and `show c` should show the appropriate parts of the General Public License. Of course, your program's commands might be different; for a GUI interface, you would use an “about box”. You should also get your employer (if you work as a programmer) or school, if any, to sign a “copyright disclaimer” for the program, if necessary. For more information on this, and how to apply and follow the GNU GPL, see <>. The GNU General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Lesser General Public License instead of this License. But first, please read <>. ================================================ FILE: NAMESPACE ================================================ # Generated by roxygen2: do not edit by hand export(create_column_server) export(create_column_ui) export(cut_variable_server) export(cut_variable_ui) export(edit_data_server) export(edit_data_ui) export(filter_data_server) export(filter_data_ui) export(get_data_packages) export(i18n) export(i18n_translations) export(import_copypaste_server) export(import_copypaste_ui) export(import_file_server) export(import_file_ui) export(import_globalenv_server) export(import_globalenv_ui) export(import_googlesheets_server) export(import_googlesheets_ui) export(import_modal) export(import_server) export(import_ui) export(import_url_server) export(import_url_ui) export(list_allowed_operations) export(list_pkg_data) export(modal_create_column) export(modal_cut_variable) export(modal_update_factor) export(sample_server) export(sample_ui) export(select_group_server) export(select_group_ui) export(set_i18n) export(show_data) export(update_factor_server) export(update_factor_ui) export(update_variables_server) export(update_variables_ui) export(validation_server) export(validation_ui) export(winbox_create_column) export(winbox_cut_variable) export(winbox_update_factor) importFrom(bslib,bs_current_theme) importFrom(bslib,bs_get_variables) importFrom(bslib,is_bs_theme) importFrom(classInt,classIntervals) importFrom(data.table,":=") importFrom(data.table,.N) importFrom(data.table,.SD) importFrom(data.table,as.data.table) importFrom(data.table,copy) importFrom(data.table,data.table) importFrom(data.table,fread) importFrom(data.table,setattr) importFrom(data.table,setnames) importFrom(data.table,setorderv) importFrom(data.table,uniqueN) importFrom(graphics,abline) importFrom(graphics,axis) importFrom(graphics,hist) importFrom(graphics,par) importFrom(graphics,plot.new) importFrom(graphics,plot.window) importFrom(htmltools,HTML) importFrom(htmltools,css) importFrom(htmltools,div) importFrom(htmltools,doRenderTags) importFrom(htmltools,htmlDependency) importFrom(htmltools,singleton) importFrom(htmltools,tagAppendAttributes) importFrom(htmltools,tagAppendChild) importFrom(htmltools,tagList) importFrom(htmltools,tags) importFrom(htmltools,validateCssUnit) importFrom(phosphoricons,ph) importFrom(reactable,colDef) importFrom(reactable,getReactableState) importFrom(reactable,reactable) importFrom(reactable,reactableOutput) importFrom(reactable,renderReactable) importFrom(reactable,updateReactable) importFrom(readxl,excel_sheets) importFrom(rio,import) importFrom(rlang,"%||%") importFrom(rlang,as_function) importFrom(rlang,as_label) importFrom(rlang,call2) importFrom(rlang,enquo) importFrom(rlang,eval_tidy) importFrom(rlang,exec) importFrom(rlang,expr) importFrom(rlang,fn_fmls_names) importFrom(rlang,is_double) importFrom(rlang,is_function) importFrom(rlang,is_list) importFrom(rlang,is_named) importFrom(rlang,is_null) importFrom(rlang,is_vector) importFrom(rlang,parse_expr) importFrom(rlang,set_names) importFrom(rlang,sym) importFrom(rlang,syms) importFrom(shiny,NS) importFrom(shiny,actionButton) importFrom(shiny,actionLink) importFrom(shiny,addResourcePath) importFrom(shiny,bindEvent) importFrom(shiny,checkboxInput) importFrom(shiny,column) importFrom(shiny,conditionalPanel) importFrom(shiny,dateRangeInput) importFrom(shiny,downloadButton) importFrom(shiny,downloadHandler) importFrom(shiny,eventReactive) importFrom(shiny,fileInput) importFrom(shiny,fluidRow) importFrom(shiny,getDefaultReactiveDomain) importFrom(shiny,hideTab) importFrom(shiny,icon) importFrom(shiny,insertUI) importFrom(shiny,is.reactive) importFrom(shiny,isTruthy) importFrom(shiny,isolate) importFrom(shiny,modalDialog) importFrom(shiny,moduleServer) importFrom(shiny,need) importFrom(shiny,numericInput) importFrom(shiny,observe) importFrom(shiny,observeEvent) importFrom(shiny,outputOptions) importFrom(shiny,plotOutput) importFrom(shiny,reactive) importFrom(shiny,reactiveVal) importFrom(shiny,reactiveValues) importFrom(shiny,reactiveValuesToList) importFrom(shiny,removeModal) importFrom(shiny,removeUI) importFrom(shiny,renderPlot) importFrom(shiny,renderUI) importFrom(shiny,req) importFrom(shiny,selectizeInput) importFrom(shiny,showModal) importFrom(shiny,singleton) importFrom(shiny,sliderInput) importFrom(shiny,tabPanel) importFrom(shiny,tabPanelBody) importFrom(shiny,tabsetPanel) importFrom(shiny,tagList) importFrom(shiny,tags) importFrom(shiny,textAreaInput) importFrom(shiny,textInput) importFrom(shiny,uiOutput) importFrom(shiny,updateActionButton) importFrom(shiny,updateSliderInput) importFrom(shiny,updateTabsetPanel) importFrom(shiny,updateTextAreaInput) importFrom(shiny,validate) importFrom(shinyWidgets,WinBox) importFrom(shinyWidgets,airDatepickerInput) importFrom(shinyWidgets,alert) importFrom(shinyWidgets,dropMenu) importFrom(shinyWidgets,html_dependency_pretty) importFrom(shinyWidgets,noUiSliderInput) importFrom(shinyWidgets,numericInputIcon) importFrom(shinyWidgets,numericRangeInput) importFrom(shinyWidgets,pickerInput) importFrom(shinyWidgets,pickerOptions) importFrom(shinyWidgets,prettyCheckbox) importFrom(shinyWidgets,prettySwitch) importFrom(shinyWidgets,radioGroupButtons) importFrom(shinyWidgets,show_alert) importFrom(shinyWidgets,textInputIcon) importFrom(shinyWidgets,updatePickerInput) importFrom(shinyWidgets,updateVirtualSelect) importFrom(shinyWidgets,virtualSelectInput) importFrom(shinyWidgets,wbControls) importFrom(shinyWidgets,wbOptions) importFrom(shinybusy,notify_failure) importFrom(shinybusy,notify_info) importFrom(shinybusy,notify_success) importFrom(shinybusy,notify_warning) importFrom(stats,setNames) importFrom(tibble,as_tibble) importFrom(toastui,datagrid) importFrom(toastui,datagridOutput) importFrom(toastui,datagridOutput2) importFrom(toastui,grid_colorbar) importFrom(toastui,grid_columns) importFrom(toastui,grid_editor) importFrom(toastui,grid_editor_opts) importFrom(toastui,grid_format) importFrom(toastui,grid_selection_row) importFrom(toastui,grid_style_column) importFrom(toastui,renderDatagrid) importFrom(toastui,renderDatagrid2) importFrom(tools,file_ext) importFrom(utils,data) importFrom(utils,getFromNamespace) importFrom(utils,hasName) importFrom(utils,head) importFrom(utils,modifyList) importFrom(utils,packageName) importFrom(utils,packageVersion) importFrom(utils,type.convert) importFrom(utils,write.csv) importFrom(writexl,write_xlsx) ================================================ FILE: NEWS.md ================================================ # datamods 1.5.4 * Rename CSS class `show` to `show-block` (used internally). * `select_group_server()`: added argument `selected_r =` to set selected values. # datamods 1.5.3 * `update_variables_server`: change of data update management after clicking on the validate button, fixed a problem when input data are the same as output data. # datamods 1.5.2 * `import_file_ui`: back to old parameters layout inside a dropdown button and new argument to switch to inline layout. * `edit_data_server`: allow to use reactive function for reactable_options. * cut variable module (`cut_variable_ui`/`cut_variable_server`): allow to select fixed breaks. # datamods 1.5.1 * New module `update_factor_ui()` / `update_factor_server()` to reorder levels of a factor. * i18n: Updated translations files with new labels, which are automatically translated, if you see incorrect translations, please open an issue or PR : https://github.com/dreamRs/datamods # datamods 1.5.0 * New module `create_column_ui()` / `create_column_server()` to add new column based on an expression to a `data.frame`. * New module `cut_variable_ui()` / `cut_variable_server()` to cut a numeric factor into several interval. # datamods 1.4.5 * `edit_data_server()` : fixed default variable labels when `var_labels = NULL`. # datamods 1.4.4 * `edit_data_server()` : added argument `add_default_values = list(...)` to specify default value for input widget when adding a new entry in the table. # datamods 1.4.3 * `edit_data_server()` : added the ability to specify callbacks functions to be executed before performing an action on the table (add, update or delete). * `edit_data_server()` : pass reactable option + selection to the table [#82](https://github.com/dreamRs/datamods/pull/82) * `edit-data` module : use factor levels and sort theme in edit input form for factors (sorting also applies for characters), thanks to [@Felixmil](https://github.com/Felixmil). * `import-file` module : allow to specify string used to identify `NA`, thanks to [@DrFabach](https://github.com/DrFabach). * `filter_data_server()` : argument `drop_ids` can now be set via option `datamods.filter.drop_ids` and can be a list like `list(p = 0.9, n = 50)` to specify threshold values to remove IDs columns. # datamods 1.4.2 * i18n: japanese translations added, thanks to [@nissinbo](https://github.com/nissinbo). * `select_group_server()` : output value now have an `inputs` attribute with a named list of selected inputs values. # datamods 1.4.1 * i18n: polish translations added, thanks to [@jakub-jedrusiak](https://github.com/jakub-jedrusiak). ### Bug fixes * Fixed displaying variable class in View tab (fix [#64](https://github.com/dreamRs/datamods/issues/64)). * `select_group_server()` : fix update inputs when `multiple = FALSE`. * `filter_data_server()` : sorting choices in select menus (select, picker and virtual) (fix [#66](https://github.com/dreamRs/datamods/issues/64))). * `filter_data_server()` : don't use `<`/`>` for empty field to not confuse to an HTML tag (fix [#65](https://github.com/dreamRs/datamods/issues/65))). # datamods 1.4.0 * New module : `edit_data_ui()` / `edit_data_server()` to interactively edit a `data.frame`, thanks to [@ggsamra](https://github.com/ggsamra). * New module : `sample_ui()` / `sample_server()` to take a sample from a table, thanks to [@ggsamra](https://github.com/ggsamra). # datamods 1.3.4 * i18n: korean translations added, thanks to [@ChangwooLim](https://github.com/ChangwooLim) (migrated from esquisse package). * `import_ui()` / `import_modal()`: added `file_extensions` argument passed to `import_file_ui()` (fix [#51](https://github.com/dreamRs/datamods/issues/51)). # datamods 1.3.3 * i18n: turkish translations added, thanks to [@sbalci](https://github.com/sbalci). * `filter_data` module now support getting and setting filter values, thanks to [@bellma-lilly](https://github.com/bellma-lilly). # datamods 1.3.2 * Fix bad link in NEWS. # datamods 1.3.1 * Fixed a bug in `update_variables` module. # datamods 1.3.0 * New module to read flat data from URLs `import_url_*()`. * Error messages displayed to the user are more informative on the actual error. * `filter_data_server()`: new argument `value_na` to set default value for NA's filters widgets. * `import_copypaste_ui()`: new argument `name_field` to show or not name field. * `import_copypaste_server()`: new argument `fread_args` to pass arguments to `data.table::fread`. * i18n: chinese translations added, thanks to [@xmusphlkg](https://github.com/xmusphlkg). * i18n: spanish translations added, thanks to [@dnldelarosa](https://github.com/dnldelarosa). * i18n: german translations added, thanks to [@SteEcker](https://github.com/SteEcker) and joerghenkebuero. # datamods 1.2.0 * Switch to [{phosphoricons}](https://github.com/dreamRs/phosphoricons) for icons. * `import_file_ui()` has a new argument `file_extensions` to select the files that the user can import. * `import_file_server()` has a new argument `read_fns` to define custom function(s) to read data. ### Translations * i18n: :macedonia: macedonian translations added, thanks to [@novica](https://github.com/novica). * i18n: :albania: albanian translations added, thanks to [@novica](https://github.com/novica). * i18n: :portugal: :brazil: brazilian portuguese translations added, thanks to [@gabrielteotonio](https://github.com/gabrielteotonio). # datamods 1.1.5 * `import_*_server()` added reset argument to clear the data. * `import_copypaste_server()` also return a `reactive` function "name" like the others. * New function `i18n()` to add internationalization in shiny apps. # datamods 1.1.4 * `filter_data_server`: convert data to `data.frame` (fix [esquisse #149](https://github.com/dreamRs/esquisse/issues/149)). * `filter_data_server`: fixed bug with timezone if POSIXct. * Import data from package: use `pkg::data` notation for data's name. # datamods 1.1.3 * Preserve class `sf` in output. # datamods 1.1.2 * Fixed a bug when retrieving data from package with parenthesis in name. * Fixed test on R-oldrel # datamods 1.1.0 * Added internationalization to translate labels used in modules, see corresponding vignette. # datamods 1.0.1 * First release on CRAN: Shiny modules import, to update, validate and filter data in interactive applications * Added a `NEWS.md` file to track changes to the package. ================================================ FILE: R/create-column.R ================================================ #' @title Create new column #' #' @description #' This module allow to enter an expression to create a new column in a `data.frame`. #' #' #' @param id Module's ID. #' #' @return A [shiny::reactive()] function returning the data. #' #' @note User can only use a subset of function: `r paste(list_allowed_operations(), collapse=", ")`. #' You can add more operations using the `allowed_operations` argument, for example if you want to allow to use package lubridate, you can do: #' ```r #' c(list_allowed_operations(), getNamespaceExports("lubridate")) #' ``` #' #' @export #' #' @importFrom htmltools tagList tags css #' @importFrom shiny NS textInput textAreaInput uiOutput actionButton #' @importFrom phosphoricons ph #' @importFrom shinyWidgets virtualSelectInput #' #' @name create-column #' #' @example examples/create_column.R create_column_ui <- function(id) { ns <- NS(id) tagList( html_dependency_datamods(), fluidRow( column( width = 6, textInput( inputId = ns("new_column"), label = i18n("New column name:"), value = "new_column1", width = "100%" ) ), column( width = 6, virtualSelectInput( inputId = ns("group_by"), label = i18n("Group calculation by:"), choices = NULL, multiple = TRUE, disableSelectAll = TRUE, hasOptionDescription = TRUE, width = "100%" ) ) ), textAreaInput( inputId = ns("expression"), label = i18n("Enter an expression to define new column:"), value = "", width = "100%", rows = 6 ), tags$i( class = "d-block", ph("info"), i18n("Click on a column name to add it to the expression:") ), uiOutput(outputId = ns("columns")), uiOutput(outputId = ns("feedback")), tags$div( style = css( display = "grid", gridTemplateColumns = "3fr 1fr", columnGap = "10px", margin = "10px 0" ), actionButton( inputId = ns("compute"), label = tagList( ph("gear"), i18n("Create column") ), class = "btn-outline-primary", width = "100%" ), actionButton( inputId = ns("remove"), label = tagList( ph("trash") ), class = "btn-outline-danger", width = "100%" ) ) ) } #' @param data_r A [shiny::reactive()] function returning a `data.frame`. #' @param allowed_operations A `list` of allowed operations, see below for details. #' #' @export #' #' @rdname create-column #' #' @importFrom shiny moduleServer reactiveValues observeEvent renderUI req #' updateTextAreaInput reactive bindEvent observe #' @importFrom shinyWidgets alert updateVirtualSelect create_column_server <- function(id, data_r = reactive(NULL), allowed_operations = list_allowed_operations()) { moduleServer( id, function(input, output, session) { ns <- session$ns info_alert <- alert( status = "info", ph("question"), i18n("Choose a name for the column to be created or modified,"), i18n("then enter an expression before clicking on the button above to validate or on "), ph("trash"), i18n("to delete it.") ) rv <- reactiveValues( data = NULL, feedback =info_alert ) observeEvent(input$hidden, rv$feedback <- info_alert) bindEvent(observe({ data <- data_r() updateVirtualSelect( inputId = "group_by", choices = make_choices_with_infos(data) ) }), data_r(), input$hidden) observeEvent(data_r(), rv$data <- data_r()) output$feedback <- renderUI(rv$feedback) output$columns <- renderUI({ data <- req(rv$data) col_type <- getFromNamespace("col_type", "esquisse") mapply( label = names(data), type = col_type(data), FUN = btn_column, MoreArgs = list(inputId = ns("add_column")), SIMPLIFY = FALSE ) }) observeEvent(input$add_column, { updateTextAreaInput( session = session, inputId = "expression", value = paste0(input$expression, input$add_column) ) }) observeEvent(input$new_column, { if (input$new_column == "") { rv$feedback <- alert( status = "warning", ph("warning"), i18n("New column name cannot be empty") ) } }) observeEvent(input$remove, { rv$data[[input$new_column]] <- NULL }) observeEvent(input$compute, { rv$feedback <- try_compute_column( expression = input$expression, name = input$new_column, rv = rv, allowed_operations = allowed_operations, by = input$group_by ) }) return(reactive(rv$data)) } ) } #' @export #' #' @rdname create-column # @importFrom methods getGroupMembers list_allowed_operations <- function() { c( "(", "c", # getGroupMembers("Arith"), c("+", "-", "*", "^", "%%", "%/%", "/"), # getGroupMembers("Compare"), c("==", ">", "<", "!=", "<=", ">="), # getGroupMembers("Logic"), c("&", "|"), # getGroupMembers("Math"), c("abs", "sign", "sqrt", "ceiling", "floor", "trunc", "cummax", "cummin", "cumprod", "cumsum", "exp", "expm1", "log", "log10", "log2", "log1p", "cos", "cosh", "sin", "sinh", "tan", "tanh", "acos", "acosh", "asin", "asinh", "atan", "atanh", "cospi", "sinpi", "tanpi", "gamma", "lgamma", "digamma", "trigamma"), # getGroupMembers("Math2"), c("round", "signif"), # getGroupMembers("Summary"), c("max", "min", "range", "prod", "sum", "any", "all"), "pmin", "pmax", "mean", "paste", "paste0", "substr", "nchar", "trimws", "gsub", "sub", "grepl", "ifelse", "length", "as.numeric", "as.character", "as.integer", "as.Date", "as.POSIXct", "as.factor", "factor" ) } #' @inheritParams shiny::modalDialog #' @export #' #' @importFrom shiny showModal modalDialog textInput #' @importFrom htmltools tagList #' #' @rdname create-column modal_create_column <- function(id, title = i18n("Create a new column"), easyClose = TRUE, size = "l", footer = NULL) { ns <- NS(id) showModal(modalDialog( title = tagList(title, button_close_modal()), create_column_ui(id), tags$div( style = "display: none;", textInput(inputId = ns("hidden"), label = NULL, value = genId()) ), easyClose = easyClose, size = size, footer = footer )) } #' @inheritParams shinyWidgets::WinBox #' @export #' #' @importFrom shinyWidgets WinBox wbOptions wbControls #' @importFrom htmltools tagList #' @rdname create-column winbox_create_column <- function(id, title = i18n("Create a new column"), options = shinyWidgets::wbOptions(), controls = shinyWidgets::wbControls()) { ns <- NS(id) WinBox( title = title, ui = tagList( create_column_ui(id), tags$div( style = "display: none;", textInput(inputId = ns("hidden"), label = NULL, value = genId()) ) ), options = modifyList( shinyWidgets::wbOptions(height = "550px", modal = TRUE), options ), controls = controls, auto_height = FALSE ) } #' @importFrom rlang parse_expr eval_tidy call2 set_names syms #' @importFrom data.table as.data.table := try_compute_column <- function(expression, name, rv, allowed_operations, by = NULL) { parsed <- try(parse(text = expression, keep.source = FALSE), silent = TRUE) if (inherits(parsed, "try-error")) { return(alert_error(attr(parsed, "condition")$message)) } funs <- unlist(c(extract_calls(parsed), lapply(parsed, extract_calls)), recursive = TRUE) if (!are_allowed_operations(funs, allowed_operations)) { return(alert_error(i18n("Some operations are not allowed"))) } if (!isTruthy(by)) { result <- try( eval_tidy(parse_expr(expression), data = rv$data), silent = TRUE ) } else { result <- try( { dt <- as.data.table(rv$data) new_col <- NULL dt[, new_col := eval_tidy(parse_expr(expression), data = .SD), by = by] dt$new_col }, silent = TRUE ) } if (inherits(result, "try-error")) { return(alert_error(attr(result, "condition")$message)) } adding_col <- try(rv$data[[name]] <- result, silent = TRUE) if (inherits(adding_col, "try-error")) { return(alert_error(attr(adding_col, "condition")$message)) } code <- if (!isTruthy(by)) { call2("mutate", !!!set_names(list(parse_expr(expression)), name)) } else { call2( "mutate", !!!set_names(list(parse_expr(expression)), name), !!!list(.by = expr(c(!!!syms(by)))) ) } attr(rv$data, "code") <- Reduce( f = function(x, y) expr(!!x %>% !!y), x = c(attr(rv$data, "code"), code) ) alert( status = "success", ph("check"), i18n("Column added!") ) } are_allowed_operations <- function(x, allowed_operations) { all( x %in% allowed_operations ) } extract_calls <- function(exp) { if (is.call(exp)) return(list( as.character(exp[[1L]]), lapply(exp[-1L], extract_calls) )) } alert_error <- function(text) { alert( status = "danger", ph("bug"), text ) } btn_column <- function(label, type, inputId) { icon <- switch ( type, discrete = "text-aa", time = "calendar", continuous = "hash", NULL ) tags$button( type = "button", class = paste0("btn btn-column-", type), style = css( "--bs-btn-padding-y" = ".25rem", "--bs-btn-padding-x" = ".5rem", "--bs-btn-font-size" = ".75rem", "margin-bottom" = "5px" ), if (!is.null(icon)) ph(icon, weight = "regular"), label, onclick = sprintf( "Shiny.setInputValue('%s', '%s', {priority: 'event'})", inputId, label ) ) } #' @importFrom data.table uniqueN #' @importFrom htmltools doRenderTags make_choices_with_infos <- function(data) { lapply( X = seq_along(data), FUN = function(i) { nm <- names(data)[i] values <- data[[nm]] icon <- if (inherits(values, "character")) { phosphoricons::ph("text-aa") } else if (inherits(values, "factor")) { phosphoricons::ph("list-bullets") } else if (inherits(values, c("numeric", "integer"))) { phosphoricons::ph("hash") } else if (inherits(values, c("Date"))) { phosphoricons::ph("calendar") } else if (inherits(values, c("POSIXt"))) { phosphoricons::ph("clock") } else { NULL } description <- if (is.atomic(values)) { paste(i18n("Unique values:"), data.table::uniqueN(values)) } else { "" } list( label = htmltools::doRenderTags(tagList( icon, nm )), value = nm, description = description ) } ) } ================================================ FILE: R/cut-variable.R ================================================ #' @title Module to Convert Numeric to Factor #' #' @description #' This module contain an interface to cut a numeric into several intervals. #' #' #' @param id Module ID. #' #' @return A [shiny::reactive()] function returning the data. #' @export #' #' @importFrom shiny NS fluidRow column numericInput checkboxInput checkboxInput plotOutput uiOutput #' @importFrom shinyWidgets virtualSelectInput #' @importFrom toastui datagridOutput2 #' #' @name cut-variable #' #' @example examples/cut_variable.R cut_variable_ui <- function(id) { ns <- NS(id) tagList( fluidRow( column( width = 3, virtualSelectInput( inputId = ns("variable"), label = i18n("Variable to cut:"), choices = NULL, width = "100%" ) ), column( width = 3, virtualSelectInput( inputId = ns("method"), label = i18n("Method:"), choices = c( "fixed", "sd", "equal", "pretty", "quantile", # "kmeans", # "hclust", # "bclust", # "fisher", # "jenks", "headtails", "maximum", "box" ), selected = "quantile", width = "100%" ) ), column( width = 3, numericInput( inputId = ns("n_breaks"), label = i18n("Number of breaks:"), value = 5, min = 2, max = 12, width = "100%" ) ), column( width = 3, checkboxInput( inputId = ns("right"), label = i18n("Close intervals on the right"), value = TRUE ), checkboxInput( inputId = ns("include_lowest"), label = i18n("Include lowest value"), value = FALSE ) ) ), conditionalPanel( condition = "input.method == 'fixed'", ns = ns, uiOutput(outputId = ns("slider_fixed")) ), plotOutput(outputId = ns("plot"), width = "100%", height = "270px"), datagridOutput2(outputId = ns("count")), actionButton( inputId = ns("create"), label = tagList(ph("scissors"), i18n("Create factor variable")), class = "btn-outline-primary float-end" ), tags$div(class = "clearfix") ) } #' @param data_r A [shiny::reactive()] function returning a `data.frame`. #' #' @export #' #' @importFrom shiny moduleServer observeEvent reactive req bindEvent renderPlot #' @importFrom shinyWidgets updateVirtualSelect noUiSliderInput #' @importFrom toastui renderDatagrid2 datagrid grid_colorbar #' @importFrom rlang %||% call2 set_names expr syms #' @importFrom classInt classIntervals #' #' @rdname cut-variable cut_variable_server <- function(id, data_r = reactive(NULL)) { moduleServer( id, function(input, output, session) { rv <- reactiveValues(data = NULL) bindEvent(observe({ data <- data_r() rv$data <- data vars_num <- vapply(data, is.numeric, logical(1)) vars_num <- names(vars_num)[vars_num] updateVirtualSelect( inputId = "variable", choices = vars_num, selected = if (isTruthy(input$variable)) input$variable else vars_num[1] ) }), data_r(), input$hidden) output$slider_fixed <- renderUI({ data <- req(data_r()) variable <- req(input$variable) req(hasName(data, variable)) noUiSliderInput( inputId = session$ns("fixed_brks"), label = i18n("Fixed breaks:"), min = floor(min(data[[variable]], na.rm = TRUE)), max = ceiling(max(data[[variable]], na.rm = TRUE)), value = classInt::classIntervals( var = data[[variable]], n = input$n_breaks, style = "quantile" )$brks, color = get_primary_color(), width = "100%" ) }) breaks_r <- reactive({ data <- req(data_r()) variable <- req(input$variable) req(hasName(data, variable)) req(input$n_breaks, input$method) if (input$method == "fixed") { req(input$fixed_brks) classInt::classIntervals( var = data[[variable]], n = input$n_breaks, style = "fixed", fixedBreaks = input$fixed_brks ) } else { classInt::classIntervals( var = data[[variable]], n = input$n_breaks, style = input$method ) } }) output$plot <- renderPlot({ data <- req(data_r()) variable <- req(input$variable) plot_histogram(data, variable, breaks = breaks_r()$brks, color = get_primary_color()) }) data_cutted_r <- reactive({ data <- req(data_r()) variable <- req(input$variable) data[[paste0(variable, "_cut")]] <- cut( x = data[[variable]], breaks = breaks_r()$brks, include.lowest = input$include_lowest, right = input$right ) code <- call2( "mutate", !!!set_names( list( expr(cut( !!!syms(list(x = variable)), !!!list(breaks = breaks_r()$brks, include.lowest = input$include_lowest, right = input$right) )) ), paste0(variable, "_cut") ) ) attr(data, "code") <- Reduce( f = function(x, y) expr(!!x %>% !!y), x = c(attr(data, "code"), code) ) data }) output$count <- renderDatagrid2({ data <- req(data_cutted_r()) variable <- req(input$variable) count_data <- as.data.frame( table( breaks = data[[paste0(variable, "_cut")]], useNA = "ifany" ), responseName = "count" ) gridTheme <- getOption("datagrid.theme") if (length(gridTheme) < 1) { apply_grid_theme() } on.exit(toastui::reset_grid_theme()) grid <- datagrid( data = count_data, colwidths = "guess", theme = "default", bodyHeight = "auto" ) grid <- toastui::grid_columns(grid, className = "font-monospace") grid_colorbar( grid, column = "count", label_outside = TRUE, label_width = "40px", bar_bg = get_primary_color(), from = c(0, max(count_data$count) + 1) ) }) data_returned_r <- observeEvent(input$create, { rv$data <- data_cutted_r() }) return(reactive(rv$data)) } ) } #' @inheritParams shiny::modalDialog #' @export #' #' @importFrom shiny showModal modalDialog textInput #' @importFrom htmltools tagList #' #' @rdname cut-variable modal_cut_variable <- function(id, title = i18n("Convert Numeric to Factor"), easyClose = TRUE, size = "l", footer = NULL) { ns <- NS(id) showModal(modalDialog( title = tagList(title, button_close_modal()), cut_variable_ui(id), tags$div( style = "display: none;", textInput(inputId = ns("hidden"), label = NULL, value = genId()) ), easyClose = easyClose, size = size, footer = footer )) } #' @inheritParams shinyWidgets::WinBox #' @export #' #' @importFrom shinyWidgets WinBox wbOptions wbControls #' @importFrom htmltools tagList #' @rdname cut-variable winbox_cut_variable <- function(id, title = i18n("Convert Numeric to Factor"), options = shinyWidgets::wbOptions(), controls = shinyWidgets::wbControls()) { ns <- NS(id) WinBox( title = title, ui = tagList( cut_variable_ui(id), tags$div( style = "display: none;", textInput(inputId = ns("hidden"), label = NULL, value = genId()) ) ), options = modifyList( shinyWidgets::wbOptions(height = "750px", modal = TRUE), options ), controls = controls, auto_height = FALSE ) } #' @importFrom graphics abline axis hist par plot.new plot.window plot_histogram <- function(data, column, bins = 30, breaks = NULL, color = "#112466") { x <- data[[column]] op <- par(mar = rep(1.5, 4)); on.exit(par(op)) plot.new() plot.window(xlim = range(pretty(x)), ylim = range(pretty(hist(x, breaks = bins, plot = FALSE)$counts))) abline(v = pretty(x), col = "#D8D8D8") abline(h = pretty(hist(x, breaks = bins, plot = FALSE)$counts), col = "#D8D8D8") hist(x, breaks = bins, xlim = range(pretty(x)), xaxs = "i", yaxs = "i", col = color, add = TRUE) axis(side = 1, at = pretty(x), pos = 0) axis(side = 2, at = pretty(hist(x, breaks = bins, plot = FALSE)$counts), pos = min(pretty(x))) abline(v = breaks, col = "#FFFFFF", lty = 1, lwd = 1.5) abline(v = breaks, col = "#2E2E2E", lty = 2, lwd = 1.5) } ================================================ FILE: R/data.R ================================================ #' Customer Credit Card Information #' #' A subset of fake customer credit card information inspired by the `{charlatan}` package. #' #' @format ## `demo_edit` #' A data frame with 20 rows and 6 columns: #' \describe{ #' \item{name}{Customer name} #' \item{job}{Customer job} #' \item{credit_card_provider}{Credit card provider} #' \item{credit_card_security_code}{Credit card security code} #' \item{date_obtained}{Date of obtaining the credit card} #' \item{contactless_card}{Contactless card} #' } #' @source "demo_edit" ================================================ FILE: R/datagrid-infos.R ================================================ #' @importFrom htmltools tagList tags css describe_col_char <- function(x, with_summary = TRUE) { tags$div( style = css(padding = "3px 0", fontSize = "x-small"), tags$div( style = css(fontStyle = "italic"), phosphoricons::ph("text-aa"), "character" ), if (with_summary) { tagList( tags$hr(style = css(margin = "3px 0")), tags$div( i18n("Unique:"), length(unique(x)) ), tags$div( i18n("Missing:"), sum(is.na(x)) ), tags$div( style = css(whiteSpace = "normal", wordBreak = "break-all"), i18n("Most Common:"), gsub( pattern = "'", replacement = "\u07F4", x = names(sort(table(x), decreasing = TRUE))[1] ) ), tags$div( "\u00A0" ) ) } ) } fmt_p <- function(val, tot) { paste0(round(val / tot * 100, 1), "%") } describe_col_factor <- function(x, with_summary = TRUE) { count <- sort(table(x, useNA = "always"), decreasing = TRUE) total <- sum(count) one <- count[!is.na(names(count))][1] two <- count[!is.na(names(count))][2] missing <- count[is.na(names(count))] tags$div( style = css(padding = "3px 0", fontSize = "x-small"), tags$div( style = css(fontStyle = "italic"), phosphoricons::ph("list-bullets"), "factor" ), if (with_summary) { tagList( tags$hr(style = css(margin = "3px 0")), tags$div( names(one), ":", fmt_p(one, total) ), tags$div( names(two), ":", fmt_p(two, total) ), tags$div( "Missing", ":", fmt_p(missing, total) ), tags$div( "\u00A0" ) ) } ) } describe_col_num <- function(x, with_summary = TRUE) { tags$div( style = css(padding = "3px 0", fontSize = "x-small"), tags$div( style = css(fontStyle = "italic"), phosphoricons::ph("hash"), "numeric" ), if (with_summary) { tagList( tags$hr(style = css(margin = "3px 0")), tags$div( i18n("Min:"), round(min(x, na.rm = TRUE), 2) ), tags$div( i18n("Mean:"), round(mean(x, na.rm = TRUE), 2) ), tags$div( i18n("Max:"), round(max(x, na.rm = TRUE), 2) ), tags$div( i18n("Missing:"), sum(is.na(x)) ) ) } ) } describe_col_date <- function(x, with_summary = TRUE) { tags$div( style = css(padding = "3px 0", fontSize = "x-small"), tags$div( style = css(fontStyle = "italic"), phosphoricons::ph("calendar"), "date" ), if (with_summary) { tagList( tags$hr(style = css(margin = "3px 0")), tags$div( i18n("Min:"), min(x, na.rm = TRUE) ), tags$div( i18n("Max:"), max(x, na.rm = TRUE) ), tags$div( i18n("Missing:"), sum(is.na(x)) ), tags$div( "\u00A0" ) ) } ) } describe_col_datetime <- function(x, with_summary = TRUE) { tags$div( style = css(padding = "3px 0", fontSize = "x-small"), tags$div( style = css(fontStyle = "italic"), phosphoricons::ph("clock"), "datetime" ), if (with_summary) { tagList( tags$hr(style = css(margin = "3px 0")), tags$div( i18n("Min:"), min(x, na.rm = TRUE) ), tags$div( i18n("Max:"), max(x, na.rm = TRUE) ), tags$div( i18n("Missing:"), sum(is.na(x)) ), tags$div( "\u00A0" ) ) } ) } describe_col_other <- function(x, with_summary = TRUE) { tags$div( style = css(padding = "3px 0", fontSize = "x-small"), tags$div( style = css(fontStyle = "italic"), # phosphoricons::ph("clock"), paste(class(x), collapse = ", ") ), if (with_summary) { tagList( tags$hr(style = css(margin = "3px 0")), tags$div( i18n("Unique:"), length(unique(x)) ), tags$div( i18n("Missing:"), sum(is.na(x)) ), tags$div( "\u00A0" ), tags$div( "\u00A0" ) ) } ) } #' @importFrom htmltools doRenderTags construct_col_summary <- function(data) { list( position = "top", height = 90, columnContent = lapply( X = setNames(names(data), names(data)), FUN = function(col) { values <- data[[col]] content <- if (inherits(values, "character")) { describe_col_char(values) } else if (inherits(values, "factor")) { describe_col_factor(values) } else if (inherits(values, c("numeric", "integer"))) { describe_col_num(values) } else if (inherits(values, c("Date"))) { describe_col_date(values) } else if (inherits(values, c("POSIXt"))) { describe_col_datetime(values) } else { describe_col_other(values) } list( template = toastui::JS( "function(value) {", sprintf( "return '%s';", gsub(replacement = "", pattern = "\n", x = doRenderTags(content)) ), "}" ) ) } ) ) } ================================================ FILE: R/edit-data-utils.R ================================================ #' @title Edit modal #' #' @description The `edit_modal` function generates a modal window with the variables to edit #' #' @return a modal window with the variables to edit #' #' @param default row on which to operate a modification or a deletion, otherwise empty list for an addition #' @param id_validate inputId of the actionButton() #' @param title title of the modalDialog() #' @param data `data.frame` to use #' @param colnames `data.frame` column names #' @param var_edit vector of `character` which allows to choose the editable columns #' @param var_mandatory vector of `character` which allows to choose obligatory fields to fill #' @param modal_size `character` which allows to choose the size of the modalDialog. One of "s" for small, "m" (the default) for medium, "l" for large, or "xl" for extra large. #' @param modal_easy_close `boolean` If TRUE, modalDialog can be dismissed by clicking outside the dialog box, or be pressing the Escape key. If FALSE (the default), modalDialog can't be dismissed in those ways; instead it must be dismissed by clicking on a modalButton(), or from a call to removeModal() on the server. #' @param session The `session` object passed to function given to shinyServer #' #' @importFrom shiny showModal modalDialog actionButton #' @importFrom phosphoricons ph #' @importFrom htmltools tagList tags css #' #' @noRd #' edit_modal <- function(default = list(), id_validate = "add_row", title = i18n("Add a row"), data, var_edit = NULL, var_mandatory = NULL, var_labels = colnames(data), modal_size = "m", modal_easy_close = FALSE, n_column = 1, session = getDefaultReactiveDomain()) { ns <- session$ns if (length(var_edit) > 0) { data <- data[, ..var_edit] } showModal(modalDialog( title = tagList( title, tags$button( phosphoricons::ph("x", title = i18n("Close"), height = "2em"), class = "btn btn-link", style = css(border = "0 none", position = "absolute", top = "5px", right = "5px"), `data-bs-dismiss` = "modal", `data-dismiss` = "modal", `aria-label` = "Close" ) ), footer = NULL, size = modal_size, easyClose = modal_easy_close, edit_input_form( default = default, data = data, var_mandatory = var_mandatory, var_labels = var_labels, n_column = n_column, session = session ), actionButton( inputId = ns(id_validate), label = tagList( ph("floppy-disk"), i18n("Save") ), class = "btn-outline-primary float-end" ) )) } #' @title Edit input form #' #' @description The `edit_input_form` function allows to correctly generate the variables to be edited in the modal window according to their respective class #' #' @param default default row on which to operate a modification or a deletion, otherwise empty list for an addition #' @param data `data.frame` to use #' @param colnames `data.frame` column names #' @param var_mandatory vector of `character` which allows to choose obligatory fields to fill #' @param position_var_edit position of editable columns in order to retrieve their name #' @param session The `session` object passed to function given to shinyServer #' #' @importFrom shiny numericInput textInput #' @importFrom shinyWidgets virtualSelectInput prettyCheckbox airDatepickerInput #' @importFrom htmltools tagList tags #' @importFrom rlang is_vector is_named #' #' @return different shiny widgets with edited columns according to their respective class #' @noRd #' edit_input_form <- function(default = list(), data, var_mandatory = NULL, var_labels = colnames(data), n_column = 1, session = getDefaultReactiveDomain()) { ns <- session$ns if (is_vector(var_labels) & !is_named(var_labels)) { var_labels <- setNames(var_labels, unlist(var_labels)) } widgets <- lapply( X = seq_len(ncol(data)), FUN = function(i) { variable_id <- colnames(data)[i] variable_label <- var_labels[which(names(var_labels) == variable_id)] if (length(variable_label) < 1) variable_label <- variable_id variable <- data[[i]] suffix <- if (isTRUE((inherits(variable, "logical")))) "" else " : " if (variable_id %in% var_mandatory) { label <- tagList( variable_label, tags$span(HTML("*"), class = "asterisk", style = "color: red;"), suffix ) } else { label <- paste0(variable_label, suffix) } if (isTRUE(inherits(variable, c("numeric", "integer")))) { opts <- getOption("datamods.edit.input.numeric", list()) opts <- modifyList( x = opts, val = list( inputId = ns(variable_id), label = label, value = default[[variable_id]] %||% NA_real_, width = "100%" ) ) do.call(numericInput, opts) } else if (inherits(variable, "factor")) { opts <- getOption("datamods.edit.input.factor", list()) opts <- modifyList( x = opts, val = list( inputId = ns(variable_id), label = label, choices = sort(unique(c(as.character(variable), levels(variable)))), selected = default[[variable_id]] %||% "", width = "100%", allowNewOption = TRUE, autoSelectFirstOption = FALSE, placeholder = i18n("Select"), zIndex = 999 ) ) do.call(virtualSelectInput, opts) } else if (inherits(variable, "character")) { opts <- getOption("datamods.edit.input.character", list()) opts <- modifyList( x = opts, val = list( inputId = ns(variable_id), label = label, value = default[[variable_id]] %||% "", width = "100%" ) ) do.call(textInput, opts) } else if (inherits(variable, "logical")) { opts <- getOption("datamods.edit.input.logical", list()) opts <- modifyList( x = opts, val = list( inputId = ns(variable_id), label = label, value = default[[variable_id]] %||% FALSE, icon = icon("check"), status = "primary", width = "100%" ) ) do.call(prettyCheckbox, opts) } else if (inherits(variable, "Date")) { opts <- getOption("datamods.edit.input.Date", list()) opts <- modifyList( x = opts, val = list( inputId = ns(variable_id), label = label, value = default[[variable_id]] %||% Sys.Date(), width = "100%" ) ) do.call(airDatepickerInput, opts) } else if (inherits(variable, c("POSIXct", "POSIXt"))) { opts <- getOption("datamods.edit.input.POSIXt", list()) opts <- modifyList( x = opts, val = list( inputId = ns(variable_id), label = label, value = default[[variable_id]] %||% Sys.time(), timepicker = TRUE, width = "100%" ) ) do.call(airDatepickerInput, opts) } else { return(NULL) } } ) fluidRow( lapply( X = split( x = seq_along(widgets), f = rep(seq_len(n_column), each = ceiling(length(widgets)/n_column))[seq_along(widgets)] ), FUN = function(i) { column( width = 12 / n_column, widgets[i] ) } ) ) } #' @title Table display #' #' @description The `table_display` function allows you to display the table in reactable format with columns to edit and delete rows #' #' @param data `data.frame` to use #' @param colnames `data.frame` column names #' @param reactable_options `list` allowing you to add reactable options #' #' @return the `data.frame` in reactable format #' @noRd #' #' @importFrom reactable reactable colDef #' @importFrom data.table copy setnames table_display <- function(data, colnames = NULL, reactable_options = NULL) { data <- copy(data) if (!is.null(colnames)) { setnames(data, old = seq_along(colnames), new = colnames) } cols <- reactable_options$columns %||% list() if (all(is.na(data$.datamods_edit_update))) { cols$.datamods_edit_update <- colDef(show = FALSE) } else { cols$.datamods_edit_update <- col_def_update() } if (all(is.na(data$.datamods_edit_delete))) { cols$.datamods_edit_delete <- colDef(show = FALSE) } else { cols$.datamods_edit_delete <- col_def_delete() } cols$.datamods_id <- colDef(show = FALSE) if (is.null(reactable_options)) reactable_options <- list() reactable_options <- reactable_options reactable_options$data <- data reactable_options$columns <- cols rlang::exec(reactable::reactable, !!!reactable_options) } #' @importFrom reactable updateReactable getReactableState #' @importFrom data.table copy setnames update_table <- function(data, colnames) { data <- copy(data) setnames(data, old = seq_along(colnames), new = colnames) page <- getReactableState(outputId = "table", name = "page") updateReactable("table", data = data, page = page) return(data) } format_edit_data <- function(data, colnames, internal_colnames = NULL) { data <- as.data.table(data) vars_datamods_edit <- intersect(c(".datamods_id", ".datamods_edit_update", ".datamods_edit_delete"), names(data)) data <- data[, -..vars_datamods_edit] if (is.null(internal_colnames)) internal_colnames <- seq_along(colnames) setnames(data, old = internal_colnames, new = colnames, skip_absent = TRUE) data[] } rename_edit <- function(data, var_labels) { for(i in seq_along(names(var_labels))) { names(data)[names(data) == names(var_labels)[i]] <- var_labels[[i]] } data } #' @importFrom rlang set_names is_null is_list is_named get_variables_labels <- function(labels, column_names, internal_names) { if (is_null(labels)) { labels <- column_names } else { if (!is_list(labels)) { stopifnot( "If `var_labels` is an unnamed vector, it must have same length as `colnames(data)`" = length(labels) == length(column_names) ) labels <- set_names(as.list(labels), column_names) } stopifnot( "`var_labels` must be a named list" = is_named(labels) ) names(labels) <- internal_names[match(names(labels), column_names)] labels <- modifyList( x = set_names(as.list(column_names), internal_names), val = labels ) } return(labels) } get_variables_default <- function(default, column_names, internal_names) { default <- default[column_names] idx <- match(names(default), column_names, nomatch = 0L) names(default)[idx > 0] <- internal_names[idx] default } #' @title The update column definition #' #' @return A column definition object that can be used to customize the update column in reactable(). #' @noRd #' #' @importFrom reactable colDef #' col_def_update <- function() { colDef( name = i18n("Update"), width = 82, sortable = FALSE, html = TRUE, filterable = FALSE ) } #' The update button #' #' @param inputId ID #' #' @return the update button #' @noRd #' #' @importFrom htmltools tags css doRenderTags #' @importFrom phosphoricons ph #' btn_update <- function(inputId) { function(value) { htmltools::doRenderTags( tags$button( class = "btn btn-outline-primary rounded-circle", style = htmltools::css( height = "40px", width = "40px", padding = 0 ), onClick = sprintf( "Shiny.setInputValue(\'%s\', %s, {priority: \'event\'})", inputId, value ), title = i18n("Click to edit"), ph("pencil-simple-line", height = "1.2em") ) ) } } #' @title The delete column definition #' #' @return A column definition object that can be used to customize the delete column in reactable(). #' @noRd #' @importFrom reactable colDef #' col_def_delete <- function() { reactable::colDef( name = i18n("Delete"), width = 96, sortable = FALSE, html = TRUE, filterable = FALSE ) } #' The delete button #' #' @param inputId ID #' #' @return the delete button #' @noRd #' #' @importFrom htmltools tags css doRenderTags #' @importFrom phosphoricons ph #' btn_delete <- function(inputId) { function(value) { htmltools::doRenderTags( tags$button( class = "btn btn-outline-danger rounded-circle", style = htmltools::css( height = "40px", width = "40px", padding = 0 ), onClick = sprintf( "Shiny.setInputValue(\'%s\', %s, {priority: \'event\'})", inputId, value ), title = i18n("Click to delete"), ph("x", height = "1.2em") ) ) } } #' Confirmation window #' #' @param inputId ID #' @param ... optional additional elements to add in the ui #' @param title title of the confirmation window #' #' @return a confirmation window #' @noRd #' #' @importFrom shiny modalDialog actionButton #' @importFrom htmltools tagList tags css #' @importFrom phosphoricons ph #' confirmation_window <- function(inputId, ..., title = NULL) { modalDialog( title = tagList( tags$button( phosphoricons::ph("x", title = i18n("Close"), height = "2em"), class = "btn btn-link", style = css(border = "0 none", position = "absolute", top = "5px", right = "5px"), `data-bs-dismiss` = "modal", `aria-label` = "Fermer" ), title ), ..., size = "m", footer = tagList( tags$button( i18n("Cancel"), class = "btn btn-outline-secondary", `data-bs-dismiss` = "modal" ), actionButton( inputId = paste0(inputId, "_no"), label = i18n("No"), class = "btn-outline-danger", `data-bs-dismiss` = "modal" ), actionButton( inputId = paste0(inputId, "_yes"), label = i18n("Yes"), class = "btn-outline-primary" ) ) ) } #' @importFrom shinybusy notify_failure notify_success notify_info notify_warning notification_failure <- function(title, text, use_notify = TRUE) { if (isTRUE(use_notify)) { shinybusy::notify_failure( title = title, text = text, position = "center-top", clickToClose = TRUE ) } } notification_warning <- function(title, text, use_notify = TRUE) { if (isTRUE(use_notify)) { shinybusy::notify_warning( title = title, text = text, position = "center-top", clickToClose = TRUE ) } } notification_success <- function(title, text, use_notify = TRUE) { if (isTRUE(use_notify)) { shinybusy::notify_success( title = title, text = text, position = "center-top", clickToClose = TRUE ) } } notification_info <- function(title, text, use_notify = TRUE) { if (isTRUE(use_notify)) { shinybusy::notify_info( title = title, text = text, position = "center-top", clickToClose = TRUE ) } } ================================================ FILE: R/edit-data.R ================================================ #' @title Shiny module to interactively edit a `data.frame` #' #' @description The module generates different options to edit a `data.frame`: adding, deleting and modifying rows, exporting data (csv and excel), choosing editable columns, choosing mandatory columns. #' This module returns the edited table with the user modifications. #' #' @param id Module ID #' #' @importFrom shiny uiOutput #' @importFrom htmltools tagList tags #' @importFrom reactable reactableOutput #' @importFrom utils getFromNamespace #' #' @export #' #' @name edit-data #' #' @example examples/edit_data.R edit_data_ui <- function(id) { ns <- NS(id) notify_dep <- getFromNamespace("html_dependency_notify", "shinybusy") tagList( notify_dep(), # Download data in Excel format -- uiOutput(outputId = ns("download_excel"), style = "display: inline;"), # Download data in csv format -- uiOutput(outputId = ns("download_csv"), style = "display: inline;"), # Add a row -- uiOutput(outputId = ns("add_button"), style = "display: inline;"), tags$div(class = "clearfix mb-2"), # Table -- reactableOutput(outputId = ns("table")) ) } #' @title Shiny module to interactively edit a `data.frame` #' #' @param id Module ID #' @param data_r data_r `reactive` function containing a `data.frame` to use in the module. #' @param add `boolean`, if `TRUE`, allows you to add a row in the table via a button at the top right. #' @param update `boolean`, if `TRUE`, allows you to modify a row of the table via a button located in the table on the row you want to edit. #' @param delete `boolean`, if `TRUE`, allows a row to be deleted from the table via a button in the table. #' @param download_csv if `TRUE`, allows to export the table in csv format via a download button. #' @param download_excel if `TRUE`, allows to export the table in excel format via a download button. #' @param file_name_export `character` that allows you to choose the export name of the downloaded file. #' @param var_edit vector of `character` which allows to choose the names of the editable columns. #' @param var_mandatory vector of `character` which allows to choose obligatory fields to fill. #' @param var_labels named list, where names are colnames and values are labels to be used in edit modal. #' @param add_default_values Default values to use for input control when adding new data, e.g. `list(my_var_text = "Default text to display")`. #' @param n_column Number of column in the edit modal window, must be a number that divide 12 since it use Bootstrap grid system with [shiny::column()]. #' @param return_class Class of returned data: `data.frame`, `data.table`, `tbl_df` (tibble) or `raw`. #' @param reactable_options Options passed to [reactable::reactable()]. #' @param modal_size `character` which allows to choose the size of the modalDialog. One of "s" for small, "m" (the default) for medium, "l" for large, or "xl" for extra large. #' @param modal_easy_close `boolean` If TRUE, modalDialog can be dismissed by clicking outside the dialog box, or be pressing the Escape key. If FALSE (the default), modalDialog can't be dismissed in those ways; instead it must be dismissed by clicking on a modalButton(), or from a call to removeModal() on the server. #' @param callback_add,callback_update,callback_delete Functions to be executed just before an action (add, update or delete) is performed on the data. #' Functions used must be like `function(data, row) {...}` where : #' * `data` will be the data in the table at the moment the function is called #' * `row` will contain either a new row of data (add), an updated row (update) or the row that will be deleted (delete). #' #' If the return value of a callback function is not truthy (see [shiny::isTruthy()]) then the action is cancelled. #' @param only_callback Only use callbacks, don't alter data within the module. #' @param use_notify Display information or not to user through [shinybusy::notify()]. #' #' #' #' @return the edited `data.frame` in reactable format with the user modifications #' #' @name edit-data #' #' @importFrom shiny moduleServer eventReactive reactiveValues is.reactive reactive renderUI actionButton observeEvent isTruthy showModal removeModal downloadButton downloadHandler #' @importFrom data.table copy as.data.table := copy setnames as.data.table setattr #' @importFrom reactable renderReactable reactableOutput getReactableState #' @importFrom phosphoricons ph #' @importFrom writexl write_xlsx #' @importFrom utils write.csv #' @importFrom htmltools tagList #' @importFrom rlang is_function is_list #' #' @export #' edit_data_server <- function(id, data_r = reactive(NULL), add = TRUE, update = TRUE, delete = TRUE, download_csv = TRUE, download_excel = TRUE, file_name_export = "data", var_edit = NULL, var_mandatory = NULL, var_labels = NULL, add_default_values = list(), n_column = 1, return_class = c("data.frame", "data.table", "tbl_df", "raw"), reactable_options = NULL, modal_size = c("m", "s", "l", "xl"), modal_easy_close = TRUE, callback_add = NULL, callback_update = NULL, callback_delete = NULL, only_callback = FALSE, use_notify = TRUE) { return_class <- match.arg(return_class) modal_size <- match.arg(modal_size) callback_default <- function(...) return(TRUE) if (!is_function(callback_add)) callback_add <- callback_default if (!is_function(callback_update)) callback_update <- callback_default if (!is_function(callback_delete)) callback_delete <- callback_default moduleServer( id, function(input, output, session) { ns <- session$ns data_rv <- reactiveValues(data = NULL, colnames = NULL, mandatory = NULL, edit = NULL) # Data data_r() with added columns ".datamods_edit_update" et ".datamods_edit_delete" --- data_init_r <- eventReactive(data_r(), { req(data_r()) data <- data_r() if (is.reactive(var_mandatory)) var_mandatory <- var_mandatory() if (is.reactive(var_labels)) var_labels <- var_labels() if (is.null(var_labels)) var_labels <- setNames(as.list(names(data)), names(data)) if (is.reactive(var_edit)) var_edit <- var_edit() if (is.null(var_edit)) var_edit <- names(data) data <- as.data.table(data) data_rv$colnames <- copy(colnames(data)) if (ncol(data) > 0) { setnames(data, paste0("col_", seq_along(data))) data_rv$internal_colnames <- copy(colnames(data)) } data_rv$mandatory <- data_rv$internal_colnames[data_rv$colnames %in% var_mandatory] data_rv$edit <- data_rv$internal_colnames[data_rv$colnames %in% var_edit] data_rv$labels <- get_variables_labels(var_labels, data_rv$colnames, data_rv$internal_colnames) data[, .datamods_id := seq_len(.N)] if (is.reactive(update)) { update <- update() } if (isTRUE(update)) { data[, .datamods_edit_update := as.character(seq_len(.N))] data[, .datamods_edit_update := list( lapply(.datamods_edit_update, btn_update(ns("update"))) )] } else { data[, .datamods_edit_update := NA] } if (is.reactive(delete)) { delete <- delete() } if (isTRUE(delete)) { data[, .datamods_edit_delete := as.character(seq_len(.N))] data[, .datamods_edit_delete := list( lapply(.datamods_edit_delete, btn_delete(ns("delete"))) )] } else { data[, .datamods_edit_delete := NA] } data_rv$data <- data return(data) }) # Table --- output$table <- renderReactable({ data <- req(data_init_r()) if (is.reactive(reactable_options)) reactable_options <- reactable_options() table_display( data = data, colnames = data_rv$colnames, reactable_options = reactable_options ) }) # Retrieve selected row(s) selected_r <- reactive({ getReactableState("table", "selected") }) # Add a row --- output$add_button <- renderUI({ if (is.reactive(add)) { add <- add() } if (isTRUE(add)) { actionButton( inputId = ns("add"), label = tagList(ph("plus"), i18n("Add a row")), class = "btn-outline-primary float-end" ) } }) observeEvent(input$add, { req(data_r()) edit_modal( default = get_variables_default( add_default_values, data_rv$colnames, data_rv$internal_colnames ), id_validate = "add_row", data = data_rv$data, var_edit = data_rv$edit, var_mandatory = data_rv$mandatory, var_labels = data_rv$labels, modal_size = modal_size, modal_easy_close = modal_easy_close, n_column = n_column ) }) observeEvent(input$add_row, { req(data_r()) data <- copy(data_rv$data) data <- as.data.table(data) for (var in data_rv$mandatory) { if (!isTruthy(input[[var]])) { notification_warning( title = i18n("Required field"), text = i18n("Please fill in the required fields"), use_notify = use_notify ) return(NULL) } } results_add <- try({ results_inputs <- lapply( X = setNames(data_rv$edit, data_rv$edit), FUN = function(x) { input[[x]] %||% NA } ) id <- max(data$.datamods_id) + 1 results_inputs[[".datamods_id"]] <- id results_inputs[[".datamods_edit_update"]] <- if (update) list(btn_update(ns("update"))(id)) else NA results_inputs[[".datamods_edit_delete"]] <- if (delete) list(btn_delete(ns("delete"))(id)) else NA new <- as.data.table(results_inputs) res_callback <- callback_add( format_edit_data(data, data_rv$colnames), format_edit_data(new, data_rv$colnames, data_rv$internal_colnames) ) if (isTruthy(res_callback) & !isTRUE(only_callback)) { data <- rbind(data, new[, .SD, .SDcols = !anyNA], use.names = TRUE, fill = TRUE) data_rv$data <- data removeModal() update_table(data, data_rv$colnames) } else { NULL } }) if (is.null(results_add)) { notification_warning( title = i18n("Warning"), text = i18n("The row wasn't added to the data"), use_notify = use_notify ) } else if (inherits(results_add, "try-error")) { notification_failure( title = i18n("Error"), text = i18n("Unable to add the row, contact the platform administrator"), use_notify = use_notify ) } else { notification_success( title = i18n("Registered"), text = i18n("Row has been saved"), use_notify = use_notify ) } }) # Update a row --- observeEvent(input$update, { data <- copy(data_rv$data) data <- as.data.table(data) row <- data[.datamods_id == input$update] edit_modal( default = row, title = i18n("Update row"), id_validate = "update_row", data = data, var_edit = data_rv$edit, var_mandatory = data_rv$mandatory, var_labels = data_rv$labels, modal_size = modal_size, modal_easy_close = modal_easy_close, n_column = n_column ) }) observeEvent(input$update_row, { req(data_r()) data <- copy(data_rv$data) data <- as.data.table(data) for (var in data_rv$mandatory) { if (!isTruthy(input[[var]])) { notification_failure( title = i18n("Required field"), text = i18n("Please fill in the required fields"), use_notify = use_notify ) return(NULL) } } results_update <- try({ id <- input$update data_updated <- copy(data) data_updated[.datamods_id == id, (data_rv$edit) := lapply(data_rv$edit, function(x) { input[[x]] %||% NA })] res_callback <- callback_update( format_edit_data(data, data_rv$colnames), format_edit_data( data_updated[.datamods_id == id], data_rv$colnames, data_rv$internal_colnames ) ) if (isTruthy(res_callback) & !isTRUE(only_callback)) { data_updated <- data_updated[order(.datamods_id)] data_rv$data <- copy(data_updated) removeModal() update_table(data_updated, data_rv$colnames) } else { NULL } }) if (is.null(results_update)) { notification_warning( title = i18n("Warning"), text = i18n("Data wasn't updated"), use_notify = use_notify ) } else if (inherits(results_update, "try-error")) { notification_failure( title = i18n("Error"), text = i18n("Unable to modify the item, contact the platform administrator"), use_notify = use_notify ) } else { notification_success( title = i18n("Registered"), text = i18n("Item has been modified"), use_notify = use_notify ) } }) # Delete a row --- observeEvent(input$delete, { req(data_r()) data <- copy(data_rv$data) data <- as.data.table(data) row <- data[.datamods_id == input$delete] removeModal() showModal(confirmation_window( inputId = ns("confirmation_delete_row"), title = i18n("Delete"), i18n("Do you want to delete the selected row ?") )) }) observeEvent(input$confirmation_delete_row_yes, { req(data_r()) data <- copy(data_rv$data) data <- as.data.table(data) results_delete <- try({ res_callback <- callback_delete( format_edit_data(data, data_rv$colnames), format_edit_data( data[.datamods_id == input$delete], data_rv$colnames, data_rv$internal_colnames ) ) if (isTruthy(res_callback) & !isTRUE(only_callback)) { data <- data[.datamods_id != input$delete] data <- data[order(.datamods_id)] data_rv$data <- data removeModal() update_table(data, data_rv$colnames) } else { NULL } }) if (is.null(results_delete)) { notification_warning( title = i18n("Warning"), text = i18n("Data wasn't deleted"), use_notify = use_notify ) } else if (inherits(results_delete, "try-error")) { notification_failure( title = i18n("Error"), text = i18n("Unable to delete the row, contact platform administrator"), use_notify = use_notify ) } else { notification_success( title = i18n("Registered"), text = i18n("The row has been deleted"), use_notify = use_notify ) } }) observeEvent(input$confirmation_delete_row_no, { notification_info( title = i18n("Information"), text = i18n("Row was not deleted"), use_notify = use_notify ) removeModal() }) # Download data in Excel format --- output$download_excel <- renderUI({ if (is.reactive(download_excel)) { download_excel <- download_excel() } if (isTRUE(download_excel)) { downloadButton( outputId = ns("export_excel"), label = tagList(ph("download"), "Excel"), icon = NULL, class = "btn-datamods-export" ) } }) output$export_excel <- downloadHandler( filename = function() { file_name <- file_name_export paste0(file_name, ".xlsx") }, content = function(file) { data <- format_edit_data(data_rv$data, data_rv$colnames) write_xlsx( x = list(data = data), path = file ) } ) # Download data in csv format --- output$download_csv <- renderUI({ if (is.reactive(download_csv)) { download_csv <- download_csv() } if (isTRUE(download_csv)) { downloadButton( outputId = ns("export_csv"), label = tagList(ph("download"), "CSV"), icon = NULL, class = "btn-datamods-export" ) } }) output$export_csv <- downloadHandler( filename = function() { file_name <- file_name_export paste0(file_name, ".csv") }, content = function(file) { data <- format_edit_data(data_rv$data, data_rv$colnames) write.csv( x = data, file = file ) } ) return( reactive({ req(data_rv$data) data <- format_edit_data(data_rv$data, data_rv$colnames) setattr(data, "selected", selected_r()) as_out(data, return_class) }) ) } ) } ================================================ FILE: R/filter-data.R ================================================ #' @title Shiny module to interactively filter a `data.frame` #' #' @description Module generate inputs to filter `data.frame` according column's type. #' Code to reproduce the filter is returned as an expression with filtered data. #' #' @param id Module id. See [shiny::moduleServer()]. #' @param show_nrow Show number of filtered rows and total. #' @param max_height Maximum height for filters panel, useful #' if you have many variables to filter and limited space. #' #' @return #' * UI: HTML tags that can be included in shiny's UI #' * Server: a `list` with four slots: #' + **filtered**: a `reactive` function returning the data filtered. #' + **code**: a `reactive` function returning the dplyr pipeline to filter data. #' + **expr**: a `reactive` function returning an expression to filter data. #' + **values**: a `reactive` function returning a named list of variables and filter values. #' #' @export #' #' @name filter-data #' #' @importFrom htmltools tagList singleton tags validateCssUnit #' @importFrom shiny NS uiOutput #' #' @example examples/filter_data.R filter_data_ui <- function(id, show_nrow = TRUE, max_height = NULL) { ns <- NS(id) max_height <- if (!is.null(max_height)) { paste0("overflow-y: auto; overflow-x: hidden; max-height:", validateCssUnit(max_height), ";") } tagList( singleton( tags$style( ".selectize-big .selectize-input {height: 72px; overflow-y: scroll;}" ) ), if (isTRUE(show_nrow)) { tags$span(i18n("Number of rows:"), uiOutput(outputId = ns("nrow"), inline = TRUE)) }, uiOutput(outputId = ns("placeholder_filters"), style = max_height) ) } #' @param data [shiny::reactive()] function returning a #' \code{data.frame} to filter. #' @param vars [shiny::reactive()] function returning a #' `character` vector of variables for which to add a filter. #' If a named `list`, names are used as labels. #' @param name [shiny::reactive()] function returning a #' `character` string representing `data` name, only used for code generated. #' @param defaults [shiny::reactive()] function returning a #' named `list` of variable:value pairs which will be used to set the filters. #' @param drop_ids Drop columns containing more than 90% of unique values, or than 50 distinct values. #' Use `FALSE` to disable or use `list(p = 0.9, n = 50)` to customize threshold values. #' @param widget_char Widget to use for `character` variables: [shinyWidgets::pickerInput()] #' or [shiny::selectInput()] (default). #' @param widget_num Widget to use for `numeric` variables: [shinyWidgets::numericRangeInput()] #' or [shiny::sliderInput()] (default). #' @param widget_date Widget to use for `date/time` variables: [shiny::dateRangeInput()] #' or [shiny::sliderInput()] (default). #' @param label_na Label for missing value widget. #' @param value_na Default value for all NA's filters. #' #' #' @rdname filter-data #' @export #' #' @importFrom rlang eval_tidy %||% #' @importFrom shiny observeEvent reactiveValues removeUI #' insertUI reactive req isolate reactive renderUI tags outputOptions filter_data_server <- function(id, data = reactive(NULL), vars = reactive(NULL), name = reactive("data"), defaults = reactive(NULL), drop_ids = getOption("datamods.filter.drop_ids", default = TRUE), widget_char = c("virtualSelect", "select", "picker"), widget_num = c("slider", "range"), widget_date = c("slider", "range"), label_na = "NA", value_na = TRUE) { widget_char <- match.arg(widget_char) widget_num <- match.arg(widget_num) widget_date <- match.arg(widget_date) moduleServer( id, function(input, output, session) { ns <- session$ns jns <- function(x) paste0("#", ns(x)) output$nrow <- renderUI({ tags$b(nrow(data_filtered()) , "/", nrow(data())) }) rv_filters <- reactiveValues(mapping = NULL, mapping_na = NULL) rv_code <- reactiveValues(expr = NULL, dplyr = NULL) output$placeholder_filters <- renderUI({ data <- data() req(data) vars <- vars() defaults <- defaults() filters <- create_filters( data = data, vars = vars, defaults = defaults, drop_ids = drop_ids, widget_char = widget_char, widget_num = widget_num, widget_date = widget_date, label_na = label_na, value_na = value_na ) rv_filters$mapping <- filters$filters_id rv_filters$mapping_na <- filters$filters_na_id return(filters$ui) }) filter_values <- reactive({ data <- data() req(data) req(all(names(rv_filters$mapping) %in% names(data))) filter_inputs <- lapply( X = rv_filters$mapping, FUN = function(x) { input[[x]] } ) filter_inputs }) data_filtered <- reactive({ data <- data() req(data) req(all(names(rv_filters$mapping) %in% names(data))) filter_inputs <- lapply( X = rv_filters$mapping, FUN = function(x) { # req(input[[x]]) input[[x]] } ) filter_nas <- lapply( X = rv_filters$mapping_na, FUN = function(x) { input[[x]] } ) filters <- make_expr_filter( filters = filter_inputs, filters_na = filter_nas, data = data, data_name = isolate(name()) %||% "data" ) rv_code$expr <- filters$expr rv_code$dplyr <- filters$expr_dplyr if (length(rv_code$expr) > 0) { result <- eval_tidy(expr = rv_code$expr, data = data) data[result, , drop = FALSE] } else { data } }) outputOptions(x = output, name = "placeholder_filters", suspendWhenHidden = FALSE) return(list( filtered = data_filtered, values = filter_values, code = reactive(rv_code$dplyr), expr = reactive(rv_code$expr) )) } ) } # Utils ------------------------------------------------------------------- #' @importFrom htmltools HTML tagList tags #' @importFrom shiny selectizeInput sliderInput dateRangeInput #' @importFrom stats setNames #' @importFrom shinyWidgets pickerInput pickerOptions numericRangeInput virtualSelectInput #' @importFrom rlang is_list create_filters <- function(data, vars = NULL, defaults = NULL, drop_ids = TRUE, widget_char = c("virtualSelect", "select", "picker"), widget_num = c("slider", "range"), widget_date = c("slider", "range"), label_na = "NA", value_na = TRUE, width = "100%", session = getDefaultReactiveDomain()) { data <- as.data.frame(data) if (ncol(data) < 1) return(NULL) widget_char <- match.arg(widget_char) widget_num <- match.arg(widget_num) widget_date <- match.arg(widget_date) ns <- session$ns data <- drop_na(data) if (isTRUE(drop_ids)) { data <- drop_id(data) } if (is_list(drop_ids)) { data <- drop_id(data, n = drop_ids$n, p = drop_ids$p) } data <- dropListColumns(data) if (is.null(vars)) { vars <- names(data) labels <- vars } else { if (rlang::is_named(vars)) { labels <- names(vars) vars <- unname(unlist(vars)) } else { labels <- vars } vars_display <- intersect(vars, names(data)) labels <- labels[vars %in% vars_display] vars <- vars_display } # filters_id <- paste0("filter_", sample.int(1e9, length(vars))) filters_id <- paste0("filter_", makeId(vars)) filters_id <- setNames(as.list(filters_id), vars) filters_na_id <- setNames(as.list(paste0("na_", filters_id)), vars) ui <- lapply( X = vars, FUN = function(variable) { var <- data[[variable]] any_na <- anyNA(var) var <- var[!is.na(var)] id <- filters_id[[variable]] label <- labels[variable == vars] tag_label <- tags$span( tags$label( label, class = "control-label", `for` = id ), HTML("  "), if (any_na) na_filter(id = ns(paste0("na_", id)), label = label_na, value = value_na) ) if (inherits(x = var, what = c("numeric", "integer"))) { params <- find_range_step(var) if(!is.null(defaults) && label %in% names(defaults)){ params$range = defaults[[label]] } if (identical(widget_num, "slider")) { tags$div( style = "position: relative;", tag_label, set_slider_attr(sliderInput( inputId = ns(id), min = params$min, max = params$max, value = params$range, step = params$step, label = NULL, width = width )) ) } else { tags$div( style = "position: relative;", tag_label, numericRangeInput( inputId = ns(id), value = params$range, label = NULL, width = width ) ) } } else if (inherits(x = var, what = c("Date", "POSIXct"))) { # browser() var <- pretty(var) range_var <- range(var) if(!is.null(defaults) && label %in% names(defaults)) { range_var <- defaults[[label]] } if (identical(widget_date, "slider")) { tags$div( style = "position: relative;", tag_label, set_slider_attr(sliderInput( inputId = ns(id), min = range_var[1], max = range_var[2], value = range_var, label = NULL, width = width, timezone = if (inherits(var, "POSIXct")) format(var[1], format = "%z") )) ) } else { range_var <- format(range_var, format = "%Y-%m-%d") tags$div( style = "position: relative;", tag_label, dateRangeInput( inputId = ns(id), min = range_var[1], max = range_var[2], start = range_var[1], end = range_var[2], label = NULL, width = width ) ) } } else { choices <- unique(as.character(sort(var))) if ("" %in% choices) choices <- append(choices, .empty_field_char) choices <- tryCatch(choices[trimws(choices) != ""], error = function(e) { Encoding(choices[!validEnc(choices)]) <- "unknown" choices }) selected <- choices if(!is.null(defaults) && label %in% names(defaults)){ selected = defaults[[label]] } if (identical(widget_char, "picker")) { tags$div( style = "position: relative;", tag_label, pickerInput( inputId = ns(id), choices = choices, selected = selected, label = NULL, multiple = TRUE, width = width, options = pickerOptions( container = "body", actionsBox = TRUE, selectedTextFormat = "count", liveSearch = TRUE ) ) ) } else if (identical(widget_char, "virtualSelect")) { tags$div( style = "position: relative;", tag_label, virtualSelectInput( inputId = ns(id), choices = choices, selected = selected, label = NULL, multiple = TRUE, width = width, showValueAsTags = TRUE, zIndex = 9999, dropboxWrapper = paste0("#", ns("placeholder_filters"), " .datamods-filters-container"), html = TRUE ) ) } else { tags$div( style = "position: relative;", class = if (length(choices) > 15) "selectize-big", tag_label, selectizeInput( inputId = ns(id), choices = choices, selected = selected, label = NULL, multiple = TRUE, width = width, options = list(plugins = list("remove_button")) ) ) } } } ) list( ui = tags$div( class = "datamods-filters-container", ui ), filters_id = filters_id, filters_na_id = filters_na_id ) } tagSetAttributes <- function(tag, ...) { tag$attribs[names(list(...))] <- NULL tag$attribs <- c(tag$attribs, list(...)) tag } set_slider_attr <- function(slider) { slider$children[[2]] <- tagSetAttributes( tag = slider$children[[2]], `data-force-edges` = "true", `data-grid-num` = "4" ) slider } #' @importFrom htmltools tags #' @importFrom shinyWidgets prettySwitch na_filter <- function(id, label = "NA", value = TRUE) { tags$span( style = "position: absolute; right: 0px; margin-right: -20px;", prettySwitch( inputId = id, label = label, value = value, slim = TRUE, status = "primary", inline = TRUE ) ) } #' @importFrom rlang expr sym make_expr_filter <- function(filters, filters_na, data, data_name) { expressions <- lapply( X = names(filters), FUN = function(var) { values <- filters[[var]] nas <- filters_na[[var]] data_values <- data[[var]] if (!is.null(values) & !match_class(values, data_values)) return(NULL) values_expr <- NULL if (inherits(x = values, what = c("numeric", "integer"))) { data_range <- find_range_step(data_values)$range if (!isTRUE(all.equal(values, data_range))) { if (isTRUE(nas)) { if (anyNA(data_values)) { values_expr <- expr(!!sym(var) >= !!values[1] & !!sym(var) <= !!values[2] | is.na(!!sym(var))) } else { values_expr <- expr(!!sym(var) >= !!values[1] & !!sym(var) <= !!values[2]) } } else { if (anyNA(data_values)) { values_expr <- expr(!!sym(var) >= !!values[1] & !!sym(var) <= !!values[2] & !is.na(!!sym(var))) } else { values_expr <- expr(!!sym(var) >= !!values[1] & !!sym(var) <= !!values[2]) } } } } else if (inherits(x = values, what = c("Date", "POSIXct"))) { date_fmt <- if (inherits(values, "Date")) { "%Y-%m-%d" } else { "%Y-%m-%d %H:%M:%S" } data_values <- pretty(data_values) data_range <- range(data_values, na.rm = TRUE) data_range <- format(data_range, format = date_fmt, tz = "UTC") if (!identical(format(values, format = date_fmt, tz = "UTC"), data_range)) { values <- format(values, format = date_fmt) if (isTRUE(nas)) { if (anyNA(data_values)) { values_expr <- expr(!!sym(var) >= !!values[1] & !!sym(var) <= !!values[2] | is.na(!!sym(var))) } else { values_expr <- expr(!!sym(var) >= !!values[1] & !!sym(var) <= !!values[2]) } } else { if (anyNA(data_values)) { values_expr <- expr(!!sym(var) >= !!values[1] & !!sym(var) <= !!values[2] & !is.na(!!sym(var))) } else { values_expr <- expr(!!sym(var) >= !!values[1] & !!sym(var) <= !!values[2]) } } } } else { data_values <- unique(as.character(data_values)) if (.empty_field_char %in% values) values[which(values == .empty_field_char)] <- "" if (!identical(sort(values), sort(data_values))) { if (length(values) == 0) { if (isTRUE(nas)) { values_expr <- expr(is.na(!!sym(var))) } else { values_expr <- expr(!(!!sym(var) %in% !!data_values[!is.na(data_values)]) & !is.na(!!sym(var))) } } else { if (length(values) <= length(data_values)/2) { if (isTRUE(nas)) { if (anyNA(data_values)) { values_expr <- expr(!!sym(var) %in% !!values | is.na(!!sym(var))) } else { values_expr <- expr(!!sym(var) %in% !!values) } } else { values_expr <- expr(!!sym(var) %in% !!values) } } else { if (isTRUE(nas)) { if (anyNA(data_values)) { values_expr <- expr(!(!!sym(var) %in% !!setdiff(data_values[!is.na(data_values)], values)) | is.na(!!sym(var))) } else { values_expr <- expr(!(!!sym(var) %in% !!setdiff(data_values[!is.na(data_values)], values))) } } else { if (anyNA(data_values)) { values_expr <- expr(!(!!sym(var) %in% !!setdiff(data_values[!is.na(data_values)], values)) & !is.na(!!sym(var))) } else { values_expr <- expr(!(!!sym(var) %in% !!setdiff(data_values[!is.na(data_values)], values))) } } } } } } if (is.null(values_expr) & !isTRUE(nas) & anyNA(data_values)) { expr(!is.na(!!sym(var))) } else { values_expr } } ) expressions <- lapply( X = expressions, FUN = function(expr) { res_expr <- try(eval_tidy(expr = expr, data = data), silent = TRUE) if (inherits(res_expr, "try-error")) return(expr) if (isTRUE(all(res_expr))) return(NULL) expr } ) expressions <- dropNullsOrEmpty(expressions) data_name <- as.character(data_name) if (grepl("::", data_name)) { data_name <- str2lang(data_name) } else { data_name <- sym(data_name) } expr_dplyr <- Reduce( f = function(x, y) expr(!!x %>% filter(!!y)), x = expressions, init = expr(!!data_name) ) expression <- Reduce( f = function(x, y) expr(!!x & !!y), x = expressions ) return(list( expr_dplyr = expr_dplyr, expr = expression )) } #' @importFrom rlang is_double drop_id <- function(data, p = 0.9, n = 50) { p <- as.numeric(p) if (!is_double(p, n = 1)) p <- 0.9 n <- as.numeric(n) if (!is_double(n, n = 1)) n <- 50 data[] <- lapply( X = data, FUN = function(x) { if (inherits(x, c("factor", "character"))) { values <- unique(as.character(x)) values <- tryCatch( values[trimws(values) != ""], error = function(e) { Encoding(values[!validEnc(values)]) <- "unknown" values } ) if (length(values) <= 1) return(NULL) if (isTRUE(length(values) >= (length(x) * p))) return(NULL) if (isTRUE(length(values) >= n)) return(NULL) } x } ) data } drop_na <- function(data) { data[] <- lapply( X = data, FUN = function(x) { if (all(is.na(x))) return(NULL) x } ) data } # borrowed from shiny hasDecimals <- function (value) { truncatedValue <- round(value) return(!identical(value, truncatedValue)) } find_range_step <- function(x) { max <- max(x, na.rm = TRUE) min <- min(x, na.rm = TRUE) range <- max - min if (range < 2 || hasDecimals(min) || hasDecimals(max)) { pretty_steps <- pretty(c(min, max), n = 100, high.u.bias = 1) n_steps <- length(pretty_steps) - 1 list( range = range(pretty_steps), min = min(pretty_steps), max = max(pretty_steps), step = signif(digits = 10, (max(pretty_steps) - min(pretty_steps))/n_steps) ) } else { list( range = range(x, na.rm = TRUE), min = min, max = max, step = 1 ) } } match_class <- function(x, y) { char <- c("character", "factor") num <- c("numeric", "integer") date <- c("Date", "POSIXt") if (inherits(x, num) & inherits(y, num)) return(TRUE) if (inherits(x, char) & inherits(y, char)) return(TRUE) if (inherits(x, date) & inherits(y, date)) return(TRUE) return(FALSE) } .empty_field_char <- "\u3008 \U0001d626\U0001d62e\U0001d631\U0001d635\U0001d63a \U0001d627\U0001d62a\U0001d626\U0001d62d\U0001d625 \u3009" ================================================ FILE: R/i18n.R ================================================ #' @title Internationalization #' #' @description Simple mechanism to translate labels in a Shiny application. #' #' @param x Label to translate. #' @param translations Either a `list` or a `data.frame` with translations. #' #' @return `i18n()` returns a `character`, `i18n_translations()` returns a `list` or a `data.frame`. #' @export #' #' @name i18n #' #' @importFrom data.table as.data.table := #' #' @example examples/i18n.R i18n <- function(x, translations = i18n_translations()) { if (is.null(translations)) return(x) if (is.list(translations) & !is.data.frame(translations)) { if (!x %in% names(translations)) { warning("i18n: translation for '", x, "' not found!", call. = FALSE) return(x) } return(translations[[x]]) } if (is.data.frame(translations)) { translations <- as.data.table(translations) translations[, label := as.character(label)] translations <- unique(translations, by = "label") translations[, translation := as.character(translation)] if (!x %in% translations$label) { warning("i18n: translation for '", x, "' not found!", call. = FALSE) return(x) } return(translations[label == x, c(translation)]) } stop("i18n option must be either: a list, a data.frame, or a path to a valid file.", call. = FALSE) } #' @param package Name of the package where the function is called, use `NULL` outside a package. #' It will retrieve option `"i18n."` (or `"i18n"` if no package) to returns appropriate labels. #' #' @export #' #' @rdname i18n #' #' @importFrom utils packageName #' @importFrom data.table fread i18n_translations <- function(package = packageName(parent.frame(2))) { if (is.null(package)) { opts <- "i18n" } else { opts <- paste(package, "i18n", sep = ".") } language <- getOption(x = opts) if (is.null(language)) return(NULL) if (is.character(language) && i18n_exist(language, package = package)) { language <- fread(file = i18n_file(language, package = package), encoding = "UTF-8", fill = TRUE) } else if (is.character(language) && file.exists(language)) { language <- fread(file = language, encoding = "UTF-8", fill = TRUE) } else if (is.character(language)) { warning( "i18n translations not found for : ", language, call. = FALSE ) language <- NULL } return(language) } #' @param value Value to set for translation. Can be: #' * single `character` to use a supported language (`"fr"`, `"mk"`, `"al"`, `"pt"` for esquisse and datamods packages). #' * a `list` with labels as names and translations as values. #' * a `data.frame` with 2 column: `label` & `translation`. #' * path to a CSV file with same structure as for `data.frame` above. #' @param packages Name of packages for which to set i18n, default to esquisse and datamods #' #' @export #' #' @rdname i18n #' #' @importFrom stats setNames set_i18n <- function(value, packages = c("datamods", "esquisse")) { if (is.null(packages)) { options("i18n" = value) } else { i18n.opts <- setNames( lapply(seq_along(packages), function(...) value), paste(packages, "i18n", sep = ".") ) options(i18n.opts) } } i18n_file <- function(x, package) { if (is.null(package)) return(character(0)) system.file("i18n", paste0(x, ".csv"), package = package) } i18n_exist <- function(x, package) { isTRUE(file.exists(i18n_file(x, package))) } i18n_test <- function(x) { i18n(x) } ================================================ FILE: R/import-copypaste.R ================================================ #' @title Import data with copy & paste #' #' @description Let the user copy data from Excel or text file then paste it into a text area to import it. #' #' @inheritParams import-globalenv #' @param name_field Show or not a field to add a name to data (that is returned server-side). #' #' @template module-import #' #' @export #' #' @name import-copypaste #' #' @importFrom shiny NS icon textAreaInput actionButton textInput #' @importFrom htmltools tags tagAppendAttributes #' #' @example examples/from-copypaste.R import_copypaste_ui <- function(id, title = TRUE, name_field = TRUE) { ns <- NS(id) if (isTRUE(title)) { title <- tags$h4( i18n("Copy & paste data"), class = "datamods-title" ) } tags$div( class = "datamods-import", html_dependency_datamods(), title, tagAppendAttributes( textAreaInput( inputId = ns("data_pasted"), label = i18n("Paste data here:"), height = "300px", width = "100%", resize = "none" ), class = "shiny-input-container-inline" ), if (isTRUE(name_field)) { textInput( inputId = ns("name"), label = NULL, placeholder = i18n("Add a label to data"), width = "100%" ) }, tags$div( id = ns("import-placeholder"), alert( id = ns("import-result"), status = "info", tags$b(i18n("Nothing pasted yet!")), i18n("Please copy and paste some data in the dialog box above."), dismissible = TRUE ) ), uiOutput( outputId = ns("container_valid_btn"), style = "margin-top: 20px;" ) ) } #' @inheritParams import_globalenv_server #' @param fread_args `list` of additional arguments to pass to [data.table::fread()] when reading data. #' #' @export #' #' @importFrom shiny moduleServer #' @importFrom data.table fread #' @importFrom shiny reactiveValues observeEvent removeUI reactive #' @importFrom htmltools tags tagList #' @importFrom rlang %||% #' #' @rdname import-copypaste import_copypaste_server <- function(id, btn_show_data = TRUE, show_data_in = c("popup", "modal"), trigger_return = c("button", "change"), return_class = c("data.frame", "data.table", "tbl_df", "raw"), reset = reactive(NULL), fread_args = list()) { trigger_return <- match.arg(trigger_return) return_class <- match.arg(return_class) module <- function(input, output, session) { ns <- session$ns imported_rv <- reactiveValues(data = NULL, name = NULL) temporary_rv <- reactiveValues(data = NULL, name = NULL, status = NULL) observeEvent(reset(), { temporary_rv$data <- NULL temporary_rv$name <- NULL temporary_rv$status <- NULL }) output$container_valid_btn <- renderUI({ if (identical(trigger_return, "button")) { button_import() } }) observeEvent(input$data_pasted, { req(input$data_pasted) fread_args$tex <- input$data_pasted imported <- try(rlang::exec(data.table::fread, !!!fread_args), silent = TRUE) if (inherits(imported, "try-error") || NROW(imported) < 1) { toggle_widget(inputId = "confirm", enable = FALSE) insert_error(mssg = i18n(attr(imported, "condition")$message)) temporary_rv$status <- "error" temporary_rv$data <- NULL temporary_rv$name <- NULL } else { toggle_widget(inputId = "confirm", enable = TRUE) insert_alert( selector = ns("import"), status = "success", make_success_alert( imported, trigger_return = trigger_return, btn_show_data = btn_show_data ) ) temporary_rv$status <- "success" temporary_rv$data <- imported } }, ignoreInit = TRUE) observeEvent(input$name, { temporary_rv$name <- if (isTruthy(input$name)) { input$name } else { "clipboard_data" } }) observeEvent(input$see_data, { show_data(temporary_rv$data, title = i18n("Imported data"), type = show_data_in) }) observeEvent(input$confirm, { imported_rv$data <- temporary_rv$data imported_rv$name <- temporary_rv$name }) if (identical(trigger_return, "button")) { return(list( status = reactive(temporary_rv$status), name = reactive(imported_rv$name), data = reactive(as_out(imported_rv$data, return_class)) )) } else { return(list( status = reactive(temporary_rv$status), name = reactive(temporary_rv$name), data = reactive(as_out(temporary_rv$data, return_class)) )) } } moduleServer( id = id, module = module ) } ================================================ FILE: R/import-file.R ================================================ #' @title Import data from a file #' #' @description Let user upload a file and import data #' #' @inheritParams import-globalenv #' @param preview_data Show or not a preview of the data under the file input. #' @param file_extensions File extensions accepted by [shiny::fileInput()], can also be MIME type. #' @param layout_params How to display import parameters : in a dropdown button or inline below file input. #' #' @template module-import #' #' @export #' #' @name import-file #' #' @importFrom shiny NS fileInput actionButton icon #' @importFrom htmltools tags tagAppendAttributes css tagAppendChild #' @importFrom shinyWidgets pickerInput numericInputIcon textInputIcon dropMenu #' @importFrom phosphoricons ph #' @importFrom toastui datagridOutput2 #' #' @example examples/from-file.R import_file_ui <- function(id, title = TRUE, preview_data = TRUE, file_extensions = c(".csv", ".txt", ".xls", ".xlsx", ".rds", ".fst", ".sas7bdat", ".sav"), layout_params = c("dropdown", "inline")) { ns <- NS(id) if (!is.null(layout_params)) { layout_params <- match.arg(layout_params) } if (isTRUE(title)) { title <- tags$h4( i18n("Import a file"), class = "datamods-title" ) } params_ui <- fluidRow( column( width = 6, numericInputIcon( inputId = ns("skip_rows"), label = i18n("Rows to skip before reading data:"), value = 0, min = 0, icon = list("n ="), size = "sm", width = "100%" ), tagAppendChild( textInputIcon( inputId = ns("na_label"), label = i18n("Missing values character(s):"), value = ",NA", icon = list("NA"), size = "sm", width = "100%" ), shiny::helpText(ph("info"), i18n("if several use a comma (',') to separate them")) ) ), column( width = 6, textInputIcon( inputId = ns("dec"), label = i18n("Decimal separator:"), value = ".", icon = list("0.00"), size = "sm", width = "100%" ), textInputIcon( inputId = ns("encoding"), label = i18n("Encoding:"), value = "UTF-8", icon = phosphoricons::ph("text-aa"), size = "sm", width = "100%" ) ) ) file_ui <- tagAppendAttributes( fileInput( inputId = ns("file"), label = i18n("Upload a file:"), buttonLabel = i18n("Browse..."), placeholder = i18n("No file selected"), accept = file_extensions, width = "100%" ), class = "mb-0" ) if (identical(layout_params, "dropdown")) { file_ui <- tags$div( style = css( display = "grid", gridTemplateColumns = "1fr 50px", gridColumnGap = "10px" ), file_ui, tags$div( class = "shiny-input-container", tags$label( class = "control-label", `for` = ns("dropdown_params"), "...", style = css(visibility = "hidden") ), shinyWidgets::dropMenu( actionButton( inputId = ns("dropdown_params"), label = ph("gear", title = "Parameters"), width = "50px", class = "px-1" ), params_ui ) ) ) } tags$div( class = "datamods-import", html_dependency_datamods(), shinyWidgets::html_dependency_winbox(), title, file_ui, if (identical(layout_params, "inline")) params_ui, tags$div( class = "hidden", id = ns("sheet-container"), pickerInput( inputId = ns("sheet"), label = i18n("Select sheet to import:"), choices = NULL, width = "100%" ) ), tags$div( id = ns("import-placeholder"), alert( id = ns("import-result"), status = "info", tags$b(i18n("No file selected:")), sprintf(i18n("You can import %s files"), paste(file_extensions, collapse = ", ")), dismissible = TRUE ) ), if (isTRUE(preview_data)) { datagridOutput2(outputId = ns("table")) }, uiOutput( outputId = ns("container_confirm_btn"), style = "margin-top: 20px;" ), tags$div( style = css(display = "none"), checkboxInput( inputId = ns("preview_data"), label = NULL, value = isTRUE(preview_data) ) ) ) } #' @inheritParams import_globalenv_server #' @param read_fns Named list with custom function(s) to read data: #' * the name must be the extension of the files to which the function will be applied #' * the value must be a function that can have 5 arguments (you can ignore some of them, but you have to use the same names), #' passed by user through the interface: #' + `file`: path to the file #' + `sheet`: for Excel files, sheet to read #' + `skip`: number of row to skip #' + `dec`: decimal separator #' + `encoding`: file encoding #' + `na.strings`: character(s) to interpret as missing values. #' #' @export #' #' @importFrom shiny moduleServer #' @importFrom htmltools tags tagList #' @importFrom shiny reactiveValues reactive observeEvent removeUI req #' @importFrom shinyWidgets updatePickerInput #' @importFrom readxl excel_sheets #' @importFrom rio import #' @importFrom rlang exec fn_fmls_names is_named is_function #' @importFrom tools file_ext #' @importFrom utils head #' @importFrom toastui renderDatagrid2 datagrid #' #' @rdname import-file import_file_server <- function(id, btn_show_data = TRUE, show_data_in = "winbox", trigger_return = c("button", "change"), return_class = c("data.frame", "data.table", "tbl_df", "raw"), reset = reactive(NULL), read_fns = list()) { if (length(read_fns) > 0) { if (!is_named(read_fns)) stop("import_file_server: `read_fns` must be a named list.", call. = FALSE) if (!all(vapply(read_fns, is_function, logical(1)))) stop("import_file_server: `read_fns` must be list of function(s).", call. = FALSE) } trigger_return <- match.arg(trigger_return) return_class <- match.arg(return_class) module <- function(input, output, session) { ns <- session$ns imported_rv <- reactiveValues(data = NULL, name = NULL) temporary_rv <- reactiveValues(data = NULL, name = NULL, status = NULL) observeEvent(reset(), { temporary_rv$data <- NULL temporary_rv$name <- NULL temporary_rv$status <- NULL }) output$container_confirm_btn <- renderUI({ if (identical(trigger_return, "button")) { button_import() } }) observeEvent(input$file, { if (isTRUE(is_excel(input$file$datapath))) { updatePickerInput( session = session, inputId = "sheet", choices = readxl::excel_sheets(input$file$datapath) ) showUI(paste0("#", ns("sheet-container"))) } else { hideUI(paste0("#", ns("sheet-container"))) } }) observeEvent(list( input$file, input$sheet, input$skip_rows, input$dec, input$encoding, input$na_label ), { req(input$file) # req(input$skip_rows) extension <- tools::file_ext(input$file$datapath) if (isTRUE(extension %in% names(read_fns))) { parameters <- list( file = input$file$datapath, sheet = input$sheet, skip = input$skip_rows, dec = input$dec, encoding = input$encoding, na.strings = split_char(input$na_label) ) parameters <- parameters[which(names(parameters) %in% fn_fmls_names(read_fns[[extension]]))] imported <- try(rlang::exec(read_fns[[extension]], !!!parameters), silent = TRUE) code <- call2(read_fns[[extension]], !!!modifyList(parameters, list(file = input$file$name))) } else { if (is_excel(input$file$datapath)) { req(input$sheet) parameters <- list( file = input$file$datapath, which = input$sheet, skip = input$skip_rows, na = split_char(input$na_label) ) } else if (is_sas(input$file$datapath)) { parameters <- list( file = input$file$datapath, skip = input$skip_rows, encoding = input$encoding ) } else { parameters <- list( file = input$file$datapath, skip = input$skip_rows, dec = input$dec, encoding = input$encoding, na.strings = split_char(input$na_label) ) } imported <- try(rlang::exec(rio::import, !!!parameters), silent = TRUE) code <- call2("import", !!!modifyList(parameters, list(file = input$file$name)), .ns = "rio") } if (inherits(imported, "try-error")) { imported <- try(rlang::exec(rio::import, !!!parameters[1]), silent = TRUE) code <- call2("import", !!!list(file = input$file$name), .ns = "rio") } if (inherits(imported, "try-error") || NROW(imported) < 1) { toggle_widget(inputId = "confirm", enable = FALSE) insert_error(mssg = i18n(attr(imported, "condition")$message)) temporary_rv$status <- "error" temporary_rv$data <- NULL temporary_rv$name <- NULL temporary_rv$code <- NULL } else { toggle_widget(inputId = "confirm", enable = TRUE) insert_alert( selector = ns("import"), status = "success", make_success_alert( imported, trigger_return = trigger_return, btn_show_data = btn_show_data, extra = if (isTRUE(input$preview_data)) i18n("First five rows are shown below:") ) ) temporary_rv$status <- "success" temporary_rv$data <- imported temporary_rv$name <- input$file$name temporary_rv$code <- code } }, ignoreInit = TRUE) observeEvent(input$see_data, { show_data(temporary_rv$data, title = i18n("Imported data"), type = show_data_in) }) output$table <- renderDatagrid2({ req(temporary_rv$data) datagrid( data = head(temporary_rv$data, 5), theme = "striped", colwidths = "guess", minBodyHeight = 250 ) }) observeEvent(input$confirm, { imported_rv$data <- temporary_rv$data imported_rv$name <- temporary_rv$name imported_rv$code <- temporary_rv$code }) if (identical(trigger_return, "button")) { return(list( status = reactive(temporary_rv$status), name = reactive(imported_rv$name), code = reactive(imported_rv$code), data = reactive(as_out(imported_rv$data, return_class)) )) } else { return(list( status = reactive(temporary_rv$status), name = reactive(temporary_rv$name), code = reactive(temporary_rv$code), data = reactive(as_out(temporary_rv$data, return_class)) )) } } moduleServer( id = id, module = module ) } # utils ------------------------------------------------------------------- is_excel <- function(path) { isTRUE(tools::file_ext(path) %in% c("xls", "xlsx")) } is_sas <- function(path) { isTRUE(tools::file_ext(path) %in% c("sas7bdat")) } ================================================ FILE: R/import-globalenv.R ================================================ #' @title Import data from an Environment #' #' @description Let the user select a dataset from its own environment or from a package's environment. #' #' @param id Module's ID. #' @param globalenv Search for data in Global environment. #' @param packages Name of packages in which to search data. #' @param title Module's title, if `TRUE` use the default title, #' use `NULL` for no title or a `shiny.tag` for a custom one. #' #' @template module-import #' #' @export #' #' @name import-globalenv #' #' @importFrom htmltools tags #' @importFrom shiny NS actionButton icon textInput #' @importFrom shinyWidgets pickerInput alert #' #' @example examples/from-globalenv.R import_globalenv_ui <- function(id, globalenv = TRUE, packages = get_data_packages(), title = TRUE) { ns <- NS(id) choices <- list() if (isTRUE(globalenv)) { choices <- append(choices, "Global Environment") } if (!is.null(packages)) { choices <- append(choices, list(Packages = as.character(packages))) } if (isTRUE(globalenv)) { selected <- "Global Environment" } else { selected <- packages[1] } if (isTRUE(title)) { title <- tags$h4( i18n("Import a dataset from an environment"), class = "datamods-title" ) } tags$div( class = "datamods-import", html_dependency_datamods(), title, pickerInput( inputId = ns("data"), label = i18n("Select a data.frame:"), choices = NULL, options = list(title = i18n("List of data.frame...")), width = "100%" ), pickerInput( inputId = ns("env"), label = i18n("Select an environment in which to search:"), choices = choices, selected = selected, width = "100%", options = list( "title" = i18n("Select environment"), "live-search" = TRUE, "size" = 10 ) ), tags$div( id = ns("import-placeholder"), alert( id = ns("import-result"), status = "info", tags$b(i18n("No data selected!")), i18n("Use a data.frame from your environment or from the environment of a package."), dismissible = TRUE ) ), uiOutput( outputId = ns("container_valid_btn"), style = "margin-top: 20px;" ) ) } #' @param btn_show_data Display or not a button to display data in a modal window if import is successful. #' @param show_data_in Where to display data: in a `"popup"` or in a `"modal"` window. #' @param trigger_return When to update selected data: #' `"button"` (when user click on button) or #' `"change"` (each time user select a dataset in the list). #' @param return_class Class of returned data: `data.frame`, `data.table`, `tbl_df` (tibble) or `raw`. #' @param reset A `reactive` function that when triggered resets the data. #' #' @export #' #' @importFrom shiny moduleServer reactiveValues observeEvent reactive removeUI is.reactive icon actionLink isTruthy #' @importFrom htmltools tags tagList #' @importFrom shinyWidgets updatePickerInput #' #' @rdname import-globalenv import_globalenv_server <- function(id, btn_show_data = TRUE, show_data_in = c("popup", "modal"), trigger_return = c("button", "change"), return_class = c("data.frame", "data.table", "tbl_df", "raw"), reset = reactive(NULL)) { trigger_return <- match.arg(trigger_return) return_class <- match.arg(return_class) module <- function(input, output, session) { ns <- session$ns imported_rv <- reactiveValues(data = NULL, name = NULL) temporary_rv <- reactiveValues(data = NULL, name = NULL, status = NULL) observeEvent(reset(), { temporary_rv$data <- NULL temporary_rv$name <- NULL temporary_rv$status <- NULL }) output$container_valid_btn <- renderUI({ if (identical(trigger_return, "button")) { button_import() } }) observeEvent(input$env, { if (identical(input$env, "Global Environment")) { choices <- search_obj("data.frame") } else { choices <- list_pkg_data(input$env) } if (is.null(choices)) { choices <- i18n("No data.frame here...") choicesOpt <- list(disabled = TRUE) } else { choicesOpt <- list( subtext = get_dimensions(choices) ) } temporary_rv$package <- attr(choices, "package") updatePickerInput( session = session, inputId = "data", choices = choices, choicesOpt = choicesOpt ) }) observeEvent(input$trigger, { if (identical(trigger_return, "change")) { hideUI(selector = paste0("#", ns("container_valid_btn"))) } }) observeEvent(input$data, { if (!isTruthy(input$data)) { toggle_widget(inputId = "confirm", enable = FALSE) insert_alert( selector = ns("import"), status = "info", tags$b(i18n("No data selected!")), i18n("Use a data.frame from your environment or from the environment of a package.") ) } else { name_df <- input$data if (!is.null(temporary_rv$package)) { attr(name_df, "package") <- temporary_rv$package } imported <- try(get_env_data(name_df), silent = TRUE) if (inherits(imported, "try-error") || NROW(imported) < 1) { toggle_widget(inputId = "confirm", enable = FALSE) insert_error(mssg = i18n(attr(imported, "condition")$message)) temporary_rv$status <- "error" temporary_rv$data <- NULL temporary_rv$name <- NULL } else { toggle_widget(inputId = "confirm", enable = TRUE) insert_alert( selector = ns("import"), status = "success", make_success_alert( imported, trigger_return = trigger_return, btn_show_data = btn_show_data ) ) pkg <- attr(name_df, "package") if (!is.null(pkg)) { name <- paste(pkg, input$data, sep = "::") } else { name <- input$data } name <- trimws(sub("\\(([^\\)]+)\\)", "", name)) temporary_rv$status <- "success" temporary_rv$data <- imported temporary_rv$name <- name } } }, ignoreInit = TRUE, ignoreNULL = FALSE) observeEvent(input$see_data, { show_data(temporary_rv$data, title = i18n("Imported data"), type = show_data_in) }) observeEvent(input$confirm, { imported_rv$data <- temporary_rv$data imported_rv$name <- temporary_rv$name }) if (identical(trigger_return, "button")) { return(list( status = reactive(temporary_rv$status), name = reactive(imported_rv$name), data = reactive(as_out(imported_rv$data, return_class)) )) } else { return(list( status = reactive(temporary_rv$status), name = reactive(temporary_rv$name), data = reactive(as_out(temporary_rv$data, return_class)) )) } } moduleServer( id = id, module = module ) } # utils ------------------------------------------------------------------- #' Get packages containing datasets #' #' @return a character vector of packages names #' @export #' #' @importFrom utils data #' #' @examples #' if (interactive()) { #' #' get_data_packages() #' #' } get_data_packages <- function() { suppressWarnings({ pkgs <- data(package = .packages(all.available = TRUE)) }) unique(pkgs$results[, 1]) } #' List dataset contained in a package #' #' @param pkg Name of the package, must be installed. #' #' @return a \code{character} vector or \code{NULL}. #' @export #' #' @importFrom utils data #' #' @examples #' #' list_pkg_data("ggplot2") list_pkg_data <- function(pkg) { if (isTRUE(requireNamespace(pkg, quietly = TRUE))) { list_data <- data(package = pkg, envir = environment())$results[, "Item"] list_data <- sort(list_data) attr(list_data, "package") <- pkg if (length(list_data) < 1) { NULL } else { unname(list_data) } } else { NULL } } #' @importFrom utils data get_env_data <- function(obj, env = globalenv()) { pkg <- attr(obj, "package") re <- regexpr(pattern = "\\(([^\\)]+)\\)", text = obj) obj_ <- substr(x = obj, start = re + 1, stop = re + attr(re, "match.length") - 2) obj <- gsub(pattern = "\\s.*", replacement = "", x = obj) if (obj %in% ls(name = env)) { get(x = obj, envir = env) } else if (!is.null(pkg) && !identical(pkg, "")) { res <- suppressWarnings(try( get(utils::data(list = obj, package = pkg, envir = environment())), silent = TRUE )) if (!inherits(res, "try-error")) return(res) data(list = obj_, package = pkg, envir = environment()) get(obj, envir = environment()) } else { NULL } } get_dimensions <- function(objs) { if (is.null(objs)) return(NULL) dataframes_dims <- Map( f = function(name, pkg) { attr(name, "package") <- pkg tmp <- suppressWarnings(get_env_data(name)) if (is.data.frame(tmp)) { sprintf("%d obs. of %d variables", nrow(tmp), ncol(tmp)) } else { i18n("Not a data.frame") } }, name = objs, pkg = if (!is.null(attr(objs, "package"))) { attr(objs, "package") } else { character(1) } ) unlist(dataframes_dims) } ================================================ FILE: R/import-googlesheets.R ================================================ #' @title Import data from Googlesheet #' #' @description Let user paste link to a Google sheet then import the data. #' #' @inheritParams import-globalenv #' #' @template module-import #' #' @export #' @name import-googlesheets #' #' @importFrom shiny NS actionLink #' @importFrom shinyWidgets textInputIcon #' @importFrom htmltools tags tagList #' #' @example examples/from-googlesheets.R import_googlesheets_ui <- function(id, title = TRUE) { ns <- NS(id) if (isTRUE(title)) { title <- tags$h4( i18n("Import Google Spreadsheet"), class = "datamods-title" ) } tags$div( class = "datamods-import", html_dependency_datamods(), title, tags$div( class = "pull-right float-right", help_popup(tagList( i18n("You can either use:"), tags$ul( tags$li( i18n("A shareable link, in that case first sheet will be read") ), tags$li( i18n("The URL that appear in your browser, in that case the current sheet will be read") ) ) )) ), textInputIcon( inputId = ns("link"), label = i18n("Enter a shareable link to a GoogleSheet:"), icon = phosphoricons::ph("link"), width = "100%" ), tags$div( id = ns("import-placeholder"), alert( id = ns("import-result"), status = "info", tags$b(i18n("Nothing pasted yet!")), i18n("Please paste a valid GoogleSheet link in the dialog box above."), dismissible = TRUE ) ), uiOutput( outputId = ns("container_confirm_btn"), style = "margin-top: 20px;" ) ) } #' @inheritParams import_globalenv_server #' #' @export #' #' @importFrom shiny moduleServer #' @importFrom shiny reactiveValues observeEvent removeUI reactive req #' @importFrom htmltools tags tagList #' #' @rdname import-googlesheets import_googlesheets_server <- function(id, btn_show_data = TRUE, show_data_in = c("popup", "modal"), trigger_return = c("button", "change"), return_class = c("data.frame", "data.table", "tbl_df", "raw"), reset = reactive(NULL)) { trigger_return <- match.arg(trigger_return) return_class <- match.arg(return_class) module <- function(input, output, session) { ns <- session$ns imported_rv <- reactiveValues(data = NULL, name = NULL) temporary_rv <- reactiveValues(data = NULL, name = NULL, status = NULL) observeEvent(reset(), { temporary_rv$data <- NULL temporary_rv$name <- NULL temporary_rv$status <- NULL }) output$container_confirm_btn <- renderUI({ if (identical(trigger_return, "button")) { button_import() } }) observeEvent(input$trigger, { if (identical(trigger_return, "change")) { hideUI(selector = paste0("#", ns("confirm-button"))) } }) observeEvent(input$link, { req(input$link) imported <- try(read_gsheet(input$link), silent = TRUE) if (inherits(imported, "try-error") || NROW(imported) < 1) { toggle_widget(inputId = "confirm", enable = FALSE) insert_error(mssg = i18n(attr(imported, "condition")$message)) temporary_rv$status <- "error" temporary_rv$data <- NULL } else { toggle_widget(inputId = "confirm", enable = TRUE) insert_alert( selector = ns("import"), status = "success", make_success_alert( imported, trigger_return = trigger_return, btn_show_data = btn_show_data ) ) temporary_rv$status <- "success" temporary_rv$data <- imported } }, ignoreInit = TRUE) observeEvent(input$see_data, { show_data(temporary_rv$data, title = i18n("Imported data"), type = show_data_in) }) observeEvent(input$confirm, { imported_rv$data <- temporary_rv$data }) if (identical(trigger_return, "button")) { return(list( status = reactive(temporary_rv$status), name = reactive(imported_rv$name), data = reactive(as_out(imported_rv$data, return_class)) )) } else { return(list( status = reactive(temporary_rv$status), name = reactive(temporary_rv$name), data = reactive(as_out(temporary_rv$data, return_class)) )) } } moduleServer( id = id, module = module ) } # Utils ------------------------------------------------------------------- get_id <- function(x) { if (grepl("/d/", x)) { x <- strsplit(x = x, split = "/") x <- unlist(x) x[which(x == "d") + 1] } else if (grepl("id=", x)) { x <- regmatches(x, gregexpr("id=[[:alnum:]_-]+", x)) gsub("^id=", "", x[[1]]) } else { stop("Failed to retrieve Googlesheet ID") } } #' @importFrom data.table fread .SD #' @importFrom utils type.convert read_gsheet <- function(url, dec = NULL) { url_ <- sprintf( "https://docs.google.com/spreadsheets/export?id=%s&format=csv", get_id(url) ) if (grepl("gid=", url)) { gid <- regmatches(url, gregexpr("gid=[0-9]+", url)) url_ <- paste0(url_, "&", gid[[1]]) } dt <- fread(input = url_) if (!is.null(dec)) { dt <- dt[, lapply(.SD, type.convert, dec = dec)] } return(dt) } ================================================ FILE: R/import-modal.R ================================================ #' @title Import from all sources #' #' @description Wrap all import modules into one, can be displayed inline or in a modal window.. #' #' @param id Module's id #' @param from The import_ui & server to use, i.e. the method. #' There are 5 options to choose from. ("env", "file", "copypaste", "googlesheets", "url") #' @inheritParams import-file #' #' @template module-import #' #' @export #' @name import-modal #' #' @importFrom shiny NS tabsetPanel tabPanel tabPanelBody icon fluidRow column #' @importFrom htmltools tags HTML #' @importFrom shinyWidgets radioGroupButtons #' #' @example examples/modal.R #' import_ui <- function(id, from = c("env", "file", "copypaste", "googlesheets", "url"), file_extensions = c(".csv", ".txt", ".xls", ".xlsx", ".rds", ".fst", ".sas7bdat", ".sav")) { ns <- NS(id) from <- match.arg(from, several.ok = TRUE) env <- if ("env" %in% from) { tabPanelBody( value = "env", tags$br(), import_globalenv_ui(id = ns("env"), title = NULL) ) } file <- if ("file" %in% from) { tabPanelBody( value = "file", tags$br(), import_file_ui(id = ns("file"), title = NULL, file_extensions = file_extensions) ) } copypaste <- if ("copypaste" %in% from) { tabPanelBody( value = "copypaste", tags$br(), import_copypaste_ui(id = ns("copypaste"), title = NULL) ) } googlesheets <- if ("googlesheets" %in% from) { tabPanelBody( value = "googlesheets", tags$br(), import_googlesheets_ui(id = ns("googlesheets"), title = NULL) ) } url <- if ("url" %in% from) { tabPanelBody( value = "url", tags$br(), import_url_ui(id = ns("url"), title = NULL) ) } #database <- if("database" %in% from) tabPanel("Database", import_database_ui(ns("database"))) labsImport <- list( "env" = i18n("Environment"), "file" = i18n("External file"), "copypaste" = i18n("Copy / Paste"), "googlesheets" = i18n("Googlesheets"), "url" = i18n("URL") ) iconsImport <- list( "env" = phosphoricons::ph("code", title = labsImport$env), "file" = phosphoricons::ph("file-arrow-down", title = labsImport$file), "copypaste" = phosphoricons::ph("clipboard-text", title = labsImport$copypaste), "googlesheets" = phosphoricons::ph("cloud-arrow-down", title = labsImport$googlesheets), "url" = phosphoricons::ph("link", title = labsImport$url) ) if (identical(length(from), 1L)) { importTab <- switch( from, "env" = import_globalenv_ui(id = ns("env")), "file" = import_file_ui(id = ns("file"), file_extensions = file_extensions), "copypaste" = import_copypaste_ui(id = ns("copypaste")), "googlesheets" = import_googlesheets_ui(id = ns("googlesheets")), "url" = import_url_ui(id = ns("url")), ) } else { tabsetPanelArgs <- dropNulls(list( env, file, copypaste, googlesheets, url, id = ns("tabs-import"), type = "hidden" )) importTab <- do.call( what = tabsetPanel, args = tabsetPanelArgs ) importTab <- fluidRow( column( width = 3, tags$br(), tags$style( HTML(sprintf("#%s>.btn-group-vertical {width: 100%%;}", ns("from"))), HTML(sprintf(".btn-group-vertical>.btn-group>.btn {text-align: left;}")) ), radioGroupButtons( inputId = ns("from"), label = i18n("How to import data?"), choiceValues = from, choiceNames = lapply( X = from, FUN = function(x) { tagList(iconsImport[[x]], labsImport[[x]]) } ), direction = "vertical", width = "100%" ) ), column( width = 9, importTab ) ) } tags$div( class = "datamods-imports", html_dependency_datamods(), tags$style(".tui-grid-cell-summary {vertical-align: baseline;}"), bslib::navset_underline( id = ns("tabs-mode"), bslib::nav_panel( title = tagList( phosphoricons::ph("download-simple", title = i18n("Import")), i18n("Import") ), value = "import", importTab ), bslib::nav_panel( title = tagList( phosphoricons::ph("table", title = i18n("View")), i18n("View") ), value = "view", tags$br(), tags$div( style = css(minHeight = "600px"), toastui::datagridOutput(outputId = ns("view"), height = "auto") ) ), bslib::nav_panel( title = tagList( phosphoricons::ph("gear-six", title = i18n("Update")), i18n("Update") ), value = "update", tags$br(), update_variables_ui(id = ns("update"), title = NULL) ), bslib::nav_panel( title = tagList( phosphoricons::ph("shield-check", title = i18n("Validate")), i18n("Validate") ), value = "validate", tags$br(), validation_ui( id = ns("validation"), display = "inline", max_height = "400px" ) ) ), tags$div( id = ns("confirm-button"), style = "margin-top: 20px;", button_import(list(ns = ns)) ), tags$div( style = "display: none;", textInput(inputId = ns("hidden"), label = NULL, value = genId()) ), tags$script( sprintf("$('#%s').addClass('nav-justified');", ns("tabs-mode")), sprintf("fadeTab({id: '%s'});", ns("tabs-mode")), sprintf("disableTab({id: '%s', value: '%s'});", ns("tabs-mode"), "view"), sprintf("disableTab({id: '%s', value: '%s'});", ns("tabs-mode"), "update"), sprintf("disableTab({id: '%s', value: '%s'});", ns("tabs-mode"), "validate") ) ) } #' @param validation_opts `list` of arguments passed to [validation_server(). #' @param allowed_status Vector of statuses allowed to confirm dataset imported, #' if you want that all validation rules are successful before importing data use `allowed_status = "OK"`. #' @param return_class Class of returned data: `data.frame`, `data.table`, `tbl_df` (tibble) or `raw`. #' #' @export #' @rdname import-modal #' @importFrom shiny moduleServer reactiveValues observeEvent #' reactive removeModal updateTabsetPanel hideTab observe #' @importFrom rlang %||% import_server <- function(id, validation_opts = NULL, allowed_status = c("OK", "Failed", "Error"), return_class = c("data.frame", "data.table", "tbl_df", "raw"), read_fns = list()) { allowed_status <- match.arg(allowed_status, several.ok = TRUE) return_class <- match.arg(return_class) if (length(read_fns) > 0) { if (!is_named(read_fns)) stop("import_file_server: `read_fns` must be a named list.", call. = FALSE) if (!all(vapply(read_fns, is_function, logical(1)))) stop("import_file_server: `read_fns` must be list of function(s).", call. = FALSE) } moduleServer( id, function(input, output, session) { ns <- session$ns data_rv <- reactiveValues(data = NULL) imported_rv <- reactiveValues(data = NULL) observeEvent(input$hidden, { data_rv$data <- NULL data_rv$name <- NULL if (length(validation_opts) < 1) { hideTab(inputId = "tabs-mode", target = "validate") } }) observeEvent(input$from, { updateTabsetPanel( session = session, inputId = "tabs-import", selected = input$from ) }) from_env <- import_globalenv_server( id = "env", trigger_return = "change", btn_show_data = FALSE, reset = reactive(input$hidden) ) from_file <- import_file_server( id = "file", trigger_return = "change", btn_show_data = FALSE, reset = reactive(input$hidden), read_fns = read_fns ) from_copypaste <- import_copypaste_server( id = "copypaste", trigger_return = "change", btn_show_data = FALSE, reset = reactive(input$hidden) ) from_googlesheets <- import_googlesheets_server( id = "googlesheets", trigger_return = "change", btn_show_data = FALSE, reset = reactive(input$hidden) ) from_url <- import_url_server( id = "url", trigger_return = "change", btn_show_data = FALSE, reset = reactive(input$hidden) ) #from_database <- import_database_server("database") observeEvent(from_env$data(), { data_rv$data <- from_env$data() data_rv$name <- from_env$name() }) observeEvent(from_file$data(), { data_rv$data <- from_file$data() data_rv$name <- from_file$name() }) observeEvent(from_copypaste$data(), { data_rv$data <- from_copypaste$data() data_rv$name <- from_copypaste$name() }) observeEvent(from_googlesheets$data(), { data_rv$data <- from_googlesheets$data() data_rv$name <- from_googlesheets$name() }) observeEvent(from_url$data(), { data_rv$data <- from_url$data() data_rv$name <- from_url$name() }) # observeEvent(from_database$data(), { # data_rv$data <- from_database$data() # }) observeEvent(data_rv$data, { req(data_rv$data) if (is.data.frame(data_rv$data)) { if (length(validation_opts) < 1) { toggle_widget(inputId = "confirm", enable = TRUE) } else { status <- validation_results$status() if (isTRUE(status %in% allowed_status)) { toggle_widget(inputId = "confirm", enable = TRUE) } else { toggle_widget(inputId = "confirm", enable = FALSE) } } enable_tab("tabs-mode", "view") enable_tab("tabs-mode", "update") enable_tab("tabs-mode", "validate") } else { toggle_widget(inputId = "confirm", enable = FALSE) } }) output$view <- toastui::renderDatagrid({ data <- req(data_rv$data) session <- shiny::getDefaultReactiveDomain() gridTheme <- getOption("datagrid.theme") if (length(gridTheme) < 1) { apply_grid_theme() } on.exit(toastui::reset_grid_theme()) grid <- toastui::datagrid( data = data, summary = construct_col_summary(data), colwidths = "guess", minBodyHeight = 500 ) toastui::grid_columns(grid, className = "font-monospace") }) updated_data <- update_variables_server( id = "update", data = reactive(data_rv$data), height = "300px" ) validation_results <- validation_server( id = "validation", data = reactive({ data_rv$data }), n_row = validation_opts$n_row, n_col = validation_opts$n_col, n_row_label = validation_opts$n_row_label %||% "Valid number of rows", n_col_label = validation_opts$n_col_label %||% "Valid number of columns", btn_label = validation_opts$btn_label, rules = validation_opts$rules ) observeEvent(validation_results$status(), { status <- validation_results$status() req(status) if (status %in% c("Error", "Failed")) { update_tab_label("tabs-mode", "validate", tagList( phosphoricons::ph("warning-circle", weight = "fill", fill = "firebrick"), i18n("Validate") )) } else { update_tab_label("tabs-mode", "validate", i18n("Validate")) } if (status %in% allowed_status) { toggle_widget(inputId = "confirm", enable = TRUE) } else { toggle_widget(inputId = "confirm", enable = FALSE) } }) observeEvent(updated_data(), { data_rv$data <- updated_data() }) observeEvent(input$confirm, { removeModal() imported_rv$data <- data_rv$data imported_rv$name <- data_rv$name %||% "imported_data" }) return(list( data = reactive(as_out(imported_rv$data, return_class)), name = reactive(imported_rv$name) )) } ) } #' @param title Modal window title. #' @param size Modal window size, default to \code{"l"} (large). #' #' @export #' @rdname import-modal #' @importFrom shiny modalDialog showModal #' @importFrom htmltools tags css import_modal <- function(id, from, title = i18n("Import data"), size = "l", file_extensions = c(".csv", ".txt", ".xls", ".xlsx", ".rds", ".fst", ".sas7bdat", ".sav")) { showModal(modalDialog( title = tagList( button_close_modal(), title ), import_ui(id, from, file_extensions = file_extensions), size = size, footer = NULL )) } ================================================ FILE: R/import-url.R ================================================ #' @title Import data from a URL #' #' @description Let user paste link to a JSON then import the data. #' #' @inheritParams import-globalenv #' #' @template module-import #' #' @export #' @name import-url #' #' @importFrom htmltools tags #' #' @example examples/from-url.R import_url_ui <- function(id, title = TRUE) { ns <- shiny::NS(id) if (isTRUE(title)) { title <- tags$h4( i18n("Import Url"), class = "datamods-title" ) } tags$div( class = "datamods-import", html_dependency_datamods(), title, shinyWidgets::textInputIcon( inputId = ns("link"), label = i18n("Enter URL to data:"), icon = phosphoricons::ph("link"), width = "100%" ), tags$div( id = ns("import-placeholder"), alert( id = ns("import-result"), status = "info", tags$b(i18n("Nothing pasted yet!")), i18n("Please paste a valid link in the dialog box above."), i18n("You can import from flat table format supported by"), tags$a( href = "https://CRAN.R-project.org/package=rio/vignettes/rio.html#Supported_file_formats", "package rio" ), dismissible = TRUE ) ), shiny::uiOutput( outputId = ns("container_confirm_btn"), style = "margin-top: 20px;" ) ) } #' @inheritParams import_globalenv_server #' #' @export #' #' @importFrom shiny moduleServer #' @importFrom shiny reactiveValues observeEvent removeUI reactive req #' @importFrom htmltools tags tagList #' #' @rdname import-url import_url_server <- function(id, btn_show_data = TRUE, show_data_in = c("popup", "modal"), trigger_return = c("button", "change"), return_class = c("data.frame", "data.table", "tbl_df", "raw"), reset = reactive(NULL)) { trigger_return <- match.arg(trigger_return) return_class <- match.arg(return_class) module <- function(input, output, session) { ns <- session$ns imported_rv <- reactiveValues(data = NULL, name = NULL) temporary_rv <- reactiveValues(data = NULL, name = NULL, status = NULL) observeEvent(reset(), { temporary_rv$data <- NULL temporary_rv$name <- NULL temporary_rv$status <- NULL }) output$container_confirm_btn <- renderUI({ if (identical(trigger_return, "button")) { button_import() } }) observeEvent(input$trigger, { if (identical(trigger_return, "change")) { hideUI(selector = paste0("#", ns("confirm-button"))) } }) observeEvent(input$link, { req(input$link) imported <- try(rio::import(input$link), silent = TRUE) if (inherits(imported, "try-error")) # retry with explicit json format imported <- try(rio::import(input$link, format = "json"), silent = TRUE) if (inherits(imported, "try-error") || NROW(imported) < 1) { toggle_widget(inputId = "confirm", enable = FALSE) # pass error message to UI insert_error(mssg = i18n(attr(imported, "condition")$message)) temporary_rv$status <- "error" temporary_rv$data <- NULL temporary_rv$name <- NULL } else { toggle_widget(inputId = "confirm", enable = TRUE) insert_alert( selector = ns("import"), status = "success", make_success_alert( imported, trigger_return = trigger_return, btn_show_data = btn_show_data ) ) temporary_rv$status <- "success" temporary_rv$data <- imported temporary_rv$name <- basename(input$link) } }, ignoreInit = TRUE) observeEvent(input$see_data, { show_data(temporary_rv$data, title = i18n("Imported data"), type = show_data_in) }) observeEvent(input$confirm, { imported_rv$data <- temporary_rv$data imported_rv$name <- temporary_rv$name }) if (identical(trigger_return, "button")) { return(list( status = reactive(temporary_rv$status), name = reactive(imported_rv$name), data = reactive(as_out(imported_rv$data, return_class)) )) } else { return(list( status = reactive(temporary_rv$status), name = reactive(temporary_rv$name), data = reactive(as_out(temporary_rv$data, return_class)) )) } } moduleServer( id = id, module = module ) } ================================================ FILE: R/onLoad.R ================================================ #' Shiny resource #' #' @importFrom shiny addResourcePath #' #' @noRd .onLoad <- function(...) { shiny::addResourcePath("datamods", system.file("assets", package = "datamods")) } ================================================ FILE: R/sample-data.R ================================================ ## Function sample_n() #' @title Sample rows #' #' @description The `sample_n` function returns the sample of a dataset from a number of rows chosen by the user. #' #' @param data `data.frame` #' @param n vector of type `numeric` #' #' @return the sample of a dataset in the form of `data.table` #' #' @noRd #' #' @importFrom data.table as.data.table .N #' #' @examples #' sample_n(iris, 25) sample_n <- function(data, n) { as.data.table(data)[sample(x = .N, size = n)] } ## Function sample_prop() #' @title Sample percentage #' #' @description The `sample_prop` function returns the sample of a dataset from a percentage chosen by the user. #' #' @param data `data.frame` #' @param percentage vector of type `numeric` #' #' @return the sample of a dataset in the form of `data.table` #' #' @noRd #' #' @importFrom data.table as.data.table .N #' sample_prop <- function(data, prop) { as.data.table(data)[sample(x = .N, size = nrow(data) * (prop/100))] } ## Function sample_ui() #' @title Shiny module to interactively sample a `data.frame` #' #' @description Allow to take a sample of `data.frame` for a given number or proportion of rows to keep. #' #' @param id Module id. See [shiny::moduleServer()]. #' #' @return #' * UI: HTML tags that can be included in shiny's UI #' * Server: a `reactive` fgunction with the sampled data. #' #' @export #' #' @name module-sample #' #' @importFrom htmltools tagList #' @importFrom shinyWidgets radioGroupButtons #' @importFrom shiny NS conditionalPanel sliderInput uiOutput #' #' @example examples/sample.R sample_ui <- function(id) { ns <- NS(id) tagList( radioGroupButtons( inputId = ns("choice"), label = i18n("Sample data by :"), choiceNames = c(i18n("number of rows"), i18n("proportion of rows")), choiceValues = c("number of rows", "proportion of rows"), justified = TRUE, size = "xs", width = "100%" ), conditionalPanel( condition = "input.choice == `proportion of rows`", ns = ns, sliderInput( inputId = ns("proportion_rows"), label = i18n("Choose a percentage :"), min = 0, max = 100, value = 100, post = " %", width = "100%" ), uiOutput(outputId = ns("feedback_proportion_rows")) ), conditionalPanel( condition = "input.choice == `number of rows`", ns = ns, sliderInput( inputId = ns("number_rows"), label = i18n("Choose a number of rows :"), min = 0, max = 10, value = 10, width = "100%" ), uiOutput(outputId = ns("feedback_number_rows")) ) ) } ## Function sample_server() #' @param data_r `reactive` containing a `data.frame` to use in the module. #' #' @export #' #' @rdname module-sample #' #' @importFrom shiny moduleServer observeEvent updateSliderInput renderUI reactive #' @importFrom htmltools tags div #' sample_server <- function(id, data_r = reactive(NULL)) { moduleServer( id, function(input, output, session) { observeEvent(data_r(), { req(data_r()) updateSliderInput( session, inputId = "number_rows", min = 0, max = nrow(data_r()), value = nrow(data_r()) ) }) output$feedback_proportion_rows <- renderUI({ sample <- req(sample_r()) tags$div( paste(input$proportion_rows, i18n("% of the total, i.e."), nrow(sample), i18n("rows")) ) }) output$feedback_number_rows <- renderUI({ data <- req(data_r()) tags$div( paste(input$number_rows, i18n("lines, i.e."), round(input$number_rows / nrow(data) * 100, 1), i18n("% of the total")) ) }) sample_r <- reactive({ req(data_r()) if (input$choice == "proportion of rows") { table_sample <- sample_prop(data = data_r(), prop = input$proportion_rows) } else { table_sample <- sample_n(data = data_r(), n = input$number_rows) } return(table_sample) }) return(sample_r) } ) } ================================================ FILE: R/select-group.R ================================================ #' @title Select Group Input Module #' #' @description Group of mutually dependent select menus for filtering `data.frame`'s columns (like in Excel). #' #' @param id Module's id. #' @param params A list of parameters passed to each [shinyWidgets::virtualSelectInput()], #' you can use : #' * `inputId`: mandatory, must correspond to variable name. #' * `label`: Display label for the control. #' * `placeholder`: Text to show when no options selected. #' @param label Character, global label on top of all labels. #' @param btn_reset_label Character, reset button label. If `NULL` no button is added. #' @param inline If `TRUE` (the default), #' select menus are horizontally positioned, otherwise vertically. #' @param vs_args Arguments passed to all [shinyWidgets::virtualSelectInput()] created. #' #' @return A [shiny::reactive()] function containing data filtered with an attribute `inputs` containing a named list of selected inputs. #' @export #' #' @name select-group #' #' @importFrom utils modifyList #' @importFrom htmltools tagList tags css #' @importFrom shiny NS actionLink icon singleton #' @importFrom shinyWidgets virtualSelectInput #' #' @example examples/select-group-default.R #' @example examples/select-group-selected.R select_group_ui <- function( id, params, label = NULL, btn_reset_label = "Reset filters", inline = TRUE, vs_args = list() ) { ns <- NS(id) button_reset <- if (!is.null(btn_reset_label)) { actionLink( inputId = ns("reset_all"), label = tagList( phosphoricons::ph("x", title = btn_reset_label), btn_reset_label ), icon = NULL, style = "text-align: right;" ) } label_tag <- if (!is.null(label)) tags$b(label, class = "select-group-label") sel_tag <- lapply( X = seq_along(params), FUN = function(x) { input <- params[[x]] vs_args <- modifyList( x = vs_args, val = list( inputId = ns(input$inputId), label = input$label, placeholder = input$placeholder, choices = input$selected, selected = input$selected, multiple = ifelse(is.null(input$multiple), TRUE, input$multiple), width = "100%" ), keep.null = TRUE ) if (is.null(vs_args$showValueAsTags)) vs_args$showValueAsTags <- TRUE if (is.null(vs_args$zIndex)) vs_args$zIndex <- 10 if (is.null(vs_args$disableSelectAll)) vs_args$disableSelectAll <- TRUE tags$div( class = "select-group-item", id = ns(paste0("container-", input$inputId)), do.call(shinyWidgets::virtualSelectInput, vs_args) ) } ) if (isTRUE(inline)) { sel_tag <- tags$div( class = "select-group-container", style = htmltools::css( display = "grid", gridTemplateColumns = sprintf("repeat(%s, 1fr)", length(params)), gridColumnGap = "5px" ), sel_tag ) } tags$div( class = "select-group", label_tag, sel_tag, button_reset, html_dependency_datamods() ) } #' @param data_r Either a [data.frame()] or a [shiny::reactive()] #' function returning a `data.frame` (do not use parentheses). #' @param vars_r character, columns to use to create filters, #' must correspond to variables listed in `params`. Can be a #' [shiny::reactive()] function, but values must be included in the initial ones (in `params`). #' @param selected_r [shiny::reactive()] function returning a named list with selected values to set. #' #' @export #' #' @rdname select-group #' @importFrom shiny observeEvent observe reactiveValues reactive is.reactive isolate isTruthy #' @importFrom shinyWidgets updateVirtualSelect #' @importFrom rlang %||% select_group_server <- function(id, data_r, vars_r, selected_r = reactive(list())) { moduleServer( id = id, module = function(input, output, session) { # Namespace ns <- session$ns hideUI(selector = paste0("#", ns("reset_all"))) # data <- as.data.frame(data) rv <- reactiveValues(data = NULL, vars = NULL) observe({ if (is.reactive(data_r)) { rv$data <- data_r() } else { rv$data <- as.data.frame(data_r) } if (is.reactive(vars_r)) { rv$vars <- vars_r() } else { rv$vars <- vars_r } for (var in names(rv$data)) { if (var %in% rv$vars) { showUI(id = paste0("container-", var)) } else { hideUI(id = paste0("container-", var)) } } }) observe({ selected <- selected_r() if (!is.list(selected)) selected <- list() lapply( X = rv$vars, FUN = function(x) { vals <- sort(unique(rv$data[[x]])) shinyWidgets::updateVirtualSelect( session = session, inputId = x, choices = vals, selected = selected[[x]] %||% isolate(input[[x]]) ) } ) }) observeEvent(input$reset_all, { lapply( X = rv$vars, FUN = function(x) { vals <- sort(unique(rv$data[[x]])) shinyWidgets::updateVirtualSelect( session = session, inputId = x, choices = vals ) } ) }) observe({ vars <- rv$vars lapply( X = vars, FUN = function(x) { ovars <- vars[vars != x] observeEvent( input[[x]], { data <- rv$data indicator <- lapply( X = vars, FUN = function(x) { data[[x]] %inT% input[[x]] } ) indicator <- Reduce(f = `&`, x = indicator) data <- data[indicator, ] if (all(indicator)) { hideUI(selector = paste0("#", ns("reset_all"))) } else { showUI(selector = paste0("#", ns("reset_all"))) } for (i in ovars) { if (!isTruthy(input[[i]])) { shinyWidgets::updateVirtualSelect( session = session, inputId = i, choices = sort(unique(data[[i]])) ) } } if (!isTruthy(input[[x]])) { shinyWidgets::updateVirtualSelect( session = session, inputId = x, choices = sort(unique(data[[x]])) ) } }, ignoreNULL = FALSE, ignoreInit = TRUE ) } ) }) return(reactive({ data <- rv$data vars <- rv$vars indicator <- lapply( X = vars, FUN = function(x) { data[[x]] %inT% input[[x]] } ) indicator <- Reduce(f = `&`, x = indicator) data <- data[indicator, ] attr(data, "inputs") <- lapply( X = setNames(vars, vars), FUN = function(x) input[[x]] ) return(data) })) } ) } ================================================ FILE: R/show_data.R ================================================ #' Display a table in a window #' #' @param data a data object (either a `matrix` or a `data.frame`). #' @param title Title to be displayed in window. #' @param show_classes Show variables classes under variables names in table header. #' @param type Display table in a pop-up with [shinyWidgets::show_alert()], #' in modal window with [shiny::showModal()] or in a WinBox window with [shinyWidgets::WinBox()]. #' @param options Arguments passed to [toastui::datagrid()]. #' @param width Width of the window, only used if `type = "popup"` or `type = "winbox"`. #' @param ... Additional options, such as `wbOptions = wbOptions()` or `wbControls = wbControls()`. #' @inheritParams shiny::showModal #' #' @note #' If you use `type = "winbox"`, you'll need to use `shinyWidgets::html_dependency_winbox()` somewhere in your UI. #' #' @return No value. #' @export #' #' @importFrom shinyWidgets show_alert #' @importFrom htmltools tags tagList css #' @importFrom shiny showModal modalDialog #' @importFrom utils modifyList packageVersion #' #' @example examples/show_data.R show_data <- function(data, title = NULL, options = NULL, show_classes = TRUE, type = c("popup", "modal", "winbox"), width = "65%", ..., session = shiny::getDefaultReactiveDomain()) { # nocov start type <- match.arg(type) data <- as.data.frame(data) args <- list(...) gridTheme <- getOption("datagrid.theme") if (length(gridTheme) < 1) { apply_grid_theme() } on.exit(toastui::reset_grid_theme()) if (is.null(options)) options <- list() options$height <- 550 options$minBodyHeight <- 400 options$data <- data options$theme <- "default" options$colwidths <- "guess" options$guess_colwidths_opts <- list(min_width = 90, max_width = 400, mul = 1, add = 10) if (isTRUE(show_classes)) options$summary <- construct_col_summary(data) datatable <- rlang::exec(toastui::datagrid, !!!options) datatable <- toastui::grid_columns(datatable, className = "font-monospace") if (is.null(session)) return(datatable) if (identical(type, "winbox")) { wb_options <- if (is.null(args$wbOptions)) { shinyWidgets::wbOptions( height = "600px", width = width, modal = TRUE ) } else { modifyList( shinyWidgets::wbOptions( height = "600px", width = width, modal = TRUE ), args$wbOptions ) } wb_controls <- if (is.null(args$wbControls)) { shinyWidgets::wbControls() } else { args$wbControls } shinyWidgets::WinBox( title = title, ui = datatable, options = wb_options, controls = wb_controls, padding = "0 5px", session = session ) } else if (identical(type, "popup")) { show_alert( title = NULL, text = tags$div( if (!is.null(title)) { tagList( tags$h3(title), tags$hr() ) }, style = "color: #000 !important;", datatable ), closeOnClickOutside = TRUE, showCloseButton = TRUE, btn_labels = NA, html = TRUE, width = width, session = session ) } else { showModal(modalDialog( title = tagList( button_close_modal(), title ), tags$div( style = css(minHeight = validateCssUnit(options$height)), toastui::renderDatagrid2(datatable) ), size = "xl", footer = NULL, easyClose = TRUE ), session = session) } } # nocov end ================================================ FILE: R/update-factor.R ================================================ #' @title Module to Reorder the Levels of a Factor Variable #' #' @description #' This module contain an interface to reorder the levels of a factor variable. #' #' #' @param id Module ID. #' #' @return A [shiny::reactive()] function returning the data. #' @export #' #' @importFrom shiny NS fluidRow tagList column actionButton #' @importFrom shinyWidgets virtualSelectInput prettyCheckbox #' @importFrom toastui datagridOutput #' @importFrom htmltools tags #' #' @name update-factor #' #' @example examples/update_factor.R update_factor_ui <- function(id) { ns <- NS(id) tagList( tags$style( ".tui-grid-row-header-draggable span {width: 3px !important; height: 3px !important;}" ), fluidRow( column( width = 6, virtualSelectInput( inputId = ns("variable"), label = i18n("Factor variable to reorder:"), choices = NULL, width = "100%", zIndex = 50 ) ), column( width = 3, class = "d-flex align-items-end", actionButton( inputId = ns("sort_levels"), label = tagList( ph("sort-ascending"), i18n("Sort by levels") ), class = "btn-outline-primary mb-3", width = "100%" ) ), column( width = 3, class = "d-flex align-items-end", actionButton( inputId = ns("sort_occurrences"), label = tagList( ph("sort-ascending"), i18n("Sort by count") ), class = "btn-outline-primary mb-3", width = "100%" ) ) ), datagridOutput(ns("grid")), tags$div( class = "float-end", prettyCheckbox( inputId = ns("new_var"), label = i18n("Create a new variable (otherwise replaces the one selected)"), value = FALSE, status = "primary", outline = TRUE, inline = TRUE ), actionButton( inputId = ns("create"), label = tagList(ph("arrow-clockwise"), i18n("Update factor variable")), class = "btn-outline-primary" ) ), tags$div(class = "clearfix") ) } #' @param data_r A [shiny::reactive()] function returning a `data.frame`. #' #' @export #' #' @importFrom shiny moduleServer observeEvent reactive reactiveValues req bindEvent isTruthy updateActionButton #' @importFrom shinyWidgets updateVirtualSelect #' @importFrom toastui renderDatagrid datagrid grid_columns grid_colorbar #' #' @rdname update-factor update_factor_server <- function(id, data_r = reactive(NULL)) { moduleServer( id, function(input, output, session) { rv <- reactiveValues(data = NULL, data_grid = NULL) bindEvent(observe({ data <- data_r() rv$data <- data vars_factor <- vapply(data, is.factor, logical(1)) vars_factor <- names(vars_factor)[vars_factor] updateVirtualSelect( inputId = "variable", choices = vars_factor, selected = if (isTruthy(input$variable)) input$variable else vars_factor[1] ) }), data_r(), input$hidden) observeEvent(input$variable, { data <- req(data_r()) variable <- req(input$variable) grid <- as.data.frame(table(data[[variable]])) rv$data_grid <- grid }) observeEvent(input$sort_levels, { if (input$sort_levels %% 2 == 1) { decreasing <- FALSE label <- tagList( ph("sort-descending"), "Sort Levels" ) } else { decreasing <- TRUE label <- tagList( ph("sort-ascending"), "Sort Levels" ) } updateActionButton(inputId = "sort_levels", label = as.character(label)) rv$data_grid <- rv$data_grid[order(rv$data_grid[[1]], decreasing = decreasing), ] }) observeEvent(input$sort_occurrences, { if (input$sort_occurrences %% 2 == 1) { decreasing <- FALSE label <- tagList( ph("sort-descending"), i18n("Sort count") ) } else { decreasing <- TRUE label <- tagList( ph("sort-ascending"), i18n("Sort count") ) } updateActionButton(inputId = "sort_occurrences", label = as.character(label)) rv$data_grid <- rv$data_grid[order(rv$data_grid[[2]], decreasing = decreasing), ] }) output$grid <- renderDatagrid({ req(rv$data_grid) gridTheme <- getOption("datagrid.theme") if (length(gridTheme) < 1) { apply_grid_theme() } on.exit(toastui::reset_grid_theme()) grid <- datagrid( data = rv$data_grid, draggable = TRUE, sortable = FALSE, data_as_input = TRUE ) grid <- grid_columns( grid, columns = c("Var1", "Freq"), header = c(i18n("Levels"), i18n("Count")) ) grid <- grid_colorbar( grid, column = "Freq", label_outside = TRUE, label_width = "30px", background = "#D8DEE9", bar_bg = get_primary_color(), from = c(0, max(rv$data_grid$Freq) + 1) ) grid }) data_updated_r <- reactive({ data <- req(data_r()) variable <- req(input$variable) grid <- req(input$grid_data) name_var <- if (isTRUE(input$new_var)) { paste0(variable, "_updated") } else { variable } data[[name_var]] <- factor( as.character(data[[variable]]), levels = grid[["Var1"]] ) data }) data_returned_r <- observeEvent(input$create, { rv$data <- data_updated_r() }) return(reactive(rv$data)) } ) } #' @inheritParams shiny::modalDialog #' @export #' #' @importFrom shiny showModal modalDialog textInput #' @importFrom htmltools tagList #' #' @rdname update-factor modal_update_factor <- function(id, title = i18n("Update levels of a factor"), easyClose = TRUE, size = "l", footer = NULL) { ns <- NS(id) showModal(modalDialog( title = tagList(title, button_close_modal()), update_factor_ui(id), tags$div( style = "display: none;", textInput(inputId = ns("hidden"), label = NULL, value = genId()) ), easyClose = easyClose, size = size, footer = footer )) } #' @inheritParams shinyWidgets::WinBox #' @export #' #' @importFrom shinyWidgets WinBox wbOptions wbControls #' @importFrom htmltools tagList #' @rdname create-column winbox_update_factor <- function(id, title = i18n("Update levels of a factor"), options = shinyWidgets::wbOptions(), controls = shinyWidgets::wbControls()) { ns <- NS(id) WinBox( title = title, ui = tagList( update_factor_ui(id), tags$div( style = "display: none;", textInput(inputId = ns("hidden"), label = NULL, value = genId()) ) ), options = modifyList( shinyWidgets::wbOptions(height = "615px", modal = TRUE), options ), controls = controls, auto_height = FALSE ) } ================================================ FILE: R/update-variables.R ================================================ #' Select, rename and convert variables #' #' @param id Module id. See [shiny::moduleServer()]. #' @param title Module's title, if `TRUE` use the default title, #' use \code{NULL} for no title or a `shiny.tag` for a custom one. #' #' @return A [shiny::reactive()] function returning the updated data. #' @export #' #' @name update-variables #' #' @importFrom shiny uiOutput actionButton icon #' @importFrom htmltools tagList tags #' @importFrom shinyWidgets html_dependency_pretty textInputIcon dropMenu #' #' @example examples/variables.R update_variables_ui <- function(id, title = TRUE) { ns <- NS(id) if (isTRUE(title)) { title <- tags$h4( i18n("Update & select variables"), class = "datamods-title" ) } tags$div( class = "datamods-update", html_dependency_pretty(), title, tags$div( style = "min-height: 25px;", tags$div( uiOutput(outputId = ns("data_info"), inline = TRUE), tagAppendAttributes( dropMenu( placement = "bottom-end", actionButton( inputId = ns("settings"), label = phosphoricons::ph("gear"), class = "pull-right float-right" ), textInputIcon( inputId = ns("format"), label = i18n("Date format:"), value = "%Y-%m-%d", icon = list(phosphoricons::ph("clock")) ), textInputIcon( inputId = ns("origin"), label = i18n("Date to use as origin to convert date/datetime:"), value = "1970-01-01", icon = list(phosphoricons::ph("calendar")) ), textInputIcon( inputId = ns("dec"), label = i18n("Decimal separator:"), value = ".", icon = list("0.00") ) ), style = "display: inline;" ) ), tags$br(), toastui::datagridOutput(outputId = ns("table")) ), tags$br(), tags$div( id = ns("update-placeholder"), alert( id = ns("update-result"), status = "info", phosphoricons::ph("info"), i18n(paste( "Select, rename and convert variables in table above,", "then apply changes by clicking button below." )) ) ), actionButton( inputId = ns("validate"), label = tagList( phosphoricons::ph("arrow-circle-right", title = i18n("Apply changes")), i18n("Apply changes") ), width = "100%" ) ) } #' @export #' #' @param id Module's ID #' @param data a \code{data.frame} or a \code{reactive} function returning a \code{data.frame}. #' @param height Height for the table. #' @param return_data_on_init Return initial data when module is called. #' @param try_silent logical: should the report of error messages be suppressed? #' #' @rdname update-variables #' #' @importFrom shiny moduleServer reactiveValues reactive renderUI reactiveValuesToList validate need reactiveVal #' @importFrom rlang call2 expr #' @importFrom data.table setorderv update_variables_server <- function(id, data, height = NULL, return_data_on_init = FALSE, try_silent = FALSE) { moduleServer( id = id, module = function(input, output, session) { ns <- session$ns updated_data <- reactiveValues(x = NULL) data_r <- reactive({ if (is.reactive(data)) { data() } else { data } }) output$data_info <- renderUI({ shiny::req(data_r()) data <- data_r() sprintf(i18n("Data has %s observations and %s variables."), nrow(data), ncol(data)) }) variables_r <- reactive({ shiny::validate( shiny::need(data(), i18n("No data to display.")) ) data <- data_r() if (isTRUE(return_data_on_init)) { updated_data$x <- data } else { updated_data$x <- NULL } summary_vars(data) }) output$table <- toastui::renderDatagrid({ req(variables_r()) variables <- variables_r() update_variables_datagrid( variables, height = height, selectionId = ns("row_selected"), buttonId = "validate" ) }) observeEvent(input$validate, { updated_data$list_rename <- NULL updated_data$list_select <- NULL updated_data$list_mutate <- NULL data <- data_r() new_selections <- input$row_selected if (length(new_selections) < 1) new_selections <- seq_along(data) data_inputs <- as.data.table(input$table_data) setorderv(data_inputs, "rowKey") old_names <- data_inputs$name new_names <- data_inputs$name_toset new_names[new_names == "Enter new name"] <- NA new_names[is.na(new_names)] <- old_names[is.na(new_names)] new_names[new_names == ""] <- old_names[new_names == ""] new_classes <- data_inputs$class_toset new_classes[new_classes == "Select new class"] <- NA data_sv <- variables_r() vars_to_change <- get_vars_to_convert(data_sv, setNames(as.list(new_classes), old_names)) res_update <- try({ # convert if (nrow(vars_to_change) > 0) { data <- convert_to( data = data, variable = vars_to_change$name, new_class = vars_to_change$class_to_set, origin = input$origin, format = input$format, dec = input$dec ) } list_mutate <- attr(data, "code_03_convert") # rename list_rename <- setNames( as.list(old_names), unlist(new_names, use.names = FALSE) ) list_rename <- list_rename[names(list_rename) != unlist(list_rename, use.names = FALSE)] names(data) <- unlist(new_names, use.names = FALSE) # select list_select <- setdiff(names(data), names(data)[new_selections]) data <- data[, new_selections, drop = FALSE] }, silent = try_silent) if (inherits(res_update, "try-error")) { insert_error(selector = "update") } else { insert_alert( selector = ns("update"), status = "success", tags$b(phosphoricons::ph("check"), i18n("Data successfully updated!")) ) updated_data$x <- data updated_data$list_rename <- list_rename updated_data$list_select <- list_select updated_data$list_mutate <- list_mutate } }, ignoreNULL = TRUE, ignoreInit = TRUE) return(reactive({ data <- updated_data$x code <- list() if (!is.null(data) && isTruthy(updated_data$list_mutate) && length(updated_data$list_mutate) > 0) { code <- c(code, list(call2("mutate", !!!updated_data$list_mutate))) } if (!is.null(data) && isTruthy(updated_data$list_rename) && length(updated_data$list_rename) > 0) { code <- c(code, list(call2("rename", !!!updated_data$list_rename))) } if (!is.null(data) && isTruthy(updated_data$list_select) && length(updated_data$list_select) > 0) { code <- c(code, list(expr(select(-any_of(c(!!!updated_data$list_select)))))) } if (length(code) > 0) { attr(data, "code") <- Reduce( f = function(x, y) expr(!!x %>% !!y), x = code ) } return(data) })) } ) } # utils ------------------------------------------------------------------- #' Get variables classes from a \code{data.frame} #' #' @param data a \code{data.frame} #' #' @return a \code{character} vector as same length as number of variables #' @noRd #' #' @examples #' #' get_classes(mtcars) get_classes <- function(data) { classes <- lapply( X = data, FUN = function(x) { paste(class(x), collapse = ", ") } ) unlist(classes, use.names = FALSE) } #' Get count of unique values in variables of \code{data.frame} #' #' @param data a \code{data.frame} #' #' @return a \code{numeric} vector as same length as number of variables #' @noRd #' #' @importFrom data.table uniqueN #' #' @examples #' get_n_unique(mtcars) get_n_unique <- function(data) { u <- lapply(data, FUN = function(x) { if (is.atomic(x)) { uniqueN(x) } else { NA_integer_ } }) unlist(u, use.names = FALSE) } #' Add padding 0 to a vector #' #' @param x a \code{vector} #' #' @return a \code{character} vector #' @noRd #' #' @examples #' #' pad0(1:10) #' pad0(c(1, 15, 150, NA)) pad0 <- function(x) { NAs <- which(is.na(x)) x <- formatC(x, width = max(nchar(as.character(x)), na.rm = TRUE), flag = "0") x[NAs] <- NA x } #' Variables summary #' #' @param data a \code{data.frame} #' #' @return a \code{data.frame} #' @noRd #' #' @examples #' #' summary_vars(iris) #' summary_vars(mtcars) summary_vars <- function(data) { data <- as.data.frame(data) datsum <- data.frame( name = names(data), class = get_classes(data), n_missing = unname(colSums(is.na(data))), stringsAsFactors = FALSE ) datsum$p_complete <- 1 - datsum$n_missing / nrow(data) datsum$n_unique <- get_n_unique(data) datsum } add_var_toset <- function(data, var_name, default = "") { datanames <- names(data) datanames <- append( x = datanames, values = paste0(var_name, "_toset"), after = which(datanames == var_name) ) data[[paste0(var_name, "_toset")]] <- default data[, datanames] } #' @importFrom toastui datagrid grid_columns grid_format grid_style_column #' grid_style_column grid_editor grid_editor_opts grid_selection_row update_variables_datagrid <- function(data, height = NULL, selectionId = NULL, buttonId = NULL) { data <- add_var_toset(data, "name", "Enter new name") data <- add_var_toset(data, "class", "Select new class") gridTheme <- getOption("datagrid.theme") if (length(gridTheme) < 1) { apply_grid_theme() } on.exit(toastui::reset_grid_theme()) grid <- datagrid( data = data, theme = "default", colwidths = NULL ) grid <- grid_columns( grid = grid, columns = c("name", "name_toset", "class", "class_toset", "n_missing", "p_complete", "n_unique"), header = c("Name", "New name", "Class", "New class", "Missing values", "Complete obs.", "Unique values"), minWidth = 120 ) grid <- grid_format( grid = grid, "p_complete", formatter = toastui::JS("function(obj) {return (obj.value*100).toFixed(0) + '%';}") ) grid <- grid_style_column( grid = grid, column = "name_toset", fontStyle = "italic" ) grid <- grid_style_column( grid = grid, column = "class_toset", fontStyle = "italic" ) grid <- grid_editor( grid = grid, column = "name_toset", type = "text" ) grid <- grid_editor( grid = grid, column = "class_toset", type = "select", choices = c("Select new class", "character", "factor", "numeric", "integer", "date", "datetime") ) grid <- grid_editor_opts( grid = grid, editingEvent = "click", actionButtonId = NULL, session = NULL ) grid <- grid_selection_row( grid = grid, inputId = selectionId, type = "checkbox", return = "index" ) return(grid) } #' Convert a variable to specific new class #' #' @param data A \code{data.frame} #' @param variable Name of the variable to convert #' @param new_class Class to set #' @param ... Other arguments passed on to methods. #' #' @return A \code{data.frame} #' @noRd #' #' @importFrom utils type.convert #' @importFrom rlang sym expr #' #' @examples #' dat <- data.frame( #' v1 = month.name, #' v2 = month.abb, #' v3 = 1:12, #' v4 = as.numeric(Sys.Date() + 0:11), #' v5 = as.character(Sys.Date() + 0:11), #' v6 = as.factor(c("a", "a", "b", "a", "b", "a", "a", "b", "a", "b", "b", "a")), #' v7 = as.character(11:22), #' stringsAsFactors = FALSE #' ) #' #' str(dat) #' #' str(convert_to(dat, "v3", "character")) #' str(convert_to(dat, "v6", "character")) #' str(convert_to(dat, "v7", "numeric")) #' str(convert_to(dat, "v4", "date", origin = "1970-01-01")) #' str(convert_to(dat, "v5", "date")) #' #' str(convert_to(dat, c("v1", "v3"), c("factor", "character"))) #' #' str(convert_to(dat, c("v1", "v3", "v4"), c("factor", "character", "date"), origin = "1970-01-01")) #' convert_to <- function(data, variable, new_class = c("character", "factor", "numeric", "integer", "date", "datetime"), ...) { new_class <- match.arg(new_class, several.ok = TRUE) stopifnot(length(new_class) == length(variable)) args <- list(...) if (length(variable) > 1) { for (i in seq_along(variable)) { data <- convert_to(data, variable[i], new_class[i], ...) } return(data) } if (identical(new_class, "character")) { data[[variable]] <- as.character(x = data[[variable]], ...) attr(data, "code_03_convert") <- c( attr(data, "code_03_convert"), setNames(list(expr(as.character(!!sym(variable)))), variable) ) } else if (identical(new_class, "factor")) { data[[variable]] <- as.factor(x = data[[variable]]) attr(data, "code_03_convert") <- c( attr(data, "code_03_convert"), setNames(list(expr(as.factor(!!sym(variable)))), variable) ) } else if (identical(new_class, "numeric")) { data[[variable]] <- as.numeric(type.convert(data[[variable]], as.is = TRUE, ...)) attr(data, "code_03_convert") <- c( attr(data, "code_03_convert"), setNames(list(expr(as.numeric(!!sym(variable)))), variable) ) } else if (identical(new_class, "integer")) { data[[variable]] <- as.integer(x = data[[variable]], ...) attr(data, "code_03_convert") <- c( attr(data, "code_03_convert"), setNames(list(expr(as.integer(!!sym(variable)))), variable) ) } else if (identical(new_class, "date")) { data[[variable]] <- as.Date(x = data[[variable]], ...) attr(data, "code_03_convert") <- c( attr(data, "code_03_convert"), setNames(list(expr(as.Date(!!sym(variable), origin = !!args$origin))), variable) ) } else if (identical(new_class, "datetime")) { data[[variable]] <- as.POSIXct(x = data[[variable]], ...) attr(data, "code_03_convert") <- c( attr(data, "code_03_convert"), setNames(list(expr(as.POSIXct(!!sym(variable)))), variable) ) } return(data) } #' Get variable(s) to convert #' #' @param vars Output of [summary_vars()] #' @param classes_input List of inputs containing new classes #' #' @return a `data.table`. #' @noRd #' #' @importFrom data.table data.table as.data.table #' #' @examples #' # 2 variables to convert #' new_classes <- list( #' "Sepal.Length" = "numeric", #' "Sepal.Width" = "numeric", #' "Petal.Length" = "character", #' "Petal.Width" = "numeric", #' "Species" = "character" #' ) #' get_vars_to_convert(summary_vars(iris), new_classes) #' #' #' # No changes #' new_classes <- list( #' "Sepal.Length" = "numeric", #' "Sepal.Width" = "numeric", #' "Petal.Length" = "numeric", #' "Petal.Width" = "numeric", #' "Species" = "factor" #' ) #' get_vars_to_convert(summary_vars(iris), new_classes) #' #' # Not set = NA or "" #' new_classes <- list( #' "Sepal.Length" = NA, #' "Sepal.Width" = NA, #' "Petal.Length" = NA, #' "Petal.Width" = NA, #' "Species" = NA #' ) #' get_vars_to_convert(summary_vars(iris), new_classes) #' #' # Set for one var #' new_classes <- list( #' "Sepal.Length" = "", #' "Sepal.Width" = "", #' "Petal.Length" = "", #' "Petal.Width" = "", #' "Species" = "character" #' ) #' get_vars_to_convert(summary_vars(iris), new_classes) #' #' new_classes <- list( #' "mpg" = "character", #' "cyl" = "numeric", #' "disp" = "character", #' "hp" = "numeric", #' "drat" = "character", #' "wt" = "character", #' "qsec" = "numeric", #' "vs" = "character", #' "am" = "numeric", #' "gear" = "character", #' "carb" = "integer" #' ) #' get_vars_to_convert(summary_vars(mtcars), new_classes) get_vars_to_convert <- function(vars, classes_input) { vars <- as.data.table(vars) classes_input <- data.table( name = names(classes_input), class_to_set = unlist(classes_input, use.names = FALSE), stringsAsFactors = FALSE ) classes_input <- classes_input[!is.na(class_to_set) & class_to_set != ""] classes_df <- merge(x = vars, y = classes_input, by = "name") classes_df <- classes_df[!is.na(class_to_set)] classes_df[class != class_to_set] } ================================================ FILE: R/utils-shiny.R ================================================ #' @importFrom htmltools htmlDependency #' @importFrom utils packageVersion html_dependency_datamods <- function() { htmlDependency( name = "datamods", version = packageVersion("datamods"), src = list(href = "datamods", file = "assets"), package = "datamods", script = "js/datamods.js", stylesheet = "css/datamods.css" ) } #' Enable or disable a widget from server #' #' @param inputId Widget's inputId. #' @param enable Enable or disable the input. #' @param session Shiny session. #' #' @noRd toggle_widget <- function(inputId, enable = TRUE, session = shiny::getDefaultReactiveDomain()) { session$sendCustomMessage( type = "datamods-toggleWidget", message = list(id = session$ns(inputId), enable = enable) ) } #' Insert an alert into a placeholder in UI #' #' @param selector Id for alert, the placeholder maust have \code{"-placeholder"} suffix. #' @param ... Arguments passed to \code{shinyWidgets::alert}. #' #' @return No value. #' @noRd #' #' @importFrom shiny removeUI insertUI #' @importFrom shinyWidgets alert #' insert_alert <- function(selector, ...) { removeUI(selector = paste0("#", selector, "-result")) insertUI( selector = paste0("#", selector, "-placeholder"), ui = alert( id = paste0(selector, "-result"), ... ) ) } showUI <- function(selector = NULL, inline = FALSE, id = NULL, session = shiny::getDefaultReactiveDomain()) { if (!is.null(id)) id <- session$ns(id) session$sendCustomMessage( type = "datamods-showUI", message = dropNulls(list( selector = selector, inline = inline, id = id )) ) } hideUI <- function(selector = NULL, inline = FALSE, id = NULL, session = shiny::getDefaultReactiveDomain()) { if (!is.null(id)) id <- session$ns(id) session$sendCustomMessage( type = "datamods-hideUI", message = dropNulls(list( selector = selector, inline = inline, id = id )) ) } enable_tab <- function(id, value, session = shiny::getDefaultReactiveDomain()) { session$sendCustomMessage( type = "datamods-enableTab", message = list(id = session$ns(id), value = value) ) } disable_tab <- function(id, value, session = shiny::getDefaultReactiveDomain()) { session$sendCustomMessage( type = "datamods-disableTab", message = list(id = session$ns(id), value = value) ) } #' @importFrom htmltools doRenderTags update_tab_label <- function(id, value, label, session = shiny::getDefaultReactiveDomain()) { session$sendCustomMessage( type = "datamods-updateTabLabel", message = list(id = session$ns(id), value = value, label = doRenderTags(label)) ) } #' @importFrom htmltools tagList tags #' @importFrom shiny icon getDefaultReactiveDomain make_success_alert <- function(data, trigger_return, btn_show_data, extra = NULL, session = shiny::getDefaultReactiveDomain()) { if (identical(trigger_return, "button")) { success_message <- tagList( tags$b(phosphoricons::ph("check", weight = "bold"), i18n("Data ready to be imported!")), sprintf( i18n("data has %s obs. of %s variables."), nrow(data), ncol(data) ), extra ) } else { success_message <- tagList( tags$b(phosphoricons::ph("check", weight = "bold"), i18n("Data successfully imported!")), sprintf( i18n("data has %s obs. of %s variables."), nrow(data), ncol(data) ), extra ) } if (isTRUE(btn_show_data)) { success_message <- tagList( success_message, tags$br(), actionLink( inputId = session$ns("see_data"), label = tagList(phosphoricons::ph("table"), i18n("click to see data")) ) ) } return(success_message) } insert_error <- function(mssg = i18n("Something went wrong..."), selector = "import", session = shiny::getDefaultReactiveDomain()) { insert_alert( selector = session$ns(selector), status = "danger", tags$b(phosphoricons::ph("warning"), i18n("Ooops")), mssg ) } #' @importFrom htmltools tagList tags doRenderTags help_popup <- function(text) { tagList( tags$span( phosphoricons::ph("question", title = i18n("Help")), `data-toggle` = "popover", `data-trigger` = "focus", title = i18n("Help"), `data-html` = "true", `data-content` = htmltools::doRenderTags(text), tabindex = "0", role = "button" ), tags$script( "$(function () { $(\'[data-toggle=\"popover\"]\').popover({container: 'body'}); })" ) ) } #' @importFrom shiny actionButton icon getDefaultReactiveDomain button_import <- function(session = shiny::getDefaultReactiveDomain()) { actionButton( inputId = session$ns("confirm"), label = tagList( phosphoricons::ph("arrow-circle-right", title = i18n("Import data")), i18n("Import data") ), width = "100%", disabled = "disabled", class = "btn-primary", `aria-label` = i18n("Import data") ) } button_close_modal <- function() { tags$button( phosphoricons::ph("x", title = i18n("Close"), height = "2em"), class = "btn btn-link", style = css(border = "0 none", position = "absolute", top = "5px", right = "5px"), `data-dismiss` = "modal", `data-bs-dismiss` = "modal", `aria-label` = i18n("Close") ) } #' @importFrom bslib bs_current_theme is_bs_theme bs_get_variables get_primary_color <- function() { theme <- bslib::bs_current_theme() if (!bslib::is_bs_theme(theme)) { return("#112466") } primary <- bslib::bs_get_variables(theme, "primary") unname(primary) } ================================================ FILE: R/utils.R ================================================ `%||%` <- function(x, y) { if (is.null(x)) y else x } dropNulls <- function(x) { x[!vapply(x, is.null, FUN.VALUE = logical(1))] } dropNullsOrEmpty <- function(x) { x[!vapply(x, nullOrEmpty, FUN.VALUE = logical(1))] } nullOrEmpty <- function(x) { is.null(x) || length(x) == 0 || x == "" } #' @importFrom data.table .SD dropListColumns <- function(x) { type_col <- vapply( X = x, FUN = typeof, FUN.VALUE = character(1), USE.NAMES = FALSE ) if (inherits(x, "data.table")) { x[, .SD, .SDcols = type_col != "list"] } else { x[, type_col != "list", drop = FALSE] } } #' Search for object with specific class in an environment #' #' @param what a class to look for #' @param env An environment #' #' @return Character vector of the names of objects, NULL if none #' @noRd #' #' @examples #' #' # NULL if no data.frame #' search_obj("data.frame") #' #' library(ggplot2) #' data("mpg") #' search_obj("data.frame") #' #' #' gg <- ggplot() #' search_obj("ggplot") #' search_obj <- function(what = "data.frame", env = globalenv()) { all <- ls(name = env) objs <- lapply( X = all, FUN = function(x) { if (inherits(get(x, envir = env), what = what)) { x } else { NULL } } ) objs <- unlist(objs) if (length(objs) == 1 && objs == "") { NULL } else { objs } } #' @importFrom data.table as.data.table #' @importFrom tibble as_tibble as_out <- function(x, return_class = c("data.frame", "data.table", "tbl_df", "raw")) { if (is.null(x)) return(NULL) return_class <- match.arg(return_class) if (identical(return_class, "raw")) return(x) is_sf <- inherits(x, "sf") x <- if (identical(return_class, "data.frame")) { as.data.frame(x) } else if (identical(return_class, "data.table")) { as.data.table(x) } else { as_tibble(x) } if (is_sf) class(x) <- c("sf", class(x)) return(x) } genId <- function(bytes = 12) { paste(format(as.hexmode(sample(256, bytes, replace = TRUE) - 1), width = 2), collapse = "") } makeId <- function(x) { if (length(x) < 1) return(NULL) x <- as.character(x) x <- lapply(X = x, FUN = function(y) { paste(as.character(charToRaw(y)), collapse = "") }) x <- unlist(x, use.names = FALSE) make.unique(x, sep = "_") } `%inT%` <- function(x, table) { if (!is.null(table) && ! "" %in% table) { x %in% table } else { rep_len(TRUE, length(x)) } } `%inF%` <- function(x, table) { if (!is.null(table) && ! "" %in% table) { x %in% table } else { rep_len(FALSE, length(x)) } } #' @importFrom utils hasName header_with_classes <- function(data) { function(value) { if (!hasName(data, value)) return("") classes <- tags$div( style = "font-style: italic; font-weight: normal; font-size: small;", get_classes(data[, value, drop = FALSE]) ) tags$div(title = value, value, classes) } } split_char <- function(x, split = ",") { if (is.null(x)) return(NULL) unlist(strsplit(x, split = split)) } apply_grid_theme <- function() { toastui::set_grid_theme( cell.normal.background = "#FFF", cell.normal.border = "#D8DEE9", cell.normal.showVerticalBorder = TRUE, cell.normal.showHorizontalBorder = TRUE, cell.header.border = "#D8DEE9", area.header.border = "#4C566A", cell.summary.border = "#D8DEE9", cell.summary.showVerticalBorder = TRUE, cell.summary.showHorizontalBorder = TRUE ) } ================================================ FILE: R/validation.R ================================================ #' @title Validation module #' #' @description Check that a dataset respect some validation expectations. #' #' @param id Module's ID. #' @param display Display validation results in a dropdown menu #' by clicking on a button or display results directly in interface. #' @param max_height Maximum height for validation results element, useful if you have many rules. #' @param ... Arguments passed to \code{actionButton} or \code{uiOutput} depending on display mode, #' you cannot use \code{inputId}/\code{outputId}, \code{label} or \code{icon} (button only). #' #' @return #' * UI: HTML tags that can be included in shiny's UI #' * Server: a \code{list} with two slots: #' + **status**: a \code{reactive} function returning the best status available between \code{"OK"}, \code{"Failed"} or \code{"Error"}. #' + **details**: a \code{reactive} function returning a \code{list} with validation details. #' @export #' #' @importFrom shiny NS actionButton icon uiOutput #' @importFrom htmltools tagList validateCssUnit #' @importFrom shinyWidgets dropMenu #' #' @rdname validation #' #' @example examples/validation.R validation_ui <- function(id, display = c("dropdown", "inline"), max_height = NULL, ...) { ns <- NS(id) display <- match.arg(display) max_height <- if (!is.null(max_height)) { paste0("overflow-y: auto; max-height:", validateCssUnit(max_height), ";") } if (identical(display, "dropdown")) { ui <- dropMenu( actionButton( inputId = ns("menu"), label = tagList( phosphoricons::ph("caret-down", weight = "fill", title = i18n("Validation:")), i18n("Validation:") ), ... ), uiOutput( outputId = ns("results"), style = "width: 300px;", style = max_height ) ) } else { ui <- uiOutput( outputId = ns("results"), ..., style = max_height ) } tagList( ui, html_dependency_datamods() ) } #' @export #' #' @param data a \code{reactive} function returning a \code{data.frame}. #' @param n_row,n_col A one-sided formula to check number of rows and columns respectively, see below for examples. #' @param n_row_label,n_col_label Text to be displayed with the result of the check for number of rows/columns. #' @param btn_label Label for the dropdown button, will be followed by validation result. #' @param rules An object of class \code{validator} created with \code{validate::validator}. #' @param bs_version Bootstrap version used, it may affect rendering, especially status badges. #' #' @rdname validation #' #' @importFrom shiny moduleServer reactiveValues observeEvent updateActionButton renderUI reactive #' @importFrom htmltools doRenderTags tags tagList validation_server <- function(id, data, n_row = NULL, n_col = NULL, n_row_label = i18n("Valid number of rows"), n_col_label = i18n("Valid number of columns"), btn_label = i18n("Dataset validation:"), rules = NULL, bs_version = 3) { moduleServer( id = id, module = function(input, output, session) { valid_ui <- reactiveValues(x = NULL) valid_rv <- reactiveValues(status = NULL, details = NULL) observeEvent(data(), { to_validate <- data() valid_dims <- check_data(to_validate, n_row = n_row, n_col = n_col) if (all(c(valid_dims$nrows, valid_dims$ncols))) { valid_status <- "OK" } else { valid_status <- "Failed" } valid_results <- lapply( X = c("nrows", "ncols"), FUN = function(x) { if (is.null(valid_dims[[x]])) return(NULL) label <- switch( x, "nrows" = n_row_label, "ncols" = n_col_label ) list( status = ifelse(valid_dims[[x]], "OK", "Failed"), label = paste0("", label, "") ) } ) if (!is.null(rules) && inherits(rules, "validator")) { validate_results <- validate::confront(to_validate, rules) validate_results <- validate::summary(validate_results) validate_results <- merge( x = validate_results, y = validate::as.data.frame(rules), by = "name" ) # validate_results <- format_validate(validate_results) if (any(validate_results$error)) { valid_status <- "Error" } else if (any(validate_results$fails > 0)) { valid_status <- "Failed" } valid_results <- c( valid_results, format_validate(validate_results) ) } if (identical(valid_status, "OK")) { label <- doRenderTags(tagList( btn_label, tags$span( class = badge_class(bs_version, "success"), phosphoricons::ph("check", weight = "bold", title = i18n("OK")), i18n("OK") ) )) } else if (identical(valid_status, "Failed")) { label <- doRenderTags(tagList( btn_label, tags$span( class = badge_class(bs_version, "warning"), phosphoricons::ph("warning", weight = "bold", title = i18n("Failed")), i18n("Failed") ) )) } else if (identical(valid_status, "Error")) { label <- doRenderTags(tagList( btn_label, tags$span( class = badge_class(bs_version, "danger"), phosphoricons::ph("x", weight = "bold", title = i18n("Error")), i18n("Error") ) )) } updateActionButton(session = session, inputId = "menu", label = label) valid_results <- dropNulls(valid_results) total <- unlist(lapply(valid_results, `[[`, "status")) header <- tags$div( class = "datamods-validation-results", tags$div( class = "datamods-validation-summary", style = "border-right: 1px solid #e6e6e6;", tags$span( class = badge_class(bs_version, "success"), i18n("OK"), tags$span(sum(total == "OK"), class = "datamods-validation-item") ) ), tags$div( class = "datamods-validation-summary", style = "border-right: 1px solid #e6e6e6;", tags$span( class = badge_class(bs_version, "warning"), i18n("Failed"), tags$span(sum(total == "Failed"), class = "datamods-validation-item") ) ), tags$div( class = "datamods-validation-summary", tags$span( class = badge_class(bs_version, "danger"), i18n("Error"), tags$span(sum(total == "Error"), class = "datamods-validation-item") ) ) ) valid_ui$x <- tagList( header, tags$br(), make_validation_alerts(valid_results) ) valid_rv$status <- valid_status valid_rv$details <- valid_results }) output$results <- renderUI({ valid_ui$x }) return(list( status = reactive(valid_rv$status), details = reactive(valid_rv$details) )) } ) } #' @importFrom rlang as_label as_function enquo check_fun <- function(fun, what) { label <- as_label(enquo(what)) if (inherits(fun, "formula")) { fun <- as_function(fun) result <- try(fun(what)) if (inherits(result, "try-error") | !is.logical(result)) { warning("Checking ", label, " must return a logical", call. = FALSE) return(FALSE) } } else { result <- NULL } return(result) } check_data <- function(data, n_row = NULL, n_col = NULL) { list( nrows = check_fun(n_row, nrow(data)), ncols = check_fun(n_col, ncol(data)) ) } #' @importFrom shiny icon #' @importFrom shinyWidgets alert #' @importFrom htmltools HTML make_validation_alerts <- function(.list) { lapply( X = .list, FUN = function(x) { icon <- switch( x$status, "OK" = phosphoricons::ph("check", title = i18n("OK")), "Failed" = phosphoricons::ph("warning", title = i18n("Failed")), "Error" = phosphoricons::ph("x", title = i18n("Error")) ) status <- switch( x$status, "OK" = "success", "Failed" = "warning", "Error" = "danger", "info" ) alert( icon, HTML(x$label), status = status, style = "margin-bottom: 10px; padding: 10px;" ) } ) } format_validate <- function(data) { lapply( X = seq_len(nrow(data)), FUN = function(i) { res <- data[i, ] if (isTRUE(res$error)) { status <- "Error" } else { if (res$fails > 0) { status <- "Failed" } else { status <- "OK" } } if (!is.null(res$label)) { label <- paste0("", res$label, "") if (!is.null(res$description) && nzchar(res$description)) { label <- paste(label, res$description, sep = ": ") } } else { label <- res$name } if (identical(status, "Failed")) { label <- paste0(label, "| failed: ", res$fails, " / ", res$items) } list( status = status, label = label, summary = res ) } ) } badge_class <- function(bs_version, status) { if (!is.numeric(bs_version)) stop("`bs_version` must be a numeric.") if (bs_version <= 3) { paste0("label label-", status) } else { sprintf("badge badge-%1$s bg-%1$s", status) } } ================================================ FILE: R/zzz.R ================================================ utils::globalVariables(c( "%>%", "filter", "group_by", "label", "translation", ".datamods_edit_update", ".datamods_edit_delete", ".datamods_id", "..var_edit", "..vars_datamods_edit", "select", "any_of", "rename", "class_to_set" )) ================================================ FILE: README.Rmd ================================================ --- output: github_document --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", fig.path = "man/figures/README-", out.width = "100%" ) ``` # datamods > Shiny modules to import and manipulate data into an application or addin. [![CRAN status](https://www.r-pkg.org/badges/version/datamods)](https://CRAN.R-project.org/package=datamods) [![cranlogs](https://cranlogs.r-pkg.org/badges/datamods)](https://cran.r-project.org/package=datamods) [![R-CMD-check](https://github.com/dreamRs/datamods/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/dreamRs/datamods/actions/workflows/R-CMD-check.yaml) [![Codecov test coverage](https://codecov.io/gh/dreamRs/datamods/graph/badge.svg)](https://app.codecov.io/gh/dreamRs/datamods) ### Overview This package provides custom shiny modules to import data from various sources, select, rename and convert variables in a dataset and validate content with [validate](https://github.com/data-cleaning/validate) package. The modules can be used in any standard shiny application or RStudio add-in. ### Internationalization ```{r, include=FALSE} i18n_flag <- function(code, language) { code <- strsplit(code, split = "|", fixed = TRUE)[[1]] flag <- sprintf("", code) paste(paste(flag, collapse = ""), language) } i18n_flags <- function(languages) { mapply( FUN = i18n_flag, code = names(languages), language = unlist(languages, use.names = FALSE), USE.NAMES = FALSE ) } languages <- list( gb = "english (default)", fr = "french", mk = "macedonian", "br|pt" = "brazilian portuguese", al = "albanian", cn = "chinese", es = "spanish", de = "german", tr = "turkish", kr = "korean", pl = "polish", ja = "japanese" ) ``` Currently you can use {datamods} in the following language: `r i18n_flags(languages)`. If you want another language to be supported, you can submit a Pull Request to add a CSV file like the one used for french (file is located in `inst/i18n` folder in the package, you can see it [here on GitHub](https://github.com/dreamRs/datamods/blob/master/inst/i18n/fr.csv)). See the [online vignette](https://dreamrs.github.io/datamods/articles/i18n.html) for more on this topic. ### Installation Install from [CRAN](https://CRAN.R-project.org/package=datamods) with: ```r install.packages("datamods") ``` You can install the development version of datamods from [GitHub](https://github.com/dreamRs/datamods) with: ```r remotes::install_github("dreamRs/datamods") ``` ### Import Import data from: * **environment**: such as Global environment or from a package * **file**: text files, Excel, SAS or SPSS format... anything that package [rio](https://github.com/gesistsa/rio#supported-file-formats) can handle * **copy/paste**: paste data from an other source like Excel or text file * **Google Sheet**: use the URL to import the Googlesheet * **URL**: use a URL to import from a flat table Each module is available in the form `import_file_ui()` / `import_file_server()` and can be use independently. Or all modules can be launched together in a modal window via `import_modal()` / `import_server()`: ![](man/figures/datamods-modal.png) This module also allow to view imported data and to update variables. ### Update Module `update_variables_ui()` / `update_variables_server()` allow to: * **select** variables of interest in a dataset * **rename** variables to be used in application after that * **convert** variables to change their class, from character to numeric for example ![](man/figures/datamods-update.png) ### Validate Define some validation rules with package [validate](https://github.com/data-cleaning/validate) and check whether data lives up to those expectations. ![](man/figures/datamods-validation.png) ### Filter Interactively filter a `data.frame`, this module also generates the code to reproduce the filters. ![](man/figures/datamods-filter.png) ### Sample This module extracts a sample from a `data.frame`, based either on a fixed number of rows or on a percentage of total rows. ![](man/figures/datamods-sample.png) ### Edit This module makes a `data.frame` editable, allowing the user to add, modify or delete rows. ![](man/figures/datamods-edit-data.png) ### Create column This module allow to enter an expression to create a new column in a `data.frame`. ![](man/figures/create_column.png) ### Cut numeric variable This module contain an interface to cut a numeric into several intervals. ![](man/figures/cut_variable.png) ### Update factor This module contain an interface to reorder the levels of a factor variable. ![](man/figures/update_factor.png) ================================================ FILE: README.md ================================================ # datamods > Shiny modules to import and manipulate data into an application or > addin. [![CRAN status](https://www.r-pkg.org/badges/version/datamods)](https://CRAN.R-project.org/package=datamods) [![cranlogs](https://cranlogs.r-pkg.org/badges/datamods)](https://cran.r-project.org/package=datamods) [![R-CMD-check](https://github.com/dreamRs/datamods/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/dreamRs/datamods/actions/workflows/R-CMD-check.yaml) [![Codecov test coverage](https://codecov.io/gh/dreamRs/datamods/graph/badge.svg)](https://app.codecov.io/gh/dreamRs/datamods) ### Overview This package provides custom shiny modules to import data from various sources, select, rename and convert variables in a dataset and validate content with [validate](https://github.com/data-cleaning/validate) package. The modules can be used in any standard shiny application or RStudio add-in. ### Internationalization Currently you can use {datamods} in the following language: english (default), french, macedonian, brazilian portuguese, albanian, chinese, spanish, german, turkish, korean, polish, japanese. If you want another language to be supported, you can submit a Pull Request to add a CSV file like the one used for french (file is located in `inst/i18n` folder in the package, you can see it [here on GitHub](https://github.com/dreamRs/datamods/blob/master/inst/i18n/fr.csv)). See the [online vignette](https://dreamrs.github.io/datamods/articles/i18n.html) for more on this topic. ### Installation Install from [CRAN](https://CRAN.R-project.org/package=datamods) with: ``` r install.packages("datamods") ``` You can install the development version of datamods from [GitHub](https://github.com/dreamRs/datamods) with: ``` r remotes::install_github("dreamRs/datamods") ``` ### Import Import data from: - **environment**: such as Global environment or from a package - **file**: text files, Excel, SAS or SPSS format… anything that package [rio](https://github.com/gesistsa/rio#supported-file-formats) can handle - **copy/paste**: paste data from an other source like Excel or text file - **Google Sheet**: use the URL to import the Googlesheet - **URL**: use a URL to import from a flat table Each module is available in the form `import_file_ui()` / `import_file_server()` and can be use independently. Or all modules can be launched together in a modal window via `import_modal()` / `import_server()`: ![](man/figures/datamods-modal.png) This module also allow to view imported data and to update variables. ### Update Module `update_variables_ui()` / `update_variables_server()` allow to: - **select** variables of interest in a dataset - **rename** variables to be used in application after that - **convert** variables to change their class, from character to numeric for example ![](man/figures/datamods-update.png) ### Validate Define some validation rules with package [validate](https://github.com/data-cleaning/validate) and check whether data lives up to those expectations. ![](man/figures/datamods-validation.png) ### Filter Interactively filter a `data.frame`, this module also generates the code to reproduce the filters. ![](man/figures/datamods-filter.png) ### Sample This module extracts a sample from a `data.frame`, based either on a fixed number of rows or on a percentage of total rows. ![](man/figures/datamods-sample.png) ### Edit This module makes a `data.frame` editable, allowing the user to add, modify or delete rows. ![](man/figures/datamods-edit-data.png) ### Create column This module allow to enter an expression to create a new column in a `data.frame`. ![](man/figures/create_column.png) ### Cut numeric variable This module contain an interface to cut a numeric into several intervals. ![](man/figures/cut_variable.png) ### Update factor This module contain an interface to reorder the levels of a factor variable. ![](man/figures/update_factor.png) ================================================ FILE: _pkgdown.yml ================================================ url: https://dreamrs.github.io/datamods template: bootstrap: 5 bootswatch: zephyr bslib: base_font: {google: "Poppins"} primary: "#112446" navbar: bg: primary authors: Victor Perrier: href: https://twitter.com/_pvictorr html: Victor Perrier Fanny Meyer: href: https://twitter.com/_mfaan html: Fanny Meyer Zauad Shahreer Abeer: href: https://twitter.com/shahreyarabeer html: Zauad Shahreer Abeer ================================================ FILE: cran-comments.md ================================================ ## Test environments * local R installation, R 4.4.1 * ubuntu 22.04, Windows 10, macOS (on GitHub Actions), R 4.4.1 * win-builder (devel) ## R CMD check results 0 errors | 0 warnings | 0 note Thanks, Victor ================================================ FILE: data-raw/demo_edit.R ================================================ ## code to prepare `demo_edit` dataset goes here #library(charlatan) demo_edit <- data.frame( "name" = ch_name(n = 20), "job" = ch_job(n = 20), "credit card provider" = as.factor(ch_credit_card_provider(n = 20)), "credit card security code" = as.numeric(ch_credit_card_security_code(n = 20)), "date obtained" = sample(seq(as.Date('2015/01/01'), as.Date('2022/01/01'), by = "year"), 20, replace = TRUE), "contactless card" = sample(c(TRUE, FALSE), 20, replace = TRUE) ) demo_edit <- janitor::clean_names(demo_edit) usethis::use_data(demo_edit, overwrite = TRUE) ================================================ FILE: examples/create_column.R ================================================ library(shiny) library(datamods) library(reactable) ui <- fluidPage( theme = bslib::bs_theme(version = 5L, preset = "bootstrap"), shinyWidgets::html_dependency_winbox(), tags$h2("Create new column"), fluidRow( column( width = 4, create_column_ui("inline"), actionButton("modal", "Or click here to open a modal to create a column"), tags$br(), tags$br(), actionButton("winbox", "Or click here to open a WinBox to create a column") ), column( width = 8, reactableOutput(outputId = "table"), verbatimTextOutput("code") ) ) ) server <- function(input, output, session) { rv <- reactiveValues(data = MASS::Cars93[, c(1, 3, 4, 5, 6, 10)]) # inline mode data_inline_r <- create_column_server( id = "inline", data_r = reactive(rv$data) ) observeEvent(data_inline_r(), rv$data <- data_inline_r()) # modal window mode observeEvent(input$modal, modal_create_column("modal")) data_modal_r <- create_column_server( id = "modal", data_r = reactive(rv$data) ) observeEvent(data_modal_r(), rv$data <- data_modal_r()) # WinBox window mode observeEvent(input$winbox, winbox_create_column("winbox")) data_winbox_r <- create_column_server( id = "winbox", data_r = reactive(rv$data) ) observeEvent(data_winbox_r(), rv$data <- data_winbox_r()) # Show result output$table <- renderReactable({ data <- req(rv$data) reactable( data = data, bordered = TRUE, compact = TRUE, striped = TRUE ) }) output$code <- renderPrint({ attr(rv$data, "code") }) } if (interactive()) shinyApp(ui, server) ================================================ FILE: examples/cut_variable.R ================================================ library(shiny) library(datamods) library(reactable) ui <- fluidPage( theme = bslib::bs_theme(version = 5L, preset = "bootstrap"), shinyWidgets::html_dependency_winbox(), tags$h2("Convert Numeric to Factor"), fluidRow( column( width = 6, cut_variable_ui("inline"), actionButton("modal", "Or click here to open a modal to cut a variable"), tags$br(), tags$br(), actionButton("winbox", "Or click here to open a WinBox to cut a variable") ), column( width = 6, reactableOutput(outputId = "table"), verbatimTextOutput("code") ) ) ) server <- function(input, output, session) { rv <- reactiveValues(data = MASS::Cars93[, c(1, 3, 4, 5, 6, 10)]) # inline mode data_inline_r <- cut_variable_server( id = "inline", data_r = reactive(rv$data) ) observeEvent(data_inline_r(), rv$data <- data_inline_r()) # modal window mode observeEvent(input$modal, modal_cut_variable("modal")) data_modal_r <- cut_variable_server( id = "modal", data_r = reactive(rv$data) ) observeEvent(data_modal_r(), rv$data <- data_modal_r()) # WinBox window mode observeEvent(input$winbox, winbox_cut_variable("winbox")) data_winbox_r <- cut_variable_server( id = "winbox", data_r = reactive(rv$data) ) observeEvent(data_winbox_r(), rv$data <- data_winbox_r()) # Show result output$table <- renderReactable({ data <- req(rv$data) reactable( data = data, bordered = TRUE, compact = TRUE, striped = TRUE ) }) output$code <- renderPrint({ attr(rv$data, "code") }) } if (interactive()) shinyApp(ui, server) ================================================ FILE: examples/edit_data-callback.R ================================================ # ------------------------------------------------------------------------- # Edit data only with callbacks ------------------------------------------- # ------------------------------------------------------------------------- # Packages ---------------------------------------------------------------- library(data.table) library(datamods) library(shiny) library(bslib) library(shinybusy) library(reactable) # Fake data --------------------------------------------------------------- mydata <- charlatan::ch_generate() setDT(mydata) mydata[, ID := seq_len(.N)] # internal ID mydata[, user := paste0("user", seq_len(.N))] setcolorder(mydata, c("ID", "user")) path_to_data <- tempfile(pattern = "mydata", fileext = ".rds") saveRDS(mydata, file = path_to_data) # Utilities to update data ------------------------------------------------ update_data <- function(modified) { res <- try({ base <- readRDS(path_to_data) setDT(base) base[ID == modified$ID, (names(modified)) := as.list(modified)] saveRDS(base, path_to_data) }) inherits(res, "try-error") } delete_data <- function(deleted) { res <- try({ base <- readRDS(path_to_data) setDT(base) base <- base[ID != deleted$ID] saveRDS(base, path_to_data) }) inherits(res, "try-error") } add_data <- function(added) { res <- try({ base <- readRDS(path_to_data) setDT(base) if (added$user %in% base$user) { shinybusy::report_warning("Warning", "User already exist.") return(FALSE) } added$ID <- max(base$ID) + 1 base <- rbind(base, added[, .SD, .SDcols = !is.na(added)], use.names = TRUE, fill = TRUE) saveRDS(base, path_to_data) }) inherits(res, "try-error") } # App --------------------------------------------------------------------- ui <- fluidPage( theme = bs_theme( version = 5 ), edit_data_ui(id = "EDIT"), verbatimTextOutput("result") ) server <- function(input, output, session) { data_r <- reactiveFileReader( intervalMillis = 1000, session = session, filePath = path_to_data, readFunc = readRDS ) # Tableau edited_gestion_utilisateurs_r <- edit_data_server( id = "EDIT", data_r = data_r, add = TRUE, update = TRUE, delete = TRUE, download_csv = FALSE, download_excel = TRUE, modal_size = "xl", n_column = 2, var_edit = c("user", "name", "job", "phone_number"), callback_update = function(data, row) { result <- update_data(row) if (isTRUE(result)) shinybusy::report_failure("Error", "Failed to update data") removeModal() return(TRUE) }, callback_delete = function(data, row) { result <- delete_data(row) if (isTRUE(result)) shinybusy::report_failure("Error", "Failed to delete data") removeModal() return(TRUE) }, callback_add = function(data, add) { result <- add_data(add) if (isTRUE(result)) shinybusy::report_failure("Error", "Failed to add data") removeModal() return(TRUE) }, only_callback = TRUE, use_notify = FALSE, reactable_options = list( pagination = FALSE, outline = TRUE, striped = TRUE, columns = list( ID = colDef(show = FALSE) ) ) ) } if (interactive()) shinyApp(ui, server) ================================================ FILE: examples/edit_data-callback_add.R ================================================ library(shiny) library(datamods) library(bslib) library(reactable) ui <- fluidPage( theme = bs_theme( version = 5 ), edit_data_ui(id = "id"), verbatimTextOutput("result") ) server <- function(input, output, session) { edited_r <- edit_data_server( id = "id", data_r = reactive(data.frame(Month = month.name, Values = 1:12)), add = TRUE, callback_add = function(data, add) { print(data) print(add) if (!add$Month %in% month.name) return(FALSE) if (add$Values > 20) { shinybusy::notify_warning("Value must be equal or inferior to 20") return(FALSE) } # else add data to table return(TRUE) } ) output$result <- renderPrint({ str(edited_r()) }) } if (interactive()) shinyApp(ui, server) ================================================ FILE: examples/edit_data-callback_delete.R ================================================ library(shiny) library(datamods) library(bslib) library(reactable) ui <- fluidPage( theme = bs_theme( version = 5 ), edit_data_ui(id = "id"), verbatimTextOutput("result") ) server <- function(input, output, session) { edited_r <- edit_data_server( id = "id", data_r = reactive(data.frame(Month = month.name, Values = 1:12)), add = TRUE, callback_delete = function(data, row) { print(data) print(row) if (row$Month == "September") { shinybusy::notify_warning("You cannot delete September") return(FALSE) } return(TRUE) } ) output$result <- renderPrint({ str(edited_r()) }) } if (interactive()) shinyApp(ui, server) ================================================ FILE: examples/edit_data-callback_update-row_style.R ================================================ library(shiny) # library(datamods) pkgload::load_all() library(bslib) library(reactable) ui <- fluidPage( theme = bs_theme( version = 5, preset = "bootstrap" ), uiOutput(outputId = "custom_styles"), edit_data_ui(id = "id"), verbatimTextOutput("row_updated") ) server <- function(input, output, session) { rv <- reactiveValues(row_updated = NULL) edited_r <- edit_data_server( id = "id", data_r = reactive(data.frame( row_id = 1:12, Month = month.name, Values = 1:12, Comment = letters[1:12] )), add = TRUE, var_edit = c("Month", "Values", "Comment"), reactable_options = list( pagination = FALSE, compact = TRUE, columns = list(row_id = colDef(show = FALSE)), rowClass = function(index) { paste0("table-row-", index) } ), callback_add = function(data, row) { print(row) return(TRUE) }, callback_delete = function(data, row) { print(row) return(TRUE) }, callback_update = function(data, row) { print(row) rv$row_updated <- row$row_id return(TRUE) } ) output$row_updated <- renderPrint({ paste("Last row updated:", rv$row_updated) }) output$custom_styles <- renderUI({ req(rv$row_updated) tags$style(sprintf( ".table-row-%s { background: #FE2E2E; transition: background 1s cubic-bezier(0.785, 0.135, 0.15, 0.86); color: white; }", rv$row_updated )) }) } if (interactive()) shinyApp(ui, server) ================================================ FILE: examples/edit_data-callback_update.R ================================================ library(shiny) library(datamods) library(bslib) library(reactable) ui <- fluidPage( theme = bs_theme( version = 5 ), edit_data_ui(id = "id"), verbatimTextOutput("result") ) server <- function(input, output, session) { edited_r <- edit_data_server( id = "id", data_r = reactive(data.frame(Month = month.name, Values = 1:12)), add = TRUE, callback_update = function(data, row) { print(data) print(row) if (!row$Month %in% month.name) return(FALSE) if (row$Values > 20) { shinybusy::notify_warning("Value must be equal or inferior to 20") return(FALSE) } # else update data return(TRUE) } ) output$result <- renderPrint({ str(edited_r()) }) } if (interactive()) shinyApp(ui, server) ================================================ FILE: examples/edit_data.R ================================================ library(shiny) library(datamods) library(bslib) library(reactable) ui <- fluidPage( theme = bs_theme( version = 5 ), tags$h2("Edit data", align = "center"), edit_data_ui(id = "id"), verbatimTextOutput("result") ) server <- function(input, output, session) { edited_r <- edit_data_server( id = "id", data_r = reactive(demo_edit), add = TRUE, update = TRUE, delete = TRUE, download_csv = TRUE, download_excel = TRUE, file_name_export = "datas", # var_edit = c("name", "job", "credit_card_provider", "credit_card_security_code"), var_mandatory = c("name", "job"), var_labels = list( name = "Name", credit_card_security_code = "Credit card security code", date_obtained = "Date obtained", contactless_card = "Contactless Card", credit_card_provider = "Credit card provider" ), add_default_values = list( name = "Please enter your name here", date_obtained = Sys.Date() ), n_column = 2, modal_size = "l", modal_easy_close = TRUE, reactable_options = list( defaultColDef = colDef(filterable = TRUE), selection = "single", columns = list( name = colDef(name = "Name", style = list(fontWeight = "bold")), credit_card_security_code = colDef(name = "Credit card security code"), date_obtained = colDef(name = "Date obtained", format = colFormat(date = TRUE)), contactless_card = colDef( name = "Contactless Card", cell = htmlwidgets::JS( "function(cellInfo) { return cellInfo.value ? '\u2714\ufe0f Yes' : '\u274c No'; }" ) ), credit_card_provider = colDef( name = "Credit card provider", style = htmlwidgets::JS( "function(rowInfo) { console.log(rowInfo); var value = rowInfo.values['credit_card_provider']; var color; if (value == 'Mastercard') { color = '#e06631'; } else if (value == 'VISA 16 digit') { color = '#0c13cf'; } else if (value == 'American Express') { color = '#4d8be8'; } else if (value == 'JCB 16 digit') { color = '#23c45e'; } else { color = '#777' } return {color: color, fontWeight: 'bold'} }" ) ) ), bordered = TRUE, compact = TRUE, searchable = TRUE, highlight = TRUE ) ) output$result <- renderPrint({ str(edited_r()) }) } if (interactive()) shinyApp(ui, server) ================================================ FILE: examples/filter_data-basic.R ================================================ library(shiny) library(datamods) ui <- fluidPage( tags$h2("Filter data.frame"), fluidRow( column( width = 3, filter_data_ui("filtering", max_height = "500px") ), column( width = 9, reactable::reactableOutput(outputId = "table"), tags$b("Code dplyr:"), verbatimTextOutput(outputId = "code_dplyr"), tags$b("Expression:"), verbatimTextOutput(outputId = "code"), tags$b("Filtered data:"), verbatimTextOutput(outputId = "res_str") ) ) ) server <- function(input, output, session) { res_filter <- filter_data_server( id = "filtering", data = reactive(data.frame( varchar = month.name, varnum = 1:12, vardate = Sys.Date() + 1:12 )), vars = reactive(list( "Variable character" = "varchar", "Variable date" = "vardate", "Variable numeric" = "varnum" )), drop_ids = FALSE ) output$table <- reactable::renderReactable({ reactable::reactable(res_filter$filtered(), pagination = FALSE) }) output$code_dplyr <- renderPrint({ res_filter$code() }) output$code <- renderPrint({ res_filter$expr() }) output$res_str <- renderPrint({ str(res_filter$filtered()) }) } if (interactive()) shinyApp(ui, server) ================================================ FILE: examples/filter_data.R ================================================ library(shiny) library(shinyWidgets) library(datamods) library(MASS) # Add some NAs to mpg mtcars_na <- mtcars mtcars_na[] <- lapply( X = mtcars_na, FUN = function(x) { x[sample.int(n = length(x), size = sample(5:10, 1))] <- NA x } ) datetime <- data.frame( date = seq(Sys.Date(), by = "day", length.out = 300), datetime = seq(Sys.time(), by = "hour", length.out = 300), num = sample.int(1e5, 300) ) one_column_numeric <- data.frame( var1 = rnorm(100) ) ui <- fluidPage( tags$h2("Filter data.frame"), actionButton("saveFilterButton","Save Filter Values"), actionButton("loadFilterButton","Load Filter Values"), radioButtons( inputId = "dataset", label = "Data:", choices = c( "iris", "mtcars", "mtcars_na", "Cars93", "datetime", "one_column_numeric" ), inline = TRUE ), fluidRow( column( width = 3, filter_data_ui("filtering", max_height = "500px") ), column( width = 9, progressBar( id = "pbar", value = 100, total = 100, display_pct = TRUE ), reactable::reactableOutput(outputId = "table"), tags$b("Code dplyr:"), verbatimTextOutput(outputId = "code_dplyr"), tags$b("Expression:"), verbatimTextOutput(outputId = "code"), tags$b("Filtered data:"), verbatimTextOutput(outputId = "res_str") ) ) ) server <- function(input, output, session) { savedFilterValues <- reactiveVal() data <- reactive({ get(input$dataset) }) vars <- reactive({ if (identical(input$dataset, "mtcars")) { setNames(as.list(names(mtcars)[1:5]), c( "Miles/(US) gallon", "Number of cylinders", "Displacement (cu.in.)", "Gross horsepower", "Rear axle ratio" )) } else { NULL } }) observeEvent(input$saveFilterButton,{ savedFilterValues <<- res_filter$values() },ignoreInit = T) defaults <- reactive({ input$loadFilterButton savedFilterValues }) res_filter <- filter_data_server( id = "filtering", data = data, name = reactive(input$dataset), vars = vars, defaults = defaults, widget_num = "slider", widget_date = "slider", label_na = "Missing" ) observeEvent(res_filter$filtered(), { updateProgressBar( session = session, id = "pbar", value = nrow(res_filter$filtered()), total = nrow(data()) ) }) output$table <- reactable::renderReactable({ reactable::reactable(res_filter$filtered()) }) output$code_dplyr <- renderPrint({ res_filter$code() }) output$code <- renderPrint({ res_filter$expr() }) output$res_str <- renderPrint({ str(res_filter$filtered()) }) } if (interactive()) shinyApp(ui, server) ================================================ FILE: examples/from-copypaste.R ================================================ library(shiny) library(datamods) ui <- fluidPage( tags$h3("Import data with copy & paste"), fluidRow( column( width = 4, import_copypaste_ui("myid") ), column( width = 8, tags$b("Import status:"), verbatimTextOutput(outputId = "status"), tags$b("Name:"), verbatimTextOutput(outputId = "name"), tags$b("Data:"), verbatimTextOutput(outputId = "data") ) ) ) server <- function(input, output, session) { imported <- import_copypaste_server("myid") output$status <- renderPrint({ imported$status() }) output$name <- renderPrint({ imported$name() }) output$data <- renderPrint({ imported$data() }) } if (interactive()) shinyApp(ui, server) ================================================ FILE: examples/from-file.R ================================================ library(shiny) library(datamods) ui <- fluidPage( # theme = bslib::bs_theme(version = 5L), # theme = bslib::bs_theme(version = 5L, preset = "bootstrap"), tags$h3("Import data from a file"), fluidRow( column( width = 4, import_file_ui( id = "myid", file_extensions = c(".csv", ".txt", ".xls", ".xlsx", ".json"), layout_params = "inline" # or "dropdown" ) ), column( width = 8, tags$b("Import status:"), verbatimTextOutput(outputId = "status"), tags$b("Name:"), verbatimTextOutput(outputId = "name"), tags$b("Code:"), verbatimTextOutput(outputId = "code"), tags$b("Data:"), verbatimTextOutput(outputId = "data") ) ) ) server <- function(input, output, session) { imported <- import_file_server( id = "myid", # Custom functions to read data read_fns = list( xls = function(file, sheet, skip, encoding) { readxl::read_xls(path = file, sheet = sheet, skip = skip) }, json = function(file) { jsonlite::read_json(file, simplifyVector = TRUE) } ), show_data_in = "modal" ) output$status <- renderPrint({ imported$status() }) output$name <- renderPrint({ imported$name() }) output$code <- renderPrint({ imported$code() }) output$data <- renderPrint({ imported$data() }) } if (interactive()) shinyApp(ui, server) ================================================ FILE: examples/from-globalenv.R ================================================ if (interactive()) { library(shiny) library(datamods) # Create some data.frames my_df <- data.frame( variable1 = sample(letters, 20, TRUE), variable2 = sample(1:100, 20, TRUE) ) results_analysis <- data.frame( id = sample(letters, 20, TRUE), measure = sample(1:100, 20, TRUE), response = sample(1:100, 20, TRUE) ) # Application ui <- fluidPage( fluidRow( column( width = 4, import_globalenv_ui("myid") ), column( width = 8, tags$b("Import status:"), verbatimTextOutput(outputId = "status"), tags$b("Name:"), verbatimTextOutput(outputId = "name"), tags$b("Data:"), verbatimTextOutput(outputId = "data") ) ) ) server <- function(input, output, session) { imported <- import_globalenv_server("myid") output$status <- renderPrint({ imported$status() }) output$name <- renderPrint({ imported$name() }) output$data <- renderPrint({ imported$data() }) } shinyApp(ui, server) } ================================================ FILE: examples/from-googlesheets.R ================================================ library(shiny) library(datamods) ui <- fluidPage( tags$h3("Import data from Googlesheets"), fluidRow( column( width = 4, import_googlesheets_ui("myid") ), column( width = 8, tags$b("Import status:"), verbatimTextOutput(outputId = "status"), tags$b("Name:"), verbatimTextOutput(outputId = "name"), tags$b("Data:"), verbatimTextOutput(outputId = "data") ) ) ) server <- function(input, output, session) { imported <- import_googlesheets_server("myid") output$status <- renderPrint({ imported$status() }) output$name <- renderPrint({ imported$name() }) output$data <- renderPrint({ imported$data() }) } if (interactive()) shinyApp(ui, server) ================================================ FILE: examples/from-url.R ================================================ library(shiny) library(datamods) ui <- fluidPage( tags$h3("Import data from URL"), fluidRow( column( width = 4, import_url_ui("myid") ), column( width = 8, tags$b("Import status:"), verbatimTextOutput(outputId = "status"), tags$b("Name:"), verbatimTextOutput(outputId = "name"), tags$b("Data:"), verbatimTextOutput(outputId = "data") ) ) ) server <- function(input, output, session) { imported <- import_url_server( "myid", btn_show_data = FALSE, return_class = "raw" ) output$status <- renderPrint({ imported$status() }) output$name <- renderPrint({ imported$name() }) output$data <- renderPrint({ imported$data() }) } if (interactive()) shinyApp(ui, server) ================================================ FILE: examples/i18n.R ================================================ library(datamods) # Use with an objet my.translations <- list( "Hello" = "Bonjour" ) i18n("Hello", my.translations) # Use with options() options("i18n" = list( "Hello" = "Bonjour" )) i18n("Hello") # With a package options("datamods.i18n" = "fr") i18n("Browse...", translations = i18n_translations("datamods")) # If you call i18n() from within a function of your package # you don't need second argument, e.g.: # i18n("Browse...") ================================================ FILE: examples/modal-validation.R ================================================ library(shiny) library(datamods) if (requireNamespace("validate")) { library(validate) # Define some rules to be applied to data myrules <- validator( is.character(Manufacturer) | is.factor(Manufacturer), is.character(Model) | is.factor(Model), is_unique(Manufacturer, Model), is.numeric(Price), is.numeric(Min.Price), is.numeric(Max.Price), Price > 12, # we should use 0 for testing positivity, but that's for the example !is.na(Luggage.room), in_range(Cylinders, min = 4, max = 8), Man.trans.avail %in% c("Yes", "No") ) # Add some labels label(myrules) <- c( "Variable Manufacturer must be character", "Variable Model must be character", "Manufacturer X Model are unique", "Variable Price must be numeric", "Variable Min.Price must be numeric", "Variable Max.Price must be numeric", "Variable Price must be strictly positive", "Luggage.room must not contain any missing values", "Cylinders must be between 4 and 8", "Man.trans.avail must be 'Yes' or 'No'" ) # you can also add a description() ui <- fluidPage( fluidRow( column( width = 4, checkboxGroupInput( inputId = "from", label = "From", choices = c("env", "file", "copypaste", "googlesheets", "url"), selected = c("file", "copypaste") ), actionButton("launch_modal", "Launch modal window") ), column( width = 8, tags$b("Imported data:"), verbatimTextOutput(outputId = "name"), verbatimTextOutput(outputId = "data") ) ) ) server <- function(input, output, session) { observeEvent(input$launch_modal, { req(input$from) import_modal( id = "myid", from = input$from, title = "Import data to be used in application" ) }) imported <- import_server( id = "myid", return_class = "tbl_df", validation_opts = list( # rules = validator(.file = system.file("extdata/rules.yaml", package = "datamods")) rules = myrules ) ) output$name <- renderPrint({ req(imported$name()) imported$name() }) output$data <- renderPrint({ req(imported$data()) imported$data() }) } if (interactive()) shinyApp(ui, server) } ================================================ FILE: examples/modal.R ================================================ library(shiny) library(datamods) ui <- fluidPage( # Try with different Bootstrap version theme = bslib::bs_theme(version = 5, preset = "bootstrap"), fluidRow( column( width = 4, checkboxGroupInput( inputId = "from", label = "From", choices = c("env", "file", "copypaste", "googlesheets", "url"), selected = c("file", "copypaste") ), actionButton("launch_modal", "Launch modal window") ), column( width = 8, tags$b("Imported data:"), verbatimTextOutput(outputId = "name"), verbatimTextOutput(outputId = "data"), verbatimTextOutput(outputId = "str_data") ) ) ) server <- function(input, output, session) { observeEvent(input$launch_modal, { req(input$from) import_modal( id = "myid", from = input$from, title = "Import data to be used in application" ) }) imported <- import_server("myid", return_class = "tbl_df") output$name <- renderPrint({ req(imported$name()) imported$name() }) output$data <- renderPrint({ req(imported$data()) imported$data() }) output$str_data <- renderPrint({ req(imported$data()) str(imported$data()) }) } if (interactive()) shinyApp(ui, server) ================================================ FILE: examples/sample.R ================================================ library(shiny) library(datamods) library(reactable) ui <- fluidPage( tags$h2("Sampling"), fluidRow( column( width = 3, sample_ui("myID") ), column( width = 9, reactableOutput("table") ) ) ) server <- function(input, output, session) { result_sample <- sample_server("myID", reactive(iris)) output$table <- renderReactable({ table_sample <- reactable( data = result_sample(), defaultColDef = colDef( align = "center" ), borderless = TRUE, highlight = TRUE, striped = TRUE ) return(table_sample) }) } if (interactive()) shinyApp(ui, server) ================================================ FILE: examples/select-group-default.R ================================================ # Default ----------------------------------------------------------------- library(shiny) library(datamods) library(shinyWidgets) ui <- fluidPage( # theme = bslib::bs_theme(version = 5L), fluidRow( column( width = 10, offset = 1, tags$h3("Filter data with select group module"), shinyWidgets::panel( select_group_ui( id = "my-filters", params = list( list(inputId = "Manufacturer", label = "Manufacturer:"), list(inputId = "Type", label = "Type:"), list(inputId = "AirBags", label = "AirBags:"), list(inputId = "DriveTrain", label = "DriveTrain:") ), vs_args = list(disableSelectAll = FALSE) ), status = "primary" ), reactable::reactableOutput(outputId = "table"), tags$b("Inputs values:"), verbatimTextOutput("inputs") ) ) ) server <- function(input, output, session) { res_mod <- select_group_server( id = "my-filters", data = reactive(MASS::Cars93), vars = reactive(c("Manufacturer", "Type", "AirBags", "DriveTrain")) ) output$table <- reactable::renderReactable({ reactable::reactable(res_mod()) }) output$inputs <- renderPrint({ attr(res_mod(), "inputs") }) } if (interactive()) shinyApp(ui, server) ================================================ FILE: examples/select-group-selected.R ================================================ # Selected value -------------------------------------------------------------------- library(shiny) library(datamods) ui <- fluidPage( select_group_ui( id = "my-filters", params = list( list(inputId = "Manufacturer", label = "Manufacturer:"), list(inputId = "Type", label = "Type:") ), vs_args = list( disableSelectAll = FALSE ) ), actionButton("set_sel", "Set Manufacturer=Acura"), verbatimTextOutput("res") ) server <- function(input, output, session) { # We use a reactiveValue so that it can be updated rv <- reactiveValues(selected = list(Manufacturer = "Audi")) # for init res_r <- select_group_server( id = "my-filters", data = reactive(MASS::Cars93), vars = reactive(c("Manufacturer", "Type")), selected_r = reactive(rv$selected) ) output$res <- renderPrint({ res_r() }) observeEvent(input$set_sel, { rv$selected <- list(Manufacturer = "Acura") }) } if (interactive()) shinyApp(ui, server) ================================================ FILE: examples/select-group-subset.R ================================================ # Subset data ------------------------------------------------------------- library(shiny) library(datamods) library(shinyWidgets) ui <- fluidPage( fluidRow( column( width = 10, offset = 1, tags$h3("Filter data with select group module"), panel( pickerInput( inputId = "car_select", choices = unique(MASS::Cars93$Manufacturer), options = list( `live-search` = TRUE, title = "None selected" ) ), select_group_ui( id = "my-filters", params = list( list(inputId = "Manufacturer", label = "Manufacturer:"), list(inputId = "Type", label = "Type:"), list(inputId = "AirBags", label = "AirBags:"), list(inputId = "DriveTrain", label = "DriveTrain:") ) ), status = "primary" ), reactable::reactableOutput(outputId = "table") ) ) ) server <- function(input, output, session) { cars_r <- reactive({ subset(MASS::Cars93, Manufacturer %in% input$car_select) }) res_mod <- select_group_server( id = "my-filters", data = cars_r, vars = c("Manufacturer", "Type", "AirBags", "DriveTrain") ) output$table <- reactable::renderReactable({ reactable::reactable(res_mod()) }) } if (interactive()) shinyApp(ui, server) ================================================ FILE: examples/select-group-vars.R ================================================ # Select variables -------------------------------------------------------- library(shiny) library(datamods) library(shinyWidgets) ui <- fluidPage( fluidRow( column( width = 10, offset = 1, tags$h3("Filter data with select group module"), panel( checkboxGroupInput( inputId = "vars", label = "Variables to use:", choices = c("Manufacturer", "Type", "AirBags", "DriveTrain"), selected = c("Manufacturer", "Type", "AirBags", "DriveTrain"), inline = TRUE ), select_group_ui( id = "my-filters", params = list( list(inputId = "Manufacturer", label = "Manufacturer:"), list(inputId = "Type", label = "Type:"), list(inputId = "AirBags", label = "AirBags:"), list(inputId = "DriveTrain", label = "DriveTrain:") ), inline = TRUE ), status = "primary" ), reactable::reactableOutput(outputId = "table") ) ) ) server <- function(input, output, session) { vars_r <- reactive({ input$vars }) res_mod <- select_group_server( id = "my-filters", data = MASS::Cars93, vars = vars_r ) output$table <- reactable::renderReactable({ reactable::reactable(res_mod()) }) } if (interactive()) shinyApp(ui, server) ================================================ FILE: examples/show_data.R ================================================ library(shiny) library(datamods) ui <- fluidPage( theme = bslib::bs_theme(version = 5L), shinyWidgets::html_dependency_winbox(), actionButton( inputId = "show1", label = "Show data in popup", icon = icon("eye") ), actionButton( inputId = "show2", label = "Show data in modal", icon = icon("eye") ), actionButton( inputId = "show3", label = "Show data without classes", icon = icon("eye") ), actionButton( inputId = "show4", label = "Show data in Winbox", icon = icon("eye") ) ) server <- function(input, output, session) { observeEvent(input$show1, { show_data(MASS::Cars93, title = "MASS::Cars93 dataset", type = "popup") }) observeEvent(input$show2, { show_data(MASS::Cars93, title = "MASS::Cars93 dataset", type = "modal") }) observeEvent(input$show3, { show_data( data = MASS::Cars93, title = "MASS::Cars93 dataset", show_classes = FALSE, options = list(pagination = 10), type = "modal" ) }) observeEvent(input$show4, { show_data( MASS::Cars93, title = "MASS::Cars93 dataset", type = "winbox", wbOptions = shinyWidgets::wbOptions(background = "forestgreen") ) }) } if (interactive()) shinyApp(ui, server) ================================================ FILE: examples/update_factor.R ================================================ library(shiny) library(datamods) library(ggplot2) ui <- fluidPage( theme = bslib::bs_theme(version = 5L, preset = "bootstrap"), shinyWidgets::html_dependency_winbox(), tags$h2("Reorder the Levels of a Factor"), fluidRow( column( width = 6, update_factor_ui("id"), actionButton("modal", "Or click here to open a modal to update factor's level"), tags$br(), tags$br(), actionButton("winbox", "Or click here to open a WinBox to create a column") ), column( width = 6, selectInput( "var", label = "Variable to plot:", choices = NULL ), plotOutput("plot"), verbatimTextOutput("res") ) ) ) server <- function(input, output, session) { rv <- reactiveValues(data = MASS::Cars93[c(1, 2, 3, 9, 10, 11, 16, 26, 27)]) observe( updateSelectInput(inputId = "var", choices = names(rv$data)) ) # Inline mode data_inline_r <- update_factor_server( id = "id", data_r = reactive(rv$data) ) observeEvent(data_inline_r(), rv$data <- data_inline_r()) # modal window mode observeEvent(input$modal, modal_update_factor("modal")) data_modal_r <- update_factor_server( id = "modal", data_r = reactive(rv$data) ) observeEvent(data_modal_r(), { shiny::removeModal() rv$data <- data_modal_r() }) # winbox mode observeEvent(input$winbox, winbox_update_factor("winbox")) data_winbox_r <- update_factor_server( id = "winbox", data_r = reactive(rv$data) ) observeEvent(data_winbox_r(), rv$data <- data_winbox_r()) # Plot results output$plot <- renderPlot({ req(input$var, rv$data) ggplot(rv$data) + aes(x = !!sym(input$var)) + geom_bar() }) # Show results output$res <- renderPrint({ data <- req(rv$data) str(data) }) } if (interactive()) shinyApp(ui, server) ================================================ FILE: examples/validation.R ================================================ library(datamods) library(shiny) if (requireNamespace("validate")) { library(validate) # Define some rules to be applied to data myrules <- validator( is.character(Manufacturer) | is.factor(Manufacturer), is.numeric(Price), Price > 12, # we should use 0 for testing positivity, but that's for the example !is.na(Luggage.room), in_range(Cylinders, min = 4, max = 8), Man.trans.avail %in% c("Yes", "No") ) # Add some labels label(myrules) <- c( "Variable Manufacturer must be character", "Variable Price must be numeric", "Variable Price must be strictly positive", "Luggage.room must not contain any missing values", "Cylinders must be between 4 and 8", "Man.trans.avail must be 'Yes' or 'No'" ) # you can also add a description() ui <- fluidPage( tags$h2("Validation"), fluidRow( column( width = 4, radioButtons( inputId = "dataset", label = "Choose dataset:", choices = c("mtcars", "MASS::Cars93") ), tags$p("Dropdown example:"), validation_ui("validation1"), tags$br(), tags$p("Inline example:"), validation_ui("validation2", display = "inline") ), column( width = 8, tags$b("Status:"), verbatimTextOutput("status"), tags$b("Details:"), verbatimTextOutput("details") ) ) ) server <- function(input, output, session) { dataset <- reactive({ if (input$dataset == "mtcars") { mtcars } else { MASS::Cars93 } }) results <- validation_server( id = "validation1", data = dataset, n_row = ~ . > 20, # more than 20 rows n_col = ~ . >= 3, # at least 3 columns rules = myrules ) validation_server( id = "validation2", data = dataset, n_row = ~ . > 20, # more than 20 rows n_col = ~ . >= 3, # at least 3 columns rules = myrules ) output$status <- renderPrint(results$status()) output$details <- renderPrint(results$details()) } if (interactive()) shinyApp(ui, server) } ================================================ FILE: examples/variables.R ================================================ library(shiny) library(datamods) testdata <- data.frame( date_as_char = as.character(Sys.Date() + 0:9), date_as_num = as.numeric(Sys.Date() + 0:9), datetime_as_char = as.character(Sys.time() + 0:9 * 3600*24), datetime_as_num = as.numeric(Sys.time() + 0:9 * 3600*24), num_as_char = as.character(1:10), char = month.name[1:10], char_na = c("A", "A", "B", NA, "B", "A", NA, "B", "A", "B"), stringsAsFactors = FALSE ) ui <- fluidPage( theme = bslib::bs_theme(version = 5L, preset = "bootstrap"), tags$h3("Select, rename and convert variables"), fluidRow( column( width = 6, # radioButtons() update_variables_ui("vars") ), column( width = 6, tags$b("original data:"), verbatimTextOutput("original"), verbatimTextOutput("original_str"), tags$b("Modified data:"), verbatimTextOutput("modified"), verbatimTextOutput("modified_str") ) ) ) server <- function(input, output, session) { updated_data <- update_variables_server( id = "vars", data = reactive(testdata), return_data_on_init = FALSE ) output$original <- renderPrint({ testdata }) output$original_str <- renderPrint({ str(testdata) }) output$modified <- renderPrint({ updated_data() }) output$modified_str <- renderPrint({ str(updated_data()) }) } if (interactive()) shinyApp(ui, server) ================================================ FILE: inst/assets/css/datamods.css ================================================ /*! * Copyright (c) 2020 dreamRs * * datamods, CSS styles * https://github.com/dreamRs/datamods * * @version 0.0.1 */ .show-block { display: block !important; } .show-inline { display: inline !important; } .hidden { display: none !important; } .invisible { visibility: hidden; } .container-rule { position: relative; text-align: center; height: 25px; margin-bottom: 5px; } .horizontal-rule { position: absolute; top: 11px; right: 0; left: 0; background-color: #d0cfcf; height: 1px; z-index: 100; margin: 0; border: none; } .label-rule { background: #FFF; opacity: 1; z-index: 101; background-color: #FFF; position: relative; padding: 0 10px 0 10px; } .datamods-table-container { overflow: auto; word-break: keep-all; white-space: nowrap; } .datamods-table-container > .table { margin-bottom: 0 !important; } .datamods-file-import { display: grid; grid-template-columns: auto 50px; grid-column-gap: 10px; } .datamods-dt-nowrap { word-break: keep-all; white-space: nowrap; } /* validation */ .datamods-validation-results { display: grid; grid-template-columns: repeat(3, 1fr); grid-template-rows: 1fr; height: 50px; line-height: 50px; font-size: large; } .datamods-validation-summary { font-weight: bold; text-align: center; } .datamods-validation-item { font-size: larger; } /* from esquisse */ .btn-column-discrete { background-color: #EF562D; color: #FFFFFF; } .btn-column-continuous { background-color: #0C4C8A; color: #FFFFFF; } .btn-column-datetime { background-color: #97D5E0; color: #FFFFFF; } .btn-column-id { background-color: #848484; color: #FFFFFF; } .btn-column-other { background-color: #2E2E2E; color: #FFFFFF; } ================================================ FILE: inst/assets/js/datamods.js ================================================ /*! * Copyright (c) 2020 dreamRs * * datamods, JavaScript utilities * https://github.com/dreamRs/datamods * * @version 0.0.1 */ /*jshint jquery:true */ /*global Shiny */ // Block or unblock an input widget Shiny.addCustomMessageHandler("datamods-toggleWidget", function(data) { $("#" + data.id).prop("disabled", !data.enable); if ($("#" + data.id).hasClass("selectpicker")) { $("#" + data.id).selectpicker("refresh"); } }); // Hide or show UI component Shiny.addCustomMessageHandler("datamods-showUI", function(data) { var sel = data.selector; if (data.hasOwnProperty("id")) { sel = "#" + $.escapeSelector(data.id); } if (data.inline) { $(sel).addClass("show-inline"); $(sel).removeClass("hidden"); } else { $(sel).addClass("show-block"); $(sel).removeClass("hidden"); } }); Shiny.addCustomMessageHandler("datamods-hideUI", function(data) { var sel = data.selector; if (data.hasOwnProperty("id")) { sel = "#" + $.escapeSelector(data.id); } if (data.inline) { $(sel).addClass("hidden"); $(sel).removeClass("show-inline"); } else { $(sel).addClass("hidden"); $(sel).removeClass("show-block"); } }); function fadeTab(data) { var tabId = $("#" + data.id).attr("data-tabsetid"); $("#" + data.id) .parent() .find(".tab-pane") .each(function(index) { if ($(this).parent().attr("data-tabsetid") == tabId) { $( this ).addClass("fade"); if (index < 1) { $( this ).addClass("in"); } } }); } function updateTabLabel(data) { var el = $("#" + data.id).find("[data-value='" + data.value + "']"); if (typeof el[0] != "undefined") { $(el[0]).html(data.label); } } Shiny.addCustomMessageHandler("datamods-updateTabLabel", updateTabLabel); function disableTab(data) { var el = $("#" + data.id).find("[data-value='" + data.value + "']"); if (typeof el[0] != "undefined") { $(el[0]).removeAttr("data-toggle"); $(el[0]).parent().addClass("disabled"); $(el[0]).addClass("disabled"); } } Shiny.addCustomMessageHandler("datamods-disableTab", disableTab); function enableTab(data) { var el = $("#" + data.id).find("[data-value='" + data.value + "']"); if (typeof el[0] != "undefined") { $(el[0]).attr("data-toggle", "tab"); $(el[0]).parent().removeClass("disabled"); $(el[0]).removeClass("disabled"); } } Shiny.addCustomMessageHandler("datamods-enableTab", enableTab); ================================================ FILE: inst/extdata/mtcars.csv ================================================ mpg,cyl,disp,hp,drat,wt,qsec,vs,am,gear,carb 21,6,160,110,3.9,2.62,16.46,0,1,4,4 21,6,160,110,3.9,2.875,17.02,0,1,4,4 22.8,4,108,93,3.85,2.32,18.61,1,1,4,1 21.4,6,258,110,3.08,3.215,19.44,1,0,3,1 18.7,8,360,175,3.15,3.44,17.02,0,0,3,2 18.1,6,225,105,2.76,3.46,20.22,1,0,3,1 14.3,8,360,245,3.21,3.57,15.84,0,0,3,4 24.4,4,146.7,62,3.69,3.19,20,1,0,4,2 22.8,4,140.8,95,3.92,3.15,22.9,1,0,4,2 19.2,6,167.6,123,3.92,3.44,18.3,1,0,4,4 17.8,6,167.6,123,3.92,3.44,18.9,1,0,4,4 16.4,8,275.8,180,3.07,4.07,17.4,0,0,3,3 17.3,8,275.8,180,3.07,3.73,17.6,0,0,3,3 15.2,8,275.8,180,3.07,3.78,18,0,0,3,3 10.4,8,472,205,2.93,5.25,17.98,0,0,3,4 10.4,8,460,215,3,5.424,17.82,0,0,3,4 14.7,8,440,230,3.23,5.345,17.42,0,0,3,4 32.4,4,78.7,66,4.08,2.2,19.47,1,1,4,1 30.4,4,75.7,52,4.93,1.615,18.52,1,1,4,2 33.9,4,71.1,65,4.22,1.835,19.9,1,1,4,1 21.5,4,120.1,97,3.7,2.465,20.01,1,0,3,1 15.5,8,318,150,2.76,3.52,16.87,0,0,3,2 15.2,8,304,150,3.15,3.435,17.3,0,0,3,2 13.3,8,350,245,3.73,3.84,15.41,0,0,3,4 19.2,8,400,175,3.08,3.845,17.05,0,0,3,2 27.3,4,79,66,4.08,1.935,18.9,1,1,4,1 26,4,120.3,91,4.43,2.14,16.7,0,1,5,2 30.4,4,95.1,113,3.77,1.513,16.9,1,1,5,2 15.8,8,351,264,4.22,3.17,14.5,0,1,5,4 19.7,6,145,175,3.62,2.77,15.5,0,1,5,6 15,8,301,335,3.54,3.57,14.6,0,1,5,8 21.4,4,121,109,4.11,2.78,18.6,1,1,4,2 ================================================ FILE: inst/extdata/mtcars.json ================================================ [{"mpg":21,"cyl":6,"disp":160,"hp":110,"drat":3.9,"wt":2.62,"qsec":16.46,"vs":0,"am":1,"gear":4,"carb":4,"_row":"Mazda RX4"},{"mpg":21,"cyl":6,"disp":160,"hp":110,"drat":3.9,"wt":2.875,"qsec":17.02,"vs":0,"am":1,"gear":4,"carb":4,"_row":"Mazda RX4 Wag"},{"mpg":22.8,"cyl":4,"disp":108,"hp":93,"drat":3.85,"wt":2.32,"qsec":18.61,"vs":1,"am":1,"gear":4,"carb":1,"_row":"Datsun 710"},{"mpg":21.4,"cyl":6,"disp":258,"hp":110,"drat":3.08,"wt":3.215,"qsec":19.44,"vs":1,"am":0,"gear":3,"carb":1,"_row":"Hornet 4 Drive"},{"mpg":18.7,"cyl":8,"disp":360,"hp":175,"drat":3.15,"wt":3.44,"qsec":17.02,"vs":0,"am":0,"gear":3,"carb":2,"_row":"Hornet Sportabout"},{"mpg":18.1,"cyl":6,"disp":225,"hp":105,"drat":2.76,"wt":3.46,"qsec":20.22,"vs":1,"am":0,"gear":3,"carb":1,"_row":"Valiant"},{"mpg":14.3,"cyl":8,"disp":360,"hp":245,"drat":3.21,"wt":3.57,"qsec":15.84,"vs":0,"am":0,"gear":3,"carb":4,"_row":"Duster 360"},{"mpg":24.4,"cyl":4,"disp":146.7,"hp":62,"drat":3.69,"wt":3.19,"qsec":20,"vs":1,"am":0,"gear":4,"carb":2,"_row":"Merc 240D"},{"mpg":22.8,"cyl":4,"disp":140.8,"hp":95,"drat":3.92,"wt":3.15,"qsec":22.9,"vs":1,"am":0,"gear":4,"carb":2,"_row":"Merc 230"},{"mpg":19.2,"cyl":6,"disp":167.6,"hp":123,"drat":3.92,"wt":3.44,"qsec":18.3,"vs":1,"am":0,"gear":4,"carb":4,"_row":"Merc 280"},{"mpg":17.8,"cyl":6,"disp":167.6,"hp":123,"drat":3.92,"wt":3.44,"qsec":18.9,"vs":1,"am":0,"gear":4,"carb":4,"_row":"Merc 280C"},{"mpg":16.4,"cyl":8,"disp":275.8,"hp":180,"drat":3.07,"wt":4.07,"qsec":17.4,"vs":0,"am":0,"gear":3,"carb":3,"_row":"Merc 450SE"},{"mpg":17.3,"cyl":8,"disp":275.8,"hp":180,"drat":3.07,"wt":3.73,"qsec":17.6,"vs":0,"am":0,"gear":3,"carb":3,"_row":"Merc 450SL"},{"mpg":15.2,"cyl":8,"disp":275.8,"hp":180,"drat":3.07,"wt":3.78,"qsec":18,"vs":0,"am":0,"gear":3,"carb":3,"_row":"Merc 450SLC"},{"mpg":10.4,"cyl":8,"disp":472,"hp":205,"drat":2.93,"wt":5.25,"qsec":17.98,"vs":0,"am":0,"gear":3,"carb":4,"_row":"Cadillac Fleetwood"},{"mpg":10.4,"cyl":8,"disp":460,"hp":215,"drat":3,"wt":5.424,"qsec":17.82,"vs":0,"am":0,"gear":3,"carb":4,"_row":"Lincoln Continental"},{"mpg":14.7,"cyl":8,"disp":440,"hp":230,"drat":3.23,"wt":5.345,"qsec":17.42,"vs":0,"am":0,"gear":3,"carb":4,"_row":"Chrysler Imperial"},{"mpg":32.4,"cyl":4,"disp":78.7,"hp":66,"drat":4.08,"wt":2.2,"qsec":19.47,"vs":1,"am":1,"gear":4,"carb":1,"_row":"Fiat 128"},{"mpg":30.4,"cyl":4,"disp":75.7,"hp":52,"drat":4.93,"wt":1.615,"qsec":18.52,"vs":1,"am":1,"gear":4,"carb":2,"_row":"Honda Civic"},{"mpg":33.9,"cyl":4,"disp":71.1,"hp":65,"drat":4.22,"wt":1.835,"qsec":19.9,"vs":1,"am":1,"gear":4,"carb":1,"_row":"Toyota Corolla"},{"mpg":21.5,"cyl":4,"disp":120.1,"hp":97,"drat":3.7,"wt":2.465,"qsec":20.01,"vs":1,"am":0,"gear":3,"carb":1,"_row":"Toyota Corona"},{"mpg":15.5,"cyl":8,"disp":318,"hp":150,"drat":2.76,"wt":3.52,"qsec":16.87,"vs":0,"am":0,"gear":3,"carb":2,"_row":"Dodge Challenger"},{"mpg":15.2,"cyl":8,"disp":304,"hp":150,"drat":3.15,"wt":3.435,"qsec":17.3,"vs":0,"am":0,"gear":3,"carb":2,"_row":"AMC Javelin"},{"mpg":13.3,"cyl":8,"disp":350,"hp":245,"drat":3.73,"wt":3.84,"qsec":15.41,"vs":0,"am":0,"gear":3,"carb":4,"_row":"Camaro Z28"},{"mpg":19.2,"cyl":8,"disp":400,"hp":175,"drat":3.08,"wt":3.845,"qsec":17.05,"vs":0,"am":0,"gear":3,"carb":2,"_row":"Pontiac Firebird"},{"mpg":27.3,"cyl":4,"disp":79,"hp":66,"drat":4.08,"wt":1.935,"qsec":18.9,"vs":1,"am":1,"gear":4,"carb":1,"_row":"Fiat X1-9"},{"mpg":26,"cyl":4,"disp":120.3,"hp":91,"drat":4.43,"wt":2.14,"qsec":16.7,"vs":0,"am":1,"gear":5,"carb":2,"_row":"Porsche 914-2"},{"mpg":30.4,"cyl":4,"disp":95.1,"hp":113,"drat":3.77,"wt":1.513,"qsec":16.9,"vs":1,"am":1,"gear":5,"carb":2,"_row":"Lotus Europa"},{"mpg":15.8,"cyl":8,"disp":351,"hp":264,"drat":4.22,"wt":3.17,"qsec":14.5,"vs":0,"am":1,"gear":5,"carb":4,"_row":"Ford Pantera L"},{"mpg":19.7,"cyl":6,"disp":145,"hp":175,"drat":3.62,"wt":2.77,"qsec":15.5,"vs":0,"am":1,"gear":5,"carb":6,"_row":"Ferrari Dino"},{"mpg":15,"cyl":8,"disp":301,"hp":335,"drat":3.54,"wt":3.57,"qsec":14.6,"vs":0,"am":1,"gear":5,"carb":8,"_row":"Maserati Bora"},{"mpg":21.4,"cyl":4,"disp":121,"hp":109,"drat":4.11,"wt":2.78,"qsec":18.6,"vs":1,"am":1,"gear":4,"carb":2,"_row":"Volvo 142E"}] ================================================ FILE: inst/extdata/mtcars_fr.csv ================================================ mpg;cyl;disp;hp;drat;wt;qsec;vs;am;gear;carb 21,0;6;160,0;110;3,90;2,620;16,46;0;1;4;4 21,0;6;160,0;110;3,90;2,875;17,02;0;1;4;4 22,8;4;108,0;93;3,85;2,320;18,61;1;1;4;1 21,4;6;258,0;110;3,08;3,215;19,44;1;0;3;1 18,7;8;360,0;175;3,15;3,440;17,02;0;0;3;2 18,1;6;225,0;105;2,76;3,460;20,22;1;0;3;1 14,3;8;360,0;245;3,21;3,570;15,84;0;0;3;4 24,4;4;146,7;62;3,69;3,190;20,00;1;0;4;2 22,8;4;140,8;95;3,92;3,150;22,90;1;0;4;2 19,2;6;167,6;123;3,92;3,440;18,30;1;0;4;4 17,8;6;167,6;123;3,92;3,440;18,90;1;0;4;4 16,4;8;275,8;180;3,07;4,070;17,40;0;0;3;3 17,3;8;275,8;180;3,07;3,730;17,60;0;0;3;3 15,2;8;275,8;180;3,07;3,780;18,00;0;0;3;3 10,4;8;472,0;205;2,93;5,250;17,98;0;0;3;4 10,4;8;460,0;215;3,00;5,424;17,82;0;0;3;4 14,7;8;440,0;230;3,23;5,345;17,42;0;0;3;4 32,4;4;78,7;66;4,08;2,200;19,47;1;1;4;1 30,4;4;75,7;52;4,93;1,615;18,52;1;1;4;2 33,9;4;71,1;65;4,22;1,835;19,90;1;1;4;1 21,5;4;120,1;97;3,70;2,465;20,01;1;0;3;1 15,5;8;318,0;150;2,76;3,520;16,87;0;0;3;2 15,2;8;304,0;150;3,15;3,435;17,30;0;0;3;2 13,3;8;350,0;245;3,73;3,840;15,41;0;0;3;4 19,2;8;400,0;175;3,08;3,845;17,05;0;0;3;2 27,3;4;79,0;66;4,08;1,935;18,90;1;1;4;1 26,0;4;120,3;91;4,43;2,140;16,70;0;1;5;2 30,4;4;95,1;113;3,77;1,513;16,90;1;1;5;2 15,8;8;351,0;264;4,22;3,170;14,50;0;1;5;4 19,7;6;145,0;175;3,62;2,770;15,50;0;1;5;6 15,0;8;301,0;335;3,54;3,570;14,60;0;1;5;8 21,4;4;121,0;109;4,11;2,780;18,60;1;1;4;2 ================================================ FILE: inst/extdata/pop-fra-dep.txt ================================================ Code région Nom de la région Code département Nom du département Nombre d'arrondissements Nombre de cantons Nombre de communes Population municipale Population totale 84 Auvergne-Rhône-Alpes 01 Ain 4 23 393 643 350 659 180 32 Hauts-de-France 02 Aisne 5 21 800 534 490 546 527 84 Auvergne-Rhône-Alpes 03 Allier 3 19 317 337 988 347 035 93 Provence-Alpes-Côte d'Azur 04 Alpes-de-Haute-Provence 4 15 198 163 915 168 381 93 Provence-Alpes-Côte d'Azur 05 Hautes-Alpes 2 15 162 141 284 145 883 93 Provence-Alpes-Côte d'Azur 06 Alpes-Maritimes 2 27 163 1 083 310 1 097 496 84 Auvergne-Rhône-Alpes 07 Ardèche 3 17 335 325 712 334 688 44 Grand Est 08 Ardennes 4 19 449 273 579 280 032 76 Occitanie 09 Ariège 3 13 327 153 153 157 210 44 Grand Est 10 Aube 3 17 431 310 020 317 118 76 Occitanie 11 Aude 3 19 433 370 260 379 094 76 Occitanie 12 Aveyron 3 23 285 279 206 289 488 93 Provence-Alpes-Côte d'Azur 13 Bouches-du-Rhône 4 29 119 2 024 162 2 048 504 28 Normandie 14 Calvados 4 25 527 694 002 708 344 84 Auvergne-Rhône-Alpes 15 Cantal 3 15 246 145 143 150 185 75 Nouvelle-Aquitaine 16 Charente 3 19 366 352 335 361 539 75 Nouvelle-Aquitaine 17 Charente-Maritime 5 27 463 644 303 659 968 24 Centre-Val de Loire 18 Cher 3 19 287 304 256 311 456 75 Nouvelle-Aquitaine 19 Corrèze 3 19 280 241 464 249 135 94 Corse 2A Corse-du-Sud 2 11 124 157 249 159 768 94 Corse 2B Haute-Corse 3 15 236 177 689 180 465 27 Bourgogne-Franche-Comté 21 Côte-d'Or 3 23 700 533 819 545 798 53 Bretagne 22 Côtes-d'Armor 4 27 348 598 814 617 107 75 Nouvelle-Aquitaine 23 Creuse 2 15 256 118 638 122 133 75 Nouvelle-Aquitaine 24 Dordogne 4 25 505 413 606 424 095 27 Bourgogne-Franche-Comté 25 Doubs 3 19 573 539 067 552 643 84 Auvergne-Rhône-Alpes 26 Drôme 3 19 364 511 553 524 574 28 Normandie 27 Eure 3 23 585 601 843 614 926 24 Centre-Val de Loire 28 Eure-et-Loir 4 15 365 433 233 443 538 53 Bretagne 29 Finistère 4 27 277 909 028 933 992 76 Occitanie 30 Gard 3 23 351 744 178 757 764 76 Occitanie 31 Haute-Garonne 3 27 586 1 362 672 1 385 122 76 Occitanie 32 Gers 3 17 461 191 091 197 953 75 Nouvelle-Aquitaine 33 Gironde 6 33 535 1 583 384 1 607 545 76 Occitanie 34 Hérault 3 25 342 1 144 892 1 162 867 53 Bretagne 35 Ille-et-Vilaine 4 27 333 1 060 199 1 084 554 24 Centre-Val de Loire 36 Indre 4 13 241 222 232 227 999 24 Centre-Val de Loire 37 Indre-et-Loire 3 19 272 606 511 618 820 84 Auvergne-Rhône-Alpes 38 Isère 3 29 512 1 258 722 1 283 384 27 Bourgogne-Franche-Comté 39 Jura 3 17 494 260 188 269 344 75 Nouvelle-Aquitaine 40 Landes 2 15 327 407 444 419 709 24 Centre-Val de Loire 41 Loir-et-Cher 3 15 267 331 915 340 499 84 Auvergne-Rhône-Alpes 42 Loire 3 21 323 762 941 777 328 84 Auvergne-Rhône-Alpes 43 Haute-Loire 3 19 257 227 283 234 190 52 Pays de la Loire 44 Loire-Atlantique 3 31 207 1 394 909 1 423 152 24 Centre-Val de Loire 45 Loiret 3 21 326 678 105 692 540 76 Occitanie 46 Lot 3 17 313 173 828 179 556 75 Nouvelle-Aquitaine 47 Lot-et-Garonne 4 21 319 332 842 341 270 76 Occitanie 48 Lozère 2 13 152 76 601 80 240 52 Pays de la Loire 49 Maine-et-Loire 4 21 177 813 493 833 154 28 Normandie 50 Manche 4 27 446 496 883 512 923 44 Grand Est 51 Marne 4 23 613 568 895 580 671 44 Grand Est 52 Haute-Marne 3 17 426 175 640 180 753 52 Pays de la Loire 53 Mayenne 3 17 242 307 445 316 750 44 Grand Est 54 Meurthe-et-Moselle 4 23 591 733 481 745 300 44 Grand Est 55 Meuse 3 17 499 187 187 192 588 53 Bretagne 56 Morbihan 3 21 250 750 863 771 911 44 Grand Est 57 Moselle 5 27 725 1 043 522 1 062 217 27 Bourgogne-Franche-Comté 58 Nièvre 4 17 309 207 182 212 742 32 Hauts-de-France 59 Nord 6 41 648 2 604 361 2 635 255 32 Hauts-de-France 60 Oise 4 21 679 824 503 841 948 28 Normandie 61 Orne 3 21 385 283 372 291 557 32 Hauts-de-France 62 Pas-de-Calais 7 39 890 1 468 018 1 489 983 84 Auvergne-Rhône-Alpes 63 Puy-de-Dôme 5 31 464 653 742 668 301 75 Nouvelle-Aquitaine 64 Pyrénées-Atlantiques 3 27 546 677 309 695 965 76 Occitanie 65 Hautes-Pyrénées 3 17 469 228 530 234 591 76 Occitanie 66 Pyrénées-Orientales 3 17 226 474 452 482 368 44 Grand Est 67 Bas-Rhin 5 23 514 1 125 559 1 141 511 44 Grand Est 68 Haut-Rhin 4 17 366 764 030 777 917 84 Auvergne-Rhône-Alpes 69 Rhône 2 13 267 1 843 319 1 869 599 27 Bourgogne-Franche-Comté 70 Haute-Saône 2 17 539 236 659 243 264 27 Bourgogne-Franche-Comté 71 Saône-et-Loire 5 29 565 553 595 569 531 52 Pays de la Loire 72 Sarthe 3 21 354 566 506 579 650 84 Auvergne-Rhône-Alpes 73 Savoie 3 19 273 431 174 443 787 84 Auvergne-Rhône-Alpes 74 Haute-Savoie 4 17 279 807 360 828 417 11 Île-de-France 75 Paris 1 1 2 187 526 2 204 773 28 Normandie 76 Seine-Maritime 3 35 708 1 254 378 1 275 559 11 Île-de-France 77 Seine-et-Marne 5 23 507 1 403 997 1 420 469 11 Île-de-France 78 Yvelines 4 21 259 1 438 266 1 463 091 75 Nouvelle-Aquitaine 79 Deux-Sèvres 3 17 256 374 351 384 479 32 Hauts-de-France 80 Somme 4 23 772 572 443 582 464 76 Occitanie 81 Tarn 2 23 314 387 890 398 412 76 Occitanie 82 Tarn-et-Garonne 2 15 195 258 349 264 130 93 Provence-Alpes-Côte d'Azur 83 Var 3 23 153 1 058 740 1 075 653 93 Provence-Alpes-Côte d'Azur 84 Vaucluse 3 17 151 559 479 570 762 52 Pays de la Loire 85 Vendée 3 17 258 675 247 693 455 75 Nouvelle-Aquitaine 86 Vienne 3 19 266 436 876 447 150 75 Nouvelle-Aquitaine 87 Haute-Vienne 3 21 195 374 426 381 379 44 Grand Est 88 Vosges 3 17 507 367 673 378 986 27 Bourgogne-Franche-Comté 89 Yonne 3 21 423 338 291 346 902 27 Bourgogne-Franche-Comté 90 Territoire de Belfort 1 9 101 142 622 145 640 11 Île-de-France 91 Essonne 3 21 194 1 296 130 1 310 599 11 Île-de-France 92 Hauts-de-Seine 3 23 36 1 609 306 1 625 917 11 Île-de-France 93 Seine-Saint-Denis 3 21 40 1 623 111 1 630 133 11 Île-de-France 94 Val-de-Marne 3 25 47 1 387 926 1 397 035 11 Île-de-France 95 Val-d'Oise 3 21 184 1 228 618 1 239 262 01 Guadeloupe 971 Guadeloupe 2 21 32 390 253 396 153 02 Martinique 972 Martinique 4 34 372 594 377 711 03 Guyane 973 Guyane 2 22 268 700 271 124 04 La Réunion 974 La Réunion 4 25 24 853 659 863 063 ================================================ FILE: inst/extdata/rules.yaml ================================================ rules: - expr: speed >= 0 name: 'speed' label: 'speed positivity' description: | speed can not be negative created: 2020-11-02 11:15:11 meta: language: validate 0.9.3.36 severity: error - expr: dist >= 0 name: 'dist' label: 'distance positivity' description: | distance cannot be negative. created: 2020-11-02 11:15:11 meta: language: validate 0.9.3.36 severity: error - expr: speed/dist <= 1.5 name: 'ratio' label: 'ratio limit' description: | The speed to distance ratio can not exceed 1.5. created: 2020-11-02 11:15:11 meta: language: validate 0.9.3.36 severity: error ================================================ FILE: inst/i18n/al.csv ================================================ "label","translation","comment" "% of the total","% e totalit","Automatically translated" "% of the total, i.e.","% e totalit, d.m.th.","Automatically translated" "A shareable link, in that case first sheet will be read","Linku që mund të ndahet, në këtë rast do të lexohet fleta e parë","" "Add a label to data","Shtoni një etiketë në të dhëna","#TODO #CHECK" "Add a row","Shtoni një rresht","" "Apply changes","Apliko ndryshimet","" "Browse...","Shfletoni...","" "Cancel","Anulo","" "Choose a name for the column to be created or modified,","Zgjidhni një emër për kolonën që do të krijohet ose modifikohet,","Automatically translated" "Choose a number of rows :","Zgjidhni një numër rreshtash:","Automatically translated" "Choose a percentage :","Zgjidhni një përqindje:","Automatically translated" "Click on a column name to add it to the expression:","Klikoni në emrin e një kolone për ta shtuar atë në shprehjen:","Automatically translated" "Click to delete","Klikoni për të fshirë","" "Click to edit","Klikoni për të redaktuar","" "Close","Мbyll","" "Close intervals on the right","Mbyllni intervalet në të djathtë","Automatically translated" "Column added!","Kolona u shtua!","Automatically translated" "Convert Numeric to Factor","Konvertoni numerik në faktor","Automatically translated" "Copy & paste data","Kopjoni dhe ngjisni të dhënat","" "Copy / Paste","Kopjoni / ngjisni","" "Count","Numëroni","Automatically translated" "Create a new column","Krijo një kolonë të re","Automatically translated" "Create a new variable otherwise replaces the one selected","Krijoni një variabël të ri përndryshe zëvendëson atë të zgjedhur","Automatically translated" "Create column","Krijo kolonë","Automatically translated" "Create factor variable","Krijo variabël faktor","Automatically translated" "Data has %s observations and %s variables.","Të dhënat kanë %s vëzhgime dhe %s variabla","" "Data ready to be imported!","Të dhënat që janë gati për t'u importuar","" "Data successfully imported!","Të dhënat u importuan me sukses","" "Data successfully updated!","Të dhënat janë azhurnuar me sukses!","" "Data wasn't deleted","Të dhënat nuk u fshinë","Automatically translated" "Data wasn't updated","Të dhënat nuk u përditësuan","Automatically translated" "Dataset validation:","Vërtetimi i grupit të të dhënave:","Automatically translated" "Date format:","Formati i datës:","" "Date to use as origin to convert date/datetime:","Data që do të përdoret për të konvertuar ndryshoren date/datetime","" "Decimal separator:","Ndarësi dhjetor:","" "Delete","Fshije","" "Do you want to delete the selected row ?","Dëshironi të fshini rreshtin e zgjedhur?","" "Encoding:","Kodimi:","" "Enter URL to data:","Futni URL-në e të dhënave:","Automatically translated" "Enter a shareable link to a GoogleSheet:","Futni një link që mund të ndahet në GoogleSheet:","" "Enter an expression to define new column:","Futni një shprehje për të përcaktuar kolonën e re:","Automatically translated" "Environment","Mjedisi","" "Error","Gabim","" "External file","Skedari i jashtëm","" "Factor variable to reorder:","Ndryshorja e faktorit për të rirenditur:","Automatically translated" "Failed","Dështim","" "First five rows are shown below:","Pesë rreshtat e parë janë treguar më poshtë:","" "Googlesheets","Googlesheets","" "Group calculation by:","Llogaritja e grupit sipas:","Automatically translated" "Help","Ndihmë","" "How to import data?","Si të importoni të dhëna?","" "Import","Importo","" "Import Google Spreadsheet","Importoni GoogleSheet","" "Import Url","Importo Url","Automatically translated" "Import a dataset from an environment","Importoni një grup të dhënash nga mjedisi","" "Import a file","Importoni skedar","" "Import data","Importoni të dhënat","" "Imported data","Të dhëna të importuara","Automatically translated" "Include lowest value","Përfshi vlerën më të ulët","Automatically translated" "Information","Informacion","" "Item has been modified","Artikulli është modifikuar","" "Levels","Nivelet","Automatically translated" "List of data.frame...","Lista e tabelave të të dhënave (data.frame)","" "Max:","Maksimumi:","Automatically translated" "Mean:","Mesatarja:","Automatically translated" "Method:","Metoda:","Automatically translated" "Min:","Min.:","Automatically translated" "Missing values characters:","Karakteret e vlerave që mungojnë:","Automatically translated" "Missing:","Mungon:","Automatically translated" "Most Common:","Më e zakonshme:","Automatically translated" "New column name cannot be empty","Emri i kolonës së re nuk mund të jetë bosh","Automatically translated" "New column name:","Emri i kolonës së re:","Automatically translated" "No","Nr","" "No data selected!","Nuk ka të dhëna të zgjedhura!","" "No data to display.","Nuk ka të dhëna për t’u shfaqur.","" "No data.frame here...","Këtu nuk ka të dhëna (data.frame) ...","" "No file selected","Nuk është zgjedhur asnjë dokument","" "No file selected:","Nuk është zgjedhur asnjë dokument:","" "Not a data.frame","Jo një të dhëna.kornizë","Automatically translated" "Nothing pasted yet!","Asgjë nuk është ngjitur deri më tani!","" "Number of breaks:","Numri i pushimeve:","Automatically translated" "Number of rows:","Numri i rreshtave:","" "OK","Në rregull","" "Ooops","Ups","" "Paste data here:","Ngjitni të dhënat tuaja këtu:","" "Please copy and paste some data in the dialog box above.","Ju lutemi kopjoni dhe ngjisni disa të dhëna në dritaren e dialogut më poshtë","" "Please fill in the required fields","Ju lutemi plotësoni fushat e kërkuara","" "Please paste a valid GoogleSheet link in the dialog box above.","Ju lutemi ngjisni një link të vlefshëm në GoogleSheet në dritaren e dialogut më poshtë.","" "Please paste a valid link in the dialog box above.","Ju lutemi ngjitni një lidhje të vlefshme në kutinë e dialogut të mësipërm.","Automatically translated" "Registered","I regjistruar","" "Required field","Fusha e kërkuar","" "Row has been saved","Rreshti është ruajtur","" "Row was not deleted","Rreshti nuk u fshi","" "Rows to skip before reading data:","Rreshtat për t'u kapërcyer përpara se të lexohen të dhënat:","Automatically translated" "Sample data by :","Mostra e të dhënave sipas:","Automatically translated" "Save","Ruaj","" "Select","Zgjidhni","" "Select a data.frame:","Zgjidhni data.frame","" "Select an environment in which to search:","Zgjidhni një mjedis në të cilin do të kërkoni","" "Select environment","Zgjidhni një mjedis","" "Select sheet to import:","Zgjidhni një fletë për ta importuar","" "Some operations are not allowed","Disa operacione nuk lejohen","Automatically translated" "Something went wrong...","Diçka shkoi keq...","" "Sort count","Rendit numërimin","Automatically translated" "The URL that appear in your browser, in that case the current sheet will be read","URL-ja që shfaqet në shfletuesin tuaj, në këtë rast do të lexohet fleta e parë","" "The row has been deleted","Rreshti është fshirë","" "The row wasn't added to the data","Rreshti nuk u shtua te të dhënat","Automatically translated" "URL","URL","Automatically translated" "Unable to add the row, contact the platform administrator","Rreshti nuk mund të shtohet, kontakto me administratorin e platformës","" "Unable to delete the row, contact platform administrator","Rreshti nuk mund të fshihet, kontakto me administratorin e platformës","" "Unable to modify the item, contact the platform administrator","Artikulli nuk mund të modifikohet, kontaktoni administratorin e platformës","" "Unique values:","Vlerat unike:","Automatically translated" "Unique:","Unike:","Automatically translated" "Update","Azhurno","" "Update & select variables","Azhurnoni dhe zgjidhni variablat","" "Update factor variable","Përditëso variablin e faktorit","Automatically translated" "Update levels of a factor","Përditëso nivelet e një faktori","Automatically translated" "Update row","Përditëso rreshtin","" "Upload a file:","Bashkangjitni një dokument:","" "Use a data.frame from your environment or from the environment of a package.","Përdorni data.frame nga mjedisi juaj ose nga mjedisi i paketës","" "Valid number of columns","Numri i vlefshëm i kolonave","Automatically translated" "Valid number of rows","Numri i vlefshëm i rreshtave","Automatically translated" "Validate","Verifiko","" "Validation:","Verifikimi:","" "Variable to cut:","Ndryshore për prerje:","Automatically translated" "View","Shiko","" "Warning","Paralajmërim","Automatically translated" "Yes","po","" "You can either use:","Mund ta përdorni edhe ndonjërën nga këto:","" "You can import %s files","Mund të importoni dokumente %s","" "You can import from flat table format supported by","Mund të importoni nga formati i tabelës së sheshtë i mbështetur nga","Automatically translated" "click to see data","Klikoni këtu për t’i parë të dhënat","" "data has %s obs. of %s variables.","Të dhënat kanë %vëzhgime dhe %variabla","" "if several use a comma ',' to separate them","nëse disa përdorin presje ',' për t'i ndarë","Automatically translated" "lines, i.e.","vijat, d.m.th.","Automatically translated" "number of rows","numri i rreshtave","Automatically translated" "proportion of rows","proporcioni i rreshtave","Automatically translated" "rows","rreshtave","Automatically translated" "then enter an expression before clicking on the button above to validate or on ","pastaj futni një shprehje përpara se të klikoni në butonin e mësipërm për të vërtetuar ose aktivizuar","Automatically translated" "to delete it.","për ta fshirë.","Automatically translated" "Sort by count","Rendit sipas numërimit","Automatically translated" "Sort by levels","Rendit sipas niveleve","Automatically translated" ================================================ FILE: inst/i18n/cn.csv ================================================ "label","translation","comment" "% of the total","鍗犳€绘暟鐨�%","Automatically translated" "% of the total, i.e.","鍗犳€绘暟鐨�%锛屽嵆","Automatically translated" "A shareable link, in that case first sheet will be read","文件共享链接,我们将导入首个工作簿的数据","" "Add a label to data","添加数据标签","" "Add a row","添加一行","" "Apply changes","应用更改","" "Browse...","浏览...","" "Cancel","取消","" "Choose a name for the column to be created or modified,","涓鸿鍒涘缓鎴栦慨鏀圭殑鍒楅€夋嫨涓€涓悕绉帮紝","Automatically translated" "Choose a number of rows :","閫夋嫨琛屾暟锛�","Automatically translated" "Choose a percentage :","閫夋嫨涓€涓櫨鍒嗘瘮锛�","Automatically translated" "Click on a column name to add it to the expression:","鍗曞嚮鍒楀悕绉板皢鍏舵坊鍔犲埌琛ㄨ揪寮忎腑锛�","Automatically translated" "Click to delete","点击删除","" "Click to edit","点击编辑","" "Close","取消","" "Close intervals on the right","鍙宠竟闂撮殧寰堣繎","Automatically translated" "Column added!","涓撴爮宸叉坊鍔狅紒","Automatically translated" "Convert Numeric to Factor","灏嗘暟瀛楄浆鎹负鍥犲瓙","Automatically translated" "Copy & paste data","复制粘贴数据","" "Copy / Paste","复制粘贴","" "Count","鏁版暟","Automatically translated" "Create a new column","鍒涘缓涓€涓柊鍒�","Automatically translated" "Create a new variable otherwise replaces the one selected","鍒涘缓涓€涓柊鍙橀噺锛屽惁鍒欐浛鎹㈡墍閫夊彉閲�","Automatically translated" "Create column","鍒涘缓涓撴爮","Automatically translated" "Create factor variable","鍒涘缓鍥犲瓙鍙橀噺","Automatically translated" "Data has %s observations and %s variables.","数据有 %s 行 %s 列.","" "Data ready to be imported!","数据等待导入!","" "Data successfully imported!","数据导入成功!","" "Data successfully updated!","数据上传成功!","" "Data wasn't deleted","鏁版嵁娌℃湁琚垹闄�","Automatically translated" "Data wasn't updated","鏁版嵁鏈洿鏂�","Automatically translated" "Dataset validation:","鏁版嵁闆嗛獙璇侊細","Automatically translated" "Date format:","日期格式:","" "Date to use as origin to convert date/datetime:","日期转换的起始时间:","" "Decimal separator:","小数点:""""""""""""""""","" "Delete","删除","" "Do you want to delete the selected row ?","您要删除选定的行吗?","" "Encoding:","文件编码:","" "Enter URL to data:","杈撳叆鏁版嵁鐨� URL锛�","Automatically translated" "Enter a shareable link to a GoogleSheet:","输入GoogleSheet的共享链接:","" "Enter an expression to define new column:","杈撳叆琛ㄨ揪寮忔潵瀹氫箟鏂板垪锛�","Automatically translated" "Environment","环境","" "Error","错误","" "External file","外部文件","" "Factor variable to reorder:","瑕侀噸鏂版帓搴忕殑鍥犲瓙鍙橀噺锛�","Automatically translated" "Failed","失败","" "First five rows are shown below:","如下是前5行数据:","" "Googlesheets","Googlesheets","" "Group calculation by:","鍒嗙粍璁$畻鏂瑰紡锛�","Automatically translated" "Help","帮助","" "How to import data?","如何导入数据?","" "Import","导入","" "Import Google Spreadsheet","导入Google数据文件","" "Import Url","瀵煎叆缃戝潃","Automatically translated" "Import a dataset from an environment","从环境中导入数据","" "Import a file","导入文件","" "Import data","导入数据","" "Imported data","瀵煎叆鏁版嵁","Automatically translated" "Include lowest value","鍖呮嫭鏈€浣庡€�","Automatically translated" "Information","信息","" "Item has been modified","项目已修改","" "Levels","绾у埆","Automatically translated" "List of data.frame...","所有数据文件","" "Max:","鏈€澶ч檺搴︼細","Automatically translated" "Mean:","鎰忔€濇槸锛�","Automatically translated" "Method:","鏂规硶锛�","Automatically translated" "Min:","鍒嗛挓锛�","Automatically translated" "Missing values characters:","缂哄け鍊煎瓧绗︼細","Automatically translated" "Missing:","涓㈠け鐨勶細","Automatically translated" "Most Common:","鏈€甯歌鐨勶細","Automatically translated" "New column name cannot be empty","鏂板垪鍚嶄笉鑳戒负绌�","Automatically translated" "New column name:","鏂板垪鍚嶇О锛�","Automatically translated" "No","不","" "No data selected!","未选择数据!","" "No data to display.","无数据.","" "No data.frame here...","未检测到数据集...","" "No file selected","尚未选择文件","" "No file selected:","尚未选择文件","" "Not a data.frame","涓嶆槸鏁版嵁妗�","Automatically translated" "Nothing pasted yet!","尚未粘贴任何数据!","" "Number of breaks:","浼戞伅娆℃暟锛�","Automatically translated" "Number of rows:","行数:","" "OK","OK","" "Ooops","啊","" "Paste data here:","粘贴位置:","" "Please copy and paste some data in the dialog box above.","请在上面的对话框中复制粘贴数据.","" "Please fill in the required fields","请填写必填字段","" "Please paste a valid GoogleSheet link in the dialog box above.","链接无效,请核对链接.","" "Please paste a valid link in the dialog box above.","璇峰湪涓婇潰鐨勫璇濇涓矘璐存湁鏁堢殑閾炬帴銆�","Automatically translated" "Registered","挂号的","" "Required field","必填项目","" "Row has been saved","行已保存”","" "Row was not deleted","行未删除","" "Rows to skip before reading data:","璇诲彇鏁版嵁涔嬪墠瑕佽烦杩囩殑琛岋細","Automatically translated" "Sample data by :","鏍锋湰鏁版嵁锛�","Automatically translated" "Save","节省","" "Select","选择","" "Select a data.frame:","选择数据文件:","" "Select an environment in which to search:","选择搜索环境:","" "Select environment","选择环境","" "Select sheet to import:","选择工作簿:","" "Some operations are not allowed","鏈変簺鎿嶄綔鏄笉鍏佽鐨�","Automatically translated" "Something went wrong...","出错了...","" "Sort count","鎺掑簭璁℃暟","Automatically translated" "The URL that appear in your browser, in that case the current sheet will be read","浏览器复制链接,我们将导入当前工作簿的数据","" "The row has been deleted","该行已被删除","" "The row wasn't added to the data","璇ヨ鏈坊鍔犲埌鏁版嵁涓�","Automatically translated" "URL","缃戝潃","Automatically translated" "Unable to add the row, contact the platform administrator","无法添加行,联系平台管理员","" "Unable to delete the row, contact platform administrator","无法删除行,联系平台管理员","" "Unable to modify the item, contact the platform administrator","无法修改物品,联系平台管理员","" "Unique values:","鐙壒鐨勪环鍊艰锛�","Automatically translated" "Unique:","鐙壒鐨勶細","Automatically translated" "Update","更新","" "Update & select variables","选择并更新数据","" "Update factor variable","鏇存柊鍥犲瓙鍙橀噺","Automatically translated" "Update levels of a factor","鏇存柊鍥犲瓙鐨勬按骞�","Automatically translated" "Update row","更新行","" "Upload a file:","上传文件:","" "Use a data.frame from your environment or from the environment of a package.","使用工作环境中或R包中数据集.","" "Valid number of columns","鏈夋晥鍒楁暟","Automatically translated" "Valid number of rows","鏈夋晥琛屾暟","Automatically translated" "Validate","验证","" "Validation:","验证:","" "Variable to cut:","瑕佸壀鍒囩殑鍙橀噺锛�","Automatically translated" "View","预览","" "Warning","璀﹀憡","Automatically translated" "Yes","是的","" "You can either use:","你还可以使用:","" "You can import %s files","你可以上传 %s 个文件","" "You can import from flat table format supported by","鎮ㄥ彲浠ヤ粠鏀寔鐨勫钩闈㈣〃鏍兼牸寮忓鍏�","Automatically translated" "click to see data","数据预览","" "data has %s obs. of %s variables.","数据有 %s 行 %s 列.","" "if several use a comma ',' to separate them","濡傛灉澶氫釜浣跨敤閫楀彿鈥�,鈥濆垎闅�","Automatically translated" "lines, i.e.","绾匡紝鍗�","Automatically translated" "number of rows","琛屾暟","Automatically translated" "proportion of rows","琛岀殑姣斾緥","Automatically translated" "rows","琛�","Automatically translated" "then enter an expression before clicking on the button above to validate or on ","鐒跺悗杈撳叆涓€涓〃杈惧紡锛岀劧鍚庡崟鍑讳笂闈㈢殑鎸夐挳杩涜楠岃瘉鎴�","Automatically translated" "to delete it.","鍒犻櫎瀹冦€�","Automatically translated" "Sort by count","鎸夋暟閲忔帓搴�","Automatically translated" "Sort by levels","鎸夌骇鍒帓搴�","Automatically translated" ================================================ FILE: inst/i18n/de.csv ================================================ "label","translation","comment" "% of the total","% von allen","Automatically translated" "% of the total, i.e.","% der Gesamtmenge, d.h.","Automatically translated" "A shareable link, in that case first sheet will be read","Ein teilbarer Link, in diesem Fall wird das erste Arbeitsblatt eingelesen","" "Add a label to data","Bezeichnung zu Daten hinzufügen","" "Add a row","Fügen Sie eine Zeile hinzu","" "Apply changes","Änderungen übernehmen","" "Browse...","Durchsuchen...","" "Cancel","Absagen","" "Choose a name for the column to be created or modified,","Wählen Sie einen Namen für die Spalte, die erstellt oder geändert werden soll.","Automatically translated" "Choose a number of rows :","Wählen Sie eine Anzahl von Zeilen:","Automatically translated" "Choose a percentage :","Wählen Sie einen Prozentsatz:","Automatically translated" "Click on a column name to add it to the expression:","Klicken Sie auf einen Spaltennamen, um ihn dem Ausdruck hinzuzufügen:","Automatically translated" "Click to delete","Zum Löschen klicken","" "Click to edit","Zum Bearbeiten anklicken","" "Close","Schließen","" "Close intervals on the right","Schließen Sie die Intervalle rechts","Automatically translated" "Column added!","Spalte hinzugefügt!","Automatically translated" "Convert Numeric to Factor","Numerisch in Faktor umwandeln","Automatically translated" "Copy & paste data","Daten hier Kopieren und Einfügen","" "Copy / Paste","Kopieren / Einfügen","" "Count","Zählen","Automatically translated" "Create a new column","Erstellen Sie eine neue Spalte","Automatically translated" "Create a new variable otherwise replaces the one selected","Erstellen Sie eine neue Variable, andernfalls ersetzt sie die ausgewählte","Automatically translated" "Create column","Spalte erstellen","Automatically translated" "Create factor variable","Faktorvariable erstellen","Automatically translated" "Data has %s observations and %s variables.","Daten haben %s Einträge und %s Variablen.","" "Data ready to be imported!","Daten bereit zum Import","" "Data successfully imported!","Daten erfolgreich importiert!","" "Data successfully updated!","Daten Aktualisierung erfolgreich !","" "Data wasn't deleted","Daten wurden nicht gelöscht","Automatically translated" "Data wasn't updated","Daten wurden nicht aktualisiert","Automatically translated" "Dataset validation:","Datensatzvalidierung:","Automatically translated" "Date format:","Datumsformat :","" "Date to use as origin to convert date/datetime:","Ursprungsdatum zur Umwandlung von Datum/Datum-Zeit :","" "Decimal separator:","Dezimaltrennzeichen:","" "Delete","Löschen","" "Do you want to delete the selected row ?","Möchten Sie die ausgewählte Zeile löschen?","" "Encoding:","Codierung:","" "Enter URL to data:","Geben Sie die URL zu den Daten ein:","Automatically translated" "Enter a shareable link to a GoogleSheet:","Teilbaren GoogleSheet Link eingeben:","" "Enter an expression to define new column:","Geben Sie einen Ausdruck ein, um eine neue Spalte zu definieren:","Automatically translated" "Environment","Umgebung","" "Error","Fehler","" "External file","Externe Datei","" "Factor variable to reorder:","Neu anzuordnende Faktorvariable:","Automatically translated" "Failed","Fehlgeschlagen","" "First five rows are shown below:","Die ersten fünf Reihen werden unten angezeigt:","" "Googlesheets","Googlesheets","" "Group calculation by:","Gruppenberechnung nach:","Automatically translated" "Help","Hilfe","" "How to import data?","Wie sollen Daten importiert werden ?","" "Import","Importieren","" "Import Google Spreadsheet","GoogleSheet importieren","" "Import Url","URL importieren","Automatically translated" "Import a dataset from an environment","Importieren Sie ein Datenset aus einer Umgebung","" "Import a file","Datei importieren","" "Import data","Daten importieren","" "Imported data","Importierte Daten","Automatically translated" "Include lowest value","Geben Sie den niedrigsten Wert an","Automatically translated" "Information","Information","" "Item has been modified","Artikel wurde geändert","" "Levels","Ebenen","Automatically translated" "List of data.frame...","Liste der Daten-Tabellen","" "Max:","Maximal:","Automatically translated" "Mean:","Bedeuten:","Automatically translated" "Method:","Methode:","Automatically translated" "Min:","Mindest:","Automatically translated" "Missing values characters:","Fehlende Wertezeichen:","Automatically translated" "Missing:","Fehlen:","Automatically translated" "Most Common:","Am gebräuchlichsten:","Automatically translated" "New column name cannot be empty","Der neue Spaltenname darf nicht leer sein","Automatically translated" "New column name:","Neuer Spaltenname:","Automatically translated" "No","Nein","" "No data selected!","Keine Daten ausgewählt!","" "No data to display.","Keine Daten zum Anzeigen.","" "No data.frame here...","Keine Daten-Tabellen hier...","" "No file selected","Keine Datei ausgewählt","" "No file selected:","Keine Datei ausgewählt","" "Not a data.frame","Kein Datenrahmen","Automatically translated" "Nothing pasted yet!","Noch nichts eingefügt!","" "Number of breaks:","Anzahl Pausen:","Automatically translated" "Number of rows:","Anzahl Reihen :","" "OK","OK","" "Ooops","Ooops","" "Paste data here:","Daten hier einfügen:","" "Please copy and paste some data in the dialog box above.","Bitte Daten in obige Dialog-Box Kopieren und Einfügen.","" "Please fill in the required fields","Bitte füllen Sie die erforderlichen Felder aus","" "Please paste a valid GoogleSheet link in the dialog box above.","Bitte gültigen GoogleSheet Link in die Dialog-Box oben einfügen .","" "Please paste a valid link in the dialog box above.","Bitte fügen Sie einen gültigen Link in das Dialogfeld oben ein.","Automatically translated" "Registered","Eingetragen","" "Required field","Pflichtfeld","" "Row has been saved","Zeile wurde gespeichert","" "Row was not deleted","Zeile wurde nicht gelöscht","" "Rows to skip before reading data:","Vor dem Lesen der Daten zu überspringende Zeilen:","Automatically translated" "Sample data by :","Beispieldaten von:","Automatically translated" "Save","Speichern","" "Select","Auswählen","" "Select a data.frame:","Daten-Tabelle auswählen:","" "Select an environment in which to search:","Wählen Sie eine Umgebung aus, in der gesucht werden soll:","" "Select environment","Umgebung auswählen","" "Select sheet to import:","Arbeitsblatt zum Importieren auswählen:","" "Some operations are not allowed","Einige Vorgänge sind nicht zulässig","Automatically translated" "Something went wrong...","Etwas ist schief gelaufen...","" "Sort count","Anzahl sortieren","Automatically translated" "The URL that appear in your browser, in that case the current sheet will be read","Die URL die im Browser aufscheint, in diesem Fall wird das aktuielle Arbeitsblatt eingelesen","" "The row has been deleted","Die Zeile wurde gelöscht","" "The row wasn't added to the data","Die Zeile wurde nicht zu den Daten hinzugefügt","Automatically translated" "URL","URL","Automatically translated" "Unable to add the row, contact the platform administrator","Die Zeile kann nicht hinzugefügt werden. Wenden Sie sich an den Plattformadministrator","" "Unable to delete the row, contact platform administrator","Die Zeile kann nicht gelöscht werden. Wenden Sie sich an den Plattformadministrator","" "Unable to modify the item, contact the platform administrator","Das Element kann nicht geändert werden. Wenden Sie sich an den Plattformadministrator","" "Unique values:","Einzigartige Werte:","Automatically translated" "Unique:","Einzigartig:","Automatically translated" "Update","Aktualisieren","" "Update & select variables","Variablen aktualisieren & auswählen","" "Update factor variable","Faktorvariable aktualisieren","Automatically translated" "Update levels of a factor","Aktualisieren Sie die Ebenen eines Faktors","Automatically translated" "Update row","Zeile aktualisieren","" "Upload a file:","Datei hochladen :","" "Use a data.frame from your environment or from the environment of a package.","Benutze eine Daten-Tabelle aus der eigenen Umgebung oder der Umgebung eines Pakets.","" "Valid number of columns","Gültige Anzahl von Spalten","Automatically translated" "Valid number of rows","Gültige Anzahl von Zeilen","Automatically translated" "Validate","Validieren","" "Validation:","Validierung :","" "Variable to cut:","Variable zum Schneiden:","Automatically translated" "View","Ansehen","" "Warning","Warnung","Automatically translated" "Yes","Ja","" "You can either use:","Sie können entweder verwenden :","" "You can import %s files","Sie können %s Datein importieren","" "You can import from flat table format supported by","Sie können aus dem von unterstützten Flat-Table-Format importieren","Automatically translated" "click to see data","klicken, um Daten anzuzeigen","" "data has %s obs. of %s variables.","Daten haben %s Einträge und %s Variablen.","" "if several use a comma ',' to separate them","Bei mehreren verwenden Sie zur Trennung ein Komma „,“.","Automatically translated" "lines, i.e.","Linien, d.h.","Automatically translated" "number of rows","Anzahl der Reihen","Automatically translated" "proportion of rows","Anteil der Reihen","Automatically translated" "rows","Reihen","Automatically translated" "then enter an expression before clicking on the button above to validate or on ","Geben Sie dann einen Ausdruck ein, bevor Sie zum Bestätigen oder Einschalten auf die Schaltfläche oben klicken","Automatically translated" "to delete it.","um es zu löschen.","Automatically translated" "Sort by count","Nach Anzahl sortieren","Automatically translated" "Sort by levels","Nach Ebenen sortieren","Automatically translated" ================================================ FILE: inst/i18n/es.csv ================================================ "label","translation","comment" "% of the total","% del total","Automatically translated" "% of the total, i.e.","% del total, es decir","Automatically translated" "A shareable link, in that case first sheet will be read","Un enlace compartible, en ese caso, primera hoja se leerá","" "Add a label to data","Añadir una etiqueta a los datos","" "Add a row","Agregar una fila","" "Apply changes","Aplicar cambios","" "Browse...","Explorar...","" "Cancel","Cancelar","" "Choose a name for the column to be created or modified,","Elija un nombre para la columna que se creará o modificará,","Automatically translated" "Choose a number of rows :","Elija una cantidad de filas:","Automatically translated" "Choose a percentage :","Elige un porcentaje:","Automatically translated" "Click on a column name to add it to the expression:","Haga clic en el nombre de una columna para agregarla a la expresión:","Automatically translated" "Click to delete","Haga clic para eliminar","" "Click to edit","Haz click para editar","" "Close","Cerrar","" "Close intervals on the right","Cerrar intervalos a la derecha","Automatically translated" "Column added!","¡Columna agregada!","Automatically translated" "Convert Numeric to Factor","Convertir numérico a factor","Automatically translated" "Copy & paste data","Copiar y pegar datos","Automatically translated" "Copy / Paste","Copiar / Pegar","" "Count","Contar","Automatically translated" "Create a new column","Crear una nueva columna","Automatically translated" "Create a new variable otherwise replaces the one selected","Crea una nueva variable, de lo contrario reemplaza la seleccionada","Automatically translated" "Create column","Crear columna","Automatically translated" "Create factor variable","Crear variable de factor","Automatically translated" "Data has %s observations and %s variables.","Los datos tienen %s obs. de %s variables.","" "Data ready to be imported!","¡Datos listos para ser importados!","Automatically translated" "Data successfully imported!","Datos importados con éxito!","" "Data successfully updated!","Datos actualizados con éxito!","" "Data wasn't deleted","Los datos no fueron eliminados","Automatically translated" "Data wasn't updated","Los datos no fueron actualizados.","Automatically translated" "Dataset validation:","Validación del conjunto de datos:","Automatically translated" "Date format:","Formato de fecha :","" "Date to use as origin to convert date/datetime:","Fecha a usar como origen para convertir fecha/datetime :","" "Decimal separator:","Separador decimal :","" "Delete","Borrar","" "Do you want to delete the selected row ?","¿Desea eliminar la fila seleccionada?","" "Encoding:","Codificación :","" "Enter URL to data:","Ingrese la URL a los datos:","Automatically translated" "Enter a shareable link to a GoogleSheet:","Introduzca un enlace compartible a un archivo de GoogleSheet:","" "Enter an expression to define new column:","Ingrese una expresión para definir una nueva columna:","Automatically translated" "Environment","Entorno","" "Error","Error","" "External file","Archivo externo","" "Factor variable to reorder:","Variable factorial a reordenar:","Automatically translated" "Failed","Falló","" "First five rows are shown below:","Las cinco primeras filas se muestran a continuación:","" "Googlesheets","Archivos de GoogleSheet","" "Group calculation by:","Cálculo de grupo por:","Automatically translated" "Help","Ayuda","" "How to import data?","¿Cómo importar datos?","" "Import","Importar","" "Import Google Spreadsheet","Importar hoja de cálculo de Google","Automatically translated" "Import Url","Importar URL","Automatically translated" "Import a dataset from an environment","Importar un conjunto de datos desde un entorno","Automatically translated" "Import a file","Importar un archivo","Automatically translated" "Import data","Importar datos","" "Imported data","Datos importados","Automatically translated" "Include lowest value","Incluir el valor más bajo","Automatically translated" "Information","Información","" "Item has been modified","El artículo ha sido modificado","" "Levels","Niveles","Automatically translated" "List of data.frame...","Lista de data.frame...","" "List of data.frame...","Lista de data.frame...","" "Max:","Máximo:","Automatically translated" "Mean:","Significar:","Automatically translated" "Method:","Método:","Automatically translated" "Min:","Mínimo:","Automatically translated" "Missing values characters:","Caracteres de valores faltantes:","Automatically translated" "Missing:","Desaparecido:","Automatically translated" "Most Common:","Más común:","Automatically translated" "New column name cannot be empty","El nombre de la nueva columna no puede estar vacío","Automatically translated" "New column name:","Nuevo nombre de columna:","Automatically translated" "No","No","" "No","No","" "No data selected!","No hay datos seleccionados!","" "No data to display.","No hay datos para mostrar.","" "No data.frame here...","No hay data.frame aquí...","" "No file selected","No hay archivo seleccionado","" "No file selected:","No hay archivo seleccionado :","" "Not a data.frame","No es un marco de datos","Automatically translated" "Nothing pasted yet!","Aún no se ha pegado nada!","" "Number of breaks:","Número de descansos:","Automatically translated" "Number of rows:","Número de filas :","" "OK","Aceptar","" "Ooops","Ups","" "Paste data here:","Pegar datos aquí :","" "Please copy and paste some data in the dialog box above.","Por favor, copie y pegue algún dato en la caja de diálogo anterior.","" "Please fill in the required fields","Por favor llene los campos requeridos","" "Please paste a valid GoogleSheet link in the dialog box above.","Por favor, pegue un enlace válido a un archivo de GoogleSheet en la caja de diálogo anterior.","" "Please paste a valid link in the dialog box above.","Pegue un enlace válido en el cuadro de diálogo de arriba.","Automatically translated" "Registered","Registrado","" "Required field","Campo requerido","" "Row has been saved","Se ha guardado la fila","" "Row was not deleted","La fila no fue eliminada","" "Rows to skip before reading data:","Filas que se deben omitir antes de leer los datos:","Automatically translated" "Sample data by :","Datos de muestra por:","Automatically translated" "Save","Ahorrar","" "Select","Seleccione","" "Select a data.frame:","Seleccionar un data.frame :","" "Select an environment in which to search:","Seleccionar un entorno en el que buscar :","" "Select environment","Seleccionar entorno","" "Select sheet to import:","Seleccionar hoja para importar :","" "Some operations are not allowed","Algunas operaciones no están permitidas.","Automatically translated" "Something went wrong...","Algo salió mal...","" "Sort count","Ordenar recuento","Automatically translated" "The URL that appear in your browser, in that case the current sheet will be read","La URL que aparece en su navegador, en ese caso, la hoja actual se leerá","" "The row has been deleted","La fila ha sido eliminada","" "The row wasn't added to the data","La fila no se agregó a los datos.","Automatically translated" "URL","URL","Automatically translated" "Unable to add the row, contact the platform administrator","No se puede agregar la fila, comuníquese con el administrador de la plataforma","" "Unable to delete the row, contact platform administrator","No se puede eliminar la fila, comuníquese con el administrador de la plataforma","" "Unable to modify the item, contact the platform administrator","No se puede modificar el elemento, comuníquese con el administrador de la plataforma","" "Unique values:","Valores únicos:","Automatically translated" "Unique:","Único:","Automatically translated" "Update","Actualizar","" "Update & select variables","Actualizar y seleccionar variables","Automatically translated" "Update factor variable","Variable de factor de actualización","Automatically translated" "Update levels of a factor","Actualizar niveles de un factor","Automatically translated" "Update row","Actualizar fila","" "Upload a file:","Subir un archivo :","" "Use a data.frame from your environment or from the environment of a package.","Usar un data.frame de su entorno o del entorno de un paquete.","" "Valid number of columns","Número válido de columnas","Automatically translated" "Valid number of rows","Número válido de filas","Automatically translated" "Validate","Validar","" "Validation:","Validación:","Automatically translated" "Variable to cut:","Variable a cortar:","Automatically translated" "View","Visualizar","" "Warning","Advertencia","Automatically translated" "Yes","Sí","" "Yes","Sí","" "You can either use:","Puede usar:","" "You can import %s files","Puede importar %s archivos","" "You can import from flat table format supported by","Puede importar desde el formato de tabla plana compatible con","Automatically translated" "click to see data","click para ver datos","Automatically translated" "data has %s obs. of %s variables.","Los datos tienen %s obs. de %s variables.","" "if several use a comma ',' to separate them","si son varios use una coma ',' para separarlos","Automatically translated" "lines, i.e.","líneas, es decir","Automatically translated" "number of rows","número de filas","Automatically translated" "proportion of rows","proporción de filas","Automatically translated" "rows","filas","Automatically translated" "then enter an expression before clicking on the button above to validate or on ","luego ingrese una expresión antes de hacer clic en el botón de arriba para validar o en","Automatically translated" "to delete it.","para borrarlo.","Automatically translated" "Sort by count","Ordenar por conteo","Automatically translated" "Sort by levels","Ordenar por niveles","Automatically translated" ================================================ FILE: inst/i18n/extract_labels.R ================================================ #' Function to extract labels #' #' @param folder file directory #' #' @return an extraction of the labels contained in the directory files #' @importFrom stringr str_subset str_extract_all str_remove_all #' @export #' #' @examples extract_labels(folder = "R") extract_labels <- function(folder = "R") { files <- list.files(folder) list_extractions <- lapply( X = files, FUN = function(file) { read_file <- readLines(file.path(folder, file)) extraction <- str_extract_all( string = str_subset(read_file, "i18n"), pattern = 'i18n\\(".*?"\\)' ) |> unlist() extraction } ) extract_labels <- unlist(list_extractions, recursive = TRUE) str_remove_all(unique(extract_labels), paste(c("i18n", "\"", "\\)", "\\("), collapse = "|")) } #' Update all csvs that are in inst/i18n #' #' @param labels results of label extractions #' @param lang the language that you want to translate the text into. See polyglotr::google_supported_languages for the Table with the codes of available languages #' @param lang_csv the name of the csv file #' @param translation TRUE or FALSE if you want to translate the language #' @param ... other arguments passed to datamods::translate_labels #' #' @return all csvs updated #' @importFrom data.table merge fwrite data.table fread unique #' @export #' #' @examples update_csv(labels = extract_labels(folder = "R")) #' new_csv_fr <- fread("inst/i18n/fr.csv") update_csv <- function(labels, lang, lang_csv, translation = TRUE, ...) { old <- fread(file = sprintf("inst/i18n/%s.csv", lang_csv), encoding = "UTF-8", fill = TRUE) new <- merge( x = data.table(label = unique(labels)), y = old, by = "label", all.x = TRUE ) if (isTRUE(translation)) { final <- rbind( new[!is.na(translation)], translate_labels(labels = new[is.na(translation)]$label, target_language = lang, ...), fill = TRUE ) } else { final <- new } fwrite(final, file = sprintf("inst/i18n/%s.csv", lang_csv), row.names = FALSE, na = '', quote = TRUE) } #' Translate labels #' #' @param labels labels to translate #' @param source_language the language that you want to translate the text into. See polyglotr::google_supported_languages for the Table with the codes of available languages #' @param target_language the language of the text that you want to translate. See polyglotr::google_supported_languages for the Table with the codes of available languages #' @param encoding Name of encoding. See stringi::stri_enc_list() for a complete list #' #' @importFrom polyglotr google_translate #' @importFrom stringr str_conv #' @importFrom data.table data.table #' #' @return a data frame with translated labels #' @export #' #' @examples translate_labels(labels = extract_labels(folder = "R")) translate_labels <- function(labels, source_language = "en", target_language = "fr", encoding = "UTF-8") { translation <- polyglotr::google_translate( text = labels, target_language = target_language, source_language = source_language ) data.table( label = labels, translation = translation |> unlist() |> str_conv(encoding), comment = "Automatically translated" ) } ================================================ FILE: inst/i18n/fr.csv ================================================ "label","translation","comment" "% of the total","% du total","Automatically translated" "% of the total, i.e.","% du total, soit","Automatically translated" "A shareable link, in that case first sheet will be read","Un lien partageable, dans ce cas la première feuille sera lue","" "Add a label to data","Ajouter un libellé aux données","" "Add a row","Ajouter","" "Apply changes","Appliquer les changements","" "Browse...","Parcourir...","" "Cancel","Annuler","" "Choose a name for the column to be created or modified,","Choisissez un nom pour la colonne à créer ou à modifier,","Automatically translated" "Choose a number of rows :","Choisissez un nombre de lignes :","Automatically translated" "Choose a percentage :","Choisissez un pourcentage :","Automatically translated" "Click on a column name to add it to the expression:","Cliquez sur un nom de colonne pour l'ajouter à l'expression :","Automatically translated" "Click to delete","Cliquez pour supprimer","" "Click to edit","Cliquez pour modifier","" "Close","Fermer","" "Close intervals on the right","Intervalles rapprochés à droite","Automatically translated" "Column added!","Colonne ajoutée !","Automatically translated" "Convert Numeric to Factor","Convertir un numérique en facteur","Automatically translated" "Copy & paste data","Copier / coller des données","" "Copy / Paste","Copier / Coller","" "Count","Compter","Automatically translated" "Create a new column","Créer une nouvelle colonne","Automatically translated" "Create a new variable otherwise replaces the one selected","Créer une nouvelle variable sinon remplace celle sélectionnée","Automatically translated" "Create column","Créer une colonne","Automatically translated" "Create factor variable","Créer une variable de facteur","Automatically translated" "Data has %s observations and %s variables.","Les données ont %s observations et %s variables.","" "Data ready to be imported!","Données prêtes à être importées","" "Data successfully imported!","Données importées avec succès","" "Data successfully updated!","Données mises à jour avec succès !","" "Data wasn't deleted","Les données n'ont pas été supprimées","Automatically translated" "Data wasn't updated","Les données n'ont pas été mises à jour","Automatically translated" "Dataset validation:","Validation de l'ensemble de données :","Automatically translated" "Date format:","Format des dates :","" "Date to use as origin to convert date/datetime:","Date à utiliser comme origine pour convertir des dates au format numérique :","" "Decimal separator:","Séparateur de décimal :","" "Delete","Effacer","" "Do you want to delete the selected row ?","Voulez-vous supprimer la ligne sélectionnée ?","" "Encoding:","Encodage :","" "Enter URL to data:","Entrez l'URL des données :","Automatically translated" "Enter a shareable link to a GoogleSheet:","Entrez un lien partageable vers une GoogleSheet :","" "Enter an expression to define new column:","Entrez une expression pour définir une nouvelle colonne :","Automatically translated" "Environment","Environnement","" "Error","Erreur","" "External file","Fichier","" "Factor variable to reorder:","Variable de facteur à réorganiser :","Automatically translated" "Failed","Echec","" "First five rows are shown below:","Les cinq premières lignes sont affichées ci-dessous :","" "Googlesheets","Googlesheets","" "Group calculation by:","Calcul de groupe par :","Automatically translated" "Help","Aide","" "How to import data?","Comment importer des données ?","" "Import","Import","" "Import Google Spreadsheet","Importer une GoogleSheet","" "Import Url","URL d'importation","Automatically translated" "Import a dataset from an environment","Importer un jeu de données depuis l'environnement global","" "Import a file","Import d'un fichier","" "Import data","Importer les données","" "Imported data","Données importées","Automatically translated" "Include lowest value","Inclure la valeur la plus basse","Automatically translated" "Information","Informations","" "Item has been modified","L'élément a été modifié","" "Levels","Les niveaux","Automatically translated" "List of data.frame...","Liste des tableau de données","" "Max:","Maximale :","Automatically translated" "Mean:","Signifier:","Automatically translated" "Method:","Méthode:","Automatically translated" "Min:","Min :","Automatically translated" "Missing values characters:","Caractères de valeurs manquants :","Automatically translated" "Missing:","Manquant:","Automatically translated" "Most Common:","Le plus commun:","Automatically translated" "New column name cannot be empty","Le nom de la nouvelle colonne ne peut pas être vide","Automatically translated" "New column name:","Nouveau nom de colonne :","Automatically translated" "No","Non","" "No data selected!","Pas de données sélectionnées","" "No data to display.","Pas de données à afficher.","" "No data.frame here...","Pas de données ici...","" "No file selected","Pas de fichier sélectionné","" "No file selected:","Aucun fichier sélectionné","" "Not a data.frame","Pas un data.frame","Automatically translated" "Nothing pasted yet!","Rien n'a encore été collé !","" "Number of breaks:","Nombre de pauses :","Automatically translated" "Number of rows:","Nombre de lignes :","" "OK","OK","" "Ooops","Ooops","" "Paste data here:","Collez vos données ici :","" "Please copy and paste some data in the dialog box above.","Veuillez copier et coller certaines données dans la boîte de dialogue ci-dessus.","" "Please fill in the required fields","Merci de remplir les champs obligatoires","" "Please paste a valid GoogleSheet link in the dialog box above.","Veuillez coller un lien GoogleSheet valide dans la boîte de dialogue ci-dessus.","" "Please paste a valid link in the dialog box above.","Veuillez coller un lien valide dans la boîte de dialogue ci-dessus.","Automatically translated" "Registered","Enregistré","" "Required field","Champs requis","" "Row has been saved","La ligne a été enregistrée","" "Row was not deleted","La ligne n'a pas été supprimée","" "Rows to skip before reading data:","Lignes à ignorer avant de lire les données :","Automatically translated" "Sample data by :","Echantillonner les données selon :","" "Save","Enregistrer","" "Select","Sélectionner","" "Select a data.frame:","Sélectionner un data.frame :","" "Select an environment in which to search:","Sélectionner un environnement dans lequel rechercher :","" "Select environment","Sélectionner un environnement","" "Select sheet to import:","Sélectionner la feuille à importer :","" "Some operations are not allowed","Certaines opérations ne sont pas autorisées","Automatically translated" "Something went wrong...","Un problème est survenu...","" "Sort count","Nombre de tris","Automatically translated" "The URL that appear in your browser, in that case the current sheet will be read","L'URL qui apparaît dans votre navigateur, dans ce cas la feuille active sera lue","" "The row has been deleted","La ligne a été supprimée","" "The row wasn't added to the data","La ligne n'a pas été ajoutée aux données","Automatically translated" "URL","URL","" "Unable to add the row, contact the platform administrator","Impossible d'ajouter la ligne, contactez l'administrateur de la plateforme","" "Unable to delete the row, contact platform administrator","Impossible de supprimer la ligne, contactez l'administrateur de la plateforme","" "Unable to modify the item, contact the platform administrator","Impossible de modifier l'élément, contactez l'administrateur de la plateforme","" "Unique values:","Valeurs uniques :","Automatically translated" "Unique:","Unique:","Automatically translated" "Update","Modifier","" "Update & select variables","Modifier et sélectionner des variables","" "Update factor variable","Variable de facteur de mise à jour","Automatically translated" "Update levels of a factor","Mettre à jour les niveaux d'un facteur","Automatically translated" "Update row","Mettre à jour la ligne","" "Upload a file:","Charger un fichier :","" "Use a data.frame from your environment or from the environment of a package.","Utiliser un tableau de données de votre environnement ou de celui d'un paquet.","" "Valid number of columns","Nombre de colonnes valide","Automatically translated" "Valid number of rows","Nombre de lignes valide","Automatically translated" "Validate","Validation","" "Validation:","Validation :","" "Variable to cut:","Variable à couper :","Automatically translated" "View","Affichage","" "Warning","Avertissement","Automatically translated" "Yes","Oui","" "You can either use:","Vous pouvez utiliser au choix :","" "You can import %s files","Vous pouvez importer des fichers %s","" "You can import from flat table format supported by","Vous pouvez importer à partir du format de tableau plat pris en charge par","Automatically translated" "click to see data","cliquez pour afficher les données","" "data has %s obs. of %s variables.","les données ont %s observations et %s colonnes.","" "if several use a comma ',' to separate them","si plusieurs utilisent une virgule ',' pour les séparer","Automatically translated" "lines, i.e.","lignes, c'est-à-dire","Automatically translated" "number of rows","Nombre de lignes","" "proportion of rows","Proportion de lignes","" "rows","lignes","Automatically translated" "then enter an expression before clicking on the button above to validate or on ","puis saisissez une expression avant de cliquer sur le bouton ci-dessus pour valider ou sur","Automatically translated" "to delete it.","pour le supprimer.","Automatically translated" "Sort by count","Trier par nombre","Automatically translated" "Sort by levels","Trier par niveaux","Automatically translated" ================================================ FILE: inst/i18n/it.csv ================================================ "label","translation","comment" "% of the total","% del totale","Automatically translated" "% of the total, i.e.","% del totale, cioè","Automatically translated" "A shareable link, in that case first sheet will be read","Un link condivisibile, in questo caso sarà letta la prima scheda","" "Add a label to data","Aggiungi un'etichetta ai dati","" "Add a row","Aggiungi una riga","" "Apply changes","Applica le modifiche","" "Browse...","Sfoglia...","" "Cancel","Annulla","" "Choose a name for the column to be created or modified,","Scegli un nome per la colonna da creare o modificare,","Automatically translated" "Choose a number of rows :","Scegli un numero di righe:","Automatically translated" "Choose a percentage :","Scegli una percentuale:","Automatically translated" "Click on a column name to add it to the expression:","Clicca sul nome di una colonna per aggiungerla all'espressione:","Automatically translated" "Click to delete","Clicca per eliminare","" "Click to edit","Clicca per modificare","" "Close","Chiudi","" "Close intervals on the right","Intervalli chiusi a destra","Automatically translated" "Column added!","Colonna aggiunta","Automatically translated" "Convert Numeric to Factor","Coverti una variabile numerica in una discreta","Automatically translated" "Copy & paste data","Copia e incolla i valori","Automatically translated" "Copy / Paste","Copia / Incolla","" "Count","Conteggio","Automatically translated" "Create a new column","Crea una nuova colonna","Automatically translated" "Create a new variable otherwise replaces the one selected","Crea una nuova variabile o sostituisci quella selezionata","Automatically translated" "Create column","Crea una colunna","Automatically translated" "Create factor variable","Crea una variabile discreta","Automatically translated" "Data has %s observations and %s variables.","I dati hanno %s osservazioni e %s variabili.","" "Data ready to be imported!","Dati pronti per l'importazione!","Automatically translated" "Data successfully imported!","Dati importati con successo!","" "Data successfully updated!","Dati aggiornati con successo!","" "Data wasn't deleted","I dati non sono stati eliminati","Automatically translated" "Data wasn't updated","I dati non sono stati aggiornati","Automatically translated" "Dataset validation:","Validazione del dataset:","Automatically translated" "Date format:","Formato della data:","" "Date to use as origin to convert date/datetime:","Data da usare come origine per convertire le date:","" "Decimal separator:","Separatore decimale:","" "Delete","Elimina","" "Do you want to delete the selected row ?","Vuoi eliminare la riga selezionata?","" "Encoding:","Codifica:","" "Enter URL to data:","Inserisci l'URL dei dati:","Automatically translated" "Enter a shareable link to a GoogleSheet:","Inserisci un link condivisibile di GoogleSheet:","" "Enter an expression to define new column:","Inserisci un'espressione per definire una nuova colonna:","Automatically translated" "Environment","Ambiente","" "Error","Errore","" "External file","File","" "Factor variable to reorder:","Variabile discreta da riordinare:","Automatically translated" "Failed","Fallito","" "First five rows are shown below:","Qui sotto sono mostrate le prime cinque righe:","" "Googlesheets","Googlesheets","" "Group calculation by:","Raggruppa per:","Automatically translated" "Help","Aiuto","" "How to import data?","Come importare i dati?","" "Import","Importare","" "Import Google Spreadsheet","Importa da Google Spreadsheet","Automatically translated" "Import Url","URL per l'importazione","Automatically translated" "Import a dataset from an environment","Importa il dataset da un'ambiente","Automatically translated" "Import a file","Importare un file","Automatically translated" "Import data","Importare dati","" "Imported data","Dati importati","Automatically translated" "Include lowest value","Includi il valore minore","Automatically translated" "Information","Informazione","" "Item has been modified","L'elemento è stato modificato","" "Levels","Livelli","Automatically translated" "List of data.frame...","Elenco dei data.frame...","" "Max:","Massimo:","Automatically translated" "Mean:","Media:","Automatically translated" "Method:","Metodo:","Automatically translated" "Min:","Minimo:","Automatically translated" "Missing values characters:","Identificativo per i valori mancanti:","Automatically translated" "Missing:","Mancante:","Automatically translated" "Most Common:","Il più frequente:","Automatically translated" "New column name cannot be empty","Il nome della nuova colonna non può essere vuoto","Automatically translated" "New column name:","Nuovo nome della colonna:","Automatically translated" "No","No","" "No data selected!","Nessun dato selezionato!","" "No data to display.","Nessun dato da mostrare.","" "No data.frame here...","Nessun data.frame...","" "No file selected","Nessun file selezionato","" "No file selected:","Nessun file selezionato:","" "Not a data.frame","Non è un data.frame","Automatically translated" "Nothing pasted yet!","Non è ancora stato incollato nulla!","" "Number of breaks:","Numero di intervalli:","Automatically translated" "Number of rows:","Numero di righe:","" "OK","OK","" "Ooops","Ooops","" "Paste data here:","Incolla i dati qui:","" "Please copy and paste some data in the dialog box above.","Copia e incolla i dati nel riquadro sovrastante.","" "Please fill in the required fields","Riempi i campi obbligatori","" "Please paste a valid GoogleSheet link in the dialog box above.","Incolla un link valido di GoogleSheet nel riquadro sovrastante.","" "Please paste a valid link in the dialog box above.","Incolla un link valido nel riquadro sovrastante.","Automatically translated" "Registered","Registrato","" "Required field","Campo obbligatorio","" "Row has been saved","La riga è stata salvata","" "Row was not deleted","La riga non è stata eliminata","" "Rows to skip before reading data:","Righe da ignorare prima della lettura dei dati:","Automatically translated" "Sample data by :","Campiona i dati per:","Automatically translated" "Save","Salva","" "Select","Seleziona","" "Select a data.frame:","Seleziona un data.frame :","" "Select an environment in which to search:","Seleziona un ambiente in cui cercare:","" "Select environment","Seleziona un ambiente","" "Select sheet to import:","Seleziona una scheda da importare:","" "Some operations are not allowed","Alcune operazioni non sono permesse","Automatically translated" "Something went wrong...","Qualcosa è andato storto...","" "Sort count","Ordina i conteggi","Automatically translated" "The URL that appear in your browser, in that case the current sheet will be read","L'URL nel tuo browser, in tal caso verrà letta la scheda corrente","" "The row has been deleted","La riga è stata eliminata","" "The row wasn't added to the data","La riga non è stata aggiunta ai dati","Automatically translated" "URL","URL","Automatically translated" "Unable to add the row, contact the platform administrator","Impossible aggiungere la riga, contatta l'amministratore del sistema","" "Unable to delete the row, contact platform administrator","Impossible eliminare la riga, contatta l'amministratore del sistema","" "Unable to modify the item, contact the platform administrator","Impossibile modificare l'elemento, contatta l'amministratore del sistema","" "Unique values:","Valori unici:","Automatically translated" "Unique:","Unico:","Automatically translated" "Update","Aggiorna","" "Update & select variables","Aggiorna e seleziona le variabili","Automatically translated" "Update factor variable","Aggiorna la variabile discreta","Automatically translated" "Update levels of a factor","Aggiorna i livelli della variabile discreta","Automatically translated" "Update row","Aggiorna la riga","" "Upload a file:","Carica un file:","" "Use a data.frame from your environment or from the environment of a package.","Usa un data.frame nel tuo ambiente o in un pacchetto.","" "Valid number of columns","Numero di colonne valido","Automatically translated" "Valid number of rows","Numero di righe valido","Automatically translated" "Validate","Validare","" "Validation:","Validazione:","Automatically translated" "Variable to cut:","Variabile da spezzare:","Automatically translated" "View","Visualizza","" "Warning","Attenzione","Automatically translated" "Yes","Sì","" "Yes","Sì","" "You can either use:","Puoi usare:","" "You can import %s files","Puoi importare %s file","" "You can import from flat table format supported by","Puoi importare tabelle nel formato supportato da","Automatically translated" "click to see data","clicca per vedere i dati","Automatically translated" "data has %s obs. of %s variables.","i dati hanno %s osservazioni e %s variabili.","" "if several use a comma ',' to separate them","se sono più d'uno separali con una virgola ','","Automatically translated" "lines, i.e.","righe, cioé","Automatically translated" "number of rows","numero di righe","Automatically translated" "proportion of rows","proporzione delle righe","Automatically translated" "rows","righe","Automatically translated" "then enter an expression before clicking on the button above to validate or on ","quindi inserisci un'espressione prima di fare clic sul pulsante in alto per validare o su","Automatically translated" "to delete it.","per eliminarlo.","Automatically translated" "Sort by count","Ordina per conteggio","Automatically translated" "Sort by levels","Ordina per livello","Automatically translated" ================================================ FILE: inst/i18n/ja.csv ================================================ label,translation,comment "Import a dataset from an environment","Global environment からデータセットをインポート" "Select a data.frame:","データフレームを選択:" "List of data.frame...","データフレームのリスト" "Select an environment in which to search:","検索する environment を選択:" "Select environment","environment を選択" "No data selected!","データが選択されていません!" "Use a data.frame from your environment or from the environment of a package.","ユーザーの環境やパッケージからデータフレームを使用" "No data.frame here...","データフレームがありません "Ooops","おっと" "Something went wrong...","問題が発生しました..." "Data successfully imported!","データは正しくインポートされました!" "Data ready to be imported!","データをインポートする準備ができました!" "data has %s obs. of %s variables.","%s 行 %s 列のデータです。" "click to see data","クリックしてデータを見る" "Import data","データのインポート" "Import a file","ファイルのインポート" "Upload a file:","ファイルのアップロード:" "Browse...","Browse..." "No file selected","ファイルが選択されていません" "Number of rows to skip before reading data:","データを読み込む前にスキップする行数:" "Decimal separator:","小数点:" "Encoding:","エンコード:" "Select sheet to import:","インポートするシートを選択:" "No file selected:","ファイルが選択されていません" "You can import %s files","%s 個のファイルをインポートできます" "First five rows are shown below:","最初の5行は以下の通り:" "Copy & paste data","データをコピー/ペースト" "Paste data here:","ここにデータをペースト:" "Nothing pasted yet!","まだ何もペーストされていません!" "Please copy and paste some data in the dialog box above.","上のダイアログボックスにデータをコピー&ペーストしてください。" "Import Google Spreadsheet","Google スプレッドシートをインポート" "You can either use:","こちらも利用できます:" "A shareable link, in that case first sheet will be read","共有可能なリンク (先頭のシートが読み込まれます)" "The URL that appear in your browser, in that case the current sheet will be read","ブラウザに表示されるリンク (現在のシートが読み込まれます)" "Enter a shareable link to a GoogleSheet:","GoogleSheet への共有可能なリンクを入力 :" "Please paste a valid GoogleSheet link in the dialog box above.","上記のダイアログボックスに有効な GoogleSheet のリンクをペーストしてください。" "Help","ヘルプ" "Environment","Environment" "External file","外部データ" "Copy / Paste","コピー/ペースト" "Googlesheets","Googlesheets" "How to import data?","データをインポートする方法?" "Import","インポート" "View","ビュー" "Update","アップデート" "Validate","検証" "Update & select variables","変数の更新と選択" "Date format:","日付のフォーマット:" "Date to use as origin to convert date/datetime:","date/datetime への変換元として使用する Date:" "Select, rename and convert variables in table above, then apply changes by clicking button below.","上の表で変数を選択、名前変更、変換し、下のボタンをクリックして変更を適用する。" "Apply changes","変更を適用" "Data has %s observations and %s variables.","%s 行 %s 列のデータです。" "Data successfully updated!","データは正しく更新されました!" "Validation:","検証:" "OK","OK" "Failed","失敗" "Error","エラー" "Number of rows:","行数:" "No data to display.","表示するデータがありません" "Close","閉じる" "Add a label to data","データにラベルを追加" "URL","URL" "Add a row","行を追加" "Required field","必須項目" "Please fill in the required fields","必須項目を埋めてください" "Unable to add the row, contact the platform administrator","行を追加できません。プラットフォームの管理者に連絡してください。" "Registered","登録済み" "Row has been saved","行データは保存されました" "Update row","行を更新" "Unable to modify the item, contact the platform administrator","項目を修正できません。プラットフォームの管理者に連絡してください。" "Item has been modified","項目は修正されました" "Delete","削除" "Do you want to delete the selected row ?","選択した行を削除しますか?" "Unable to delete the row, contact platform administrator",""行を削除できません。プラットフォームの管理者に連絡してください。" "The row has been deleted","行は削除されました" "Information","情報" "Row was not deleted","行は削除されませんでした" "Save","保存" "Click to edit","クリックして編集" "Click to delete","クリックして削除" "Cancel","キャンセル" "No","いいえ" "Yes","はい" "Edit data","データを編集" "Select", "選択" "Missing values character(s):","欠損値文字:" "if several use a comma (',') to separate them","複数の場合はカンマ (「,」) を使用して区切ります" ================================================ FILE: inst/i18n/kr.csv ================================================ "label","translation","comment" "% of the total","�꾩껜�� %","Automatically translated" "% of the total, i.e.","�꾩껜�� %, 利�","Automatically translated" "A shareable link, in that case first sheet will be read","공유 가능한 링크에서는 첫번째 Sheet가 사용됩니다.","" "Add a label to data","데이터에 레이블 추가","" "Add a row","행 추가","" "Apply changes","적용","" "Browse...","찾기","" "Cancel","취소","" "Choose a name for the column to be created or modified,","�앹꽦�섍굅�� �섏젙�� �댁쓽 �대쫫�� �좏깮�섍퀬,","Automatically translated" "Choose a number of rows :","�� �섎� �좏깮�섏떗�쒖삤.","Automatically translated" "Choose a percentage :","鍮꾩쑉�� �좏깮�섏꽭��:","Automatically translated" "Click on a column name to add it to the expression:","�� �대쫫�� �대┃�섏뿬 �쒗쁽�앹뿉 異붽��⑸땲��.","Automatically translated" "Click to delete","삭제하려면 클릭하세요.","" "Click to edit","수정하려면 클릭하세요.","" "Close","닫기","" "Close intervals on the right","�ㅻⅨ履쎌쓽 媛꾧꺽 �リ린","Automatically translated" "Column added!","移쇰읆�� 異붽��섏뿀�듬땲��!","Automatically translated" "Convert Numeric to Factor","�レ옄瑜� �몄닔濡� 蹂€��","Automatically translated" "Copy & paste data","�곗씠�� 蹂듭궗 諛� 遺숈뿬�j린","Automatically translated" "Copy / Paste","복사 / 붙여넣기","" "Count","�몃떎","Automatically translated" "Create a new column","�� �� 留뚮뱾湲�","Automatically translated" "Create a new variable otherwise replaces the one selected","�� 蹂€�섎� �앹꽦�섍굅�� 洹몃젃吏€ �딆쑝硫� �좏깮�� 蹂€�섎� �€泥댄빀�덈떎.","Automatically translated" "Create column","�� �앹꽦","Automatically translated" "Create factor variable","�붿씤蹂€�� �앹꽦","Automatically translated" "Data has %s observations and %s variables.","%s행 %s 변수","" "Data ready to be imported!","�곗씠�곕� 媛€�몄삱 以€鍮꾧� �섏뿀�듬땲��!","Automatically translated" "Data successfully imported!","데이터 불러오기 성공","" "Data successfully updated!","데이터 업데이트 성공","" "Data wasn't deleted","�곗씠�곌� ��젣�섏� �딆븯�듬땲��.","Automatically translated" "Data wasn't updated","�곗씠�곌� �낅뜲�댄듃�섏� �딆븯�듬땲��.","Automatically translated" "Dataset validation:","�곗씠�곗꽭�� 寃€利�:","Automatically translated" "Date format:","날짜 포맷:","" "Date to use as origin to convert date/datetime:","날짜/날짜/시간을 변환하기 위해 원 데이터로 사용할 날짜:","" "Decimal separator:","소수점 구분 기호:","" "Delete","삭제","" "Do you want to delete the selected row ?","선택한 행을 삭제하시겠습니까?","" "Encoding:","인코딩:","" "Enter URL to data:","데이터 URL 입력:","" "Enter a shareable link to a GoogleSheet:","공유 가능한 GoogleSheet 링크:","" "Enter an expression to define new column:","�� �댁쓣 �뺤쓽�섎뒗 �쒗쁽�앹쓣 �낅젰�섏꽭��.","Automatically translated" "Environment","환경 선택","" "Error","에러","" "External file","외부 파일","" "Factor variable to reorder:","�ъ젙�ы븷 �붿씤 蹂€��:","Automatically translated" "Failed","실패","" "First five rows are shown below:","泥섏쓬 5媛� �됱� �ㅼ쓬怨� 媛숈뒿�덈떎.","Automatically translated" "Googlesheets","Googlesheets","" "Group calculation by:","洹몃9 怨꾩궛 湲곗�:","Automatically translated" "Help","도움말","" "How to import data?","데이터 불러오기 방법","" "Import","불러오기","" "Import Google Spreadsheet","Google �ㅽ봽�덈뱶�쒗듃 媛€�몄삤湲�","Automatically translated" "Import Url","URL 媛€�몄삤湲�","Automatically translated" "Import a dataset from an environment","�섍꼍�먯꽌 �곗씠�곗꽭�� 媛€�몄삤湲�","Automatically translated" "Import a file","�뚯씪 媛€�몄삤湲�","Automatically translated" "Import data","데이터 불러오기","" "Imported data","媛€�몄삩 �곗씠��","Automatically translated" "Include lowest value","媛€�� ��� 媛� �ы븿","Automatically translated" "Information","정보","" "Item has been modified","항목이 수정되었습니다","" "Levels","�덈꺼","Automatically translated" "List of data.frame...","data.frame 리스트","" "Max:","理쒕�:","Automatically translated" "Mean:","�됯퇏:","Automatically translated" "Method:","諛⑸쾿:","Automatically translated" "Min:","理쒖냼:","Automatically translated" "Missing values characters:","�꾨씫�� 媛� 臾몄옄:","Automatically translated" "Missing:","�놁뼱吏�:","Automatically translated" "Most Common:","媛€�� �뷀븳:","Automatically translated" "New column name cannot be empty","�� �� �대쫫�€ 鍮꾩썙�� �� �놁뒿�덈떎.","Automatically translated" "New column name:","�� �� �대쫫:","Automatically translated" "No","아니","" "No data selected!","데이터 선택되지 않음!","" "No data to display.","화면에 나타낼 데이터 없음.","" "No data.frame here...","여기에 data.frame이 없습니다...","" "No file selected","파일 선택되지 않음:","" "No file selected:","파일 선택되지 않음:","" "Not a data.frame","data.frame�� �꾨떃�덈떎.","Automatically translated" "Nothing pasted yet!","아무것도 붙여넣기되지 않음!","" "Number of breaks:","�댁떇 �잛닔:","Automatically translated" "Number of rows:","열 수","" "OK","OK","" "Ooops","에러","" "Paste data here:","여기에 데이터 붙여넣기:","" "Please copy and paste some data in the dialog box above.","위의 상자에 데이터를 붙여넣으세요.","" "Please fill in the required fields","필수 필드를 작성하십시오","" "Please paste a valid GoogleSheet link in the dialog box above.","위의 상자에 유효한 GoogleSheet 링크를 붙여넣으세요.","" "Please paste a valid link in the dialog box above.","위의 대화 상자에 유효한 링크를 붙여넣으십시오.","" "Registered","등기","" "Required field","필수 입력란","" "Row has been saved","행이 저장되었습니다.","" "Row was not deleted","행이 삭제되지 않았습니다.","" "Rows to skip before reading data:","�곗씠�곕� �쎄린 �꾩뿉 嫄대꼫�� ��:","Automatically translated" "Sample data by :","�섑뵆 �곗씠�� 湲곗�:","Automatically translated" "Save","구하다","" "Select","고르다","" "Select a data.frame:","Data Frame 선택:","" "Select an environment in which to search:","찾을 환경 선택","" "Select environment","환경 선택","" "Select sheet to import:","불러올 Sheet 선택:","" "Some operations are not allowed","�쇰� �묒뾽�€ �덉슜�섏� �딆뒿�덈떎","Automatically translated" "Something went wrong...","臾몄젣媛€ 諛쒖깮�덉뒿�덈떎.","Automatically translated" "Sort count","�뺣젹 �잛닔","Automatically translated" "The URL that appear in your browser, in that case the current sheet will be read","브라우저에 나타난 URL을 붙여넣으면 현재 보고 있는 Sheet가 사용됩니다.","" "The row has been deleted","행이 삭제되었습니다.","" "The row wasn't added to the data","�됱씠 �곗씠�곗뿉 異붽��섏� �딆븯�듬땲��.","Automatically translated" "URL","URL","" "Unable to add the row, contact the platform administrator","행을 추가할 수 없습니다. 플랫폼 관리자에게 문의하세요.","" "Unable to delete the row, contact platform administrator","행을 삭제할 수 없습니다. 플랫폼 관리자에게 문의하세요.","" "Unable to modify the item, contact the platform administrator","항목을 수정할 수 없습니다. 플랫폼 관리자에게 문의하세요.","" "Unique values:","怨좎쑀�� 媛�:","Automatically translated" "Unique:","怨좎쑀��:","Automatically translated" "Update","업데이트","" "Update & select variables","蹂€�� �낅뜲�댄듃 諛� �좏깮","Automatically translated" "Update factor variable","�붿씤 蹂€�� �낅뜲�댄듃","Automatically translated" "Update levels of a factor","�붿씤 �섏� �낅뜲�댄듃","Automatically translated" "Update row","행 업데이트","" "Upload a file:","파일 업로드:","" "Use a data.frame from your environment or from the environment of a package.","사용자 환경이나 패키지로부터 data.frame을 불러와 사용하세요.","" "Valid number of columns","�좏슚�� �� ��","Automatically translated" "Valid number of rows","�좏슚�� �� ��","Automatically translated" "Validate","검증","" "Validation:","�뺤씤:","Automatically translated" "Variable to cut:","�섎씪�� 蹂€��:","Automatically translated" "View","보기","" "Warning","寃쎄퀬","Automatically translated" "Yes","예","" "You can either use:","다음 중 선택:","" "You can import %s files","%s 파일 불러오기 가능","" "You can import from flat table format supported by","에서 지원하는 플랫 테이블 형식에서 가져올 수 있습니다.","" "click to see data","�곗씠�곕� 蹂대젮硫� �대┃�섏꽭��","Automatically translated" "data has %s obs. of %s variables.","%s행 변수 %s","" "if several use a comma ',' to separate them","�щ윭 紐낆씠 �쇳몴 ','瑜� �ъ슜�섏뿬 援щ텇�섎뒗 寃쎌슦","Automatically translated" "lines, i.e.","�쇱씤, 利�","Automatically translated" "number of rows","�� ��","Automatically translated" "proportion of rows","�됱쓽 鍮꾩쑉","Automatically translated" "rows","��","Automatically translated" "then enter an expression before clicking on the button above to validate or on ","洹몃윴 �ㅼ쓬 �꾩쓽 踰꾪듉�� �대┃�섍린 �꾩뿉 �쒗쁽�앹쓣 �낅젰�섏뿬 �좏슚�깆쓣 寃€�ы븯嫄곕굹","Automatically translated" "to delete it.","��젣�섎젮硫�","Automatically translated" "Sort by count","媛쒖닔蹂꾨줈 �뺣젹","Automatically translated" "Sort by levels","�덈꺼蹂꾨줈 �뺣젹","Automatically translated" ================================================ FILE: inst/i18n/maj_csv.R ================================================ library(data.table) library(stringr) library(utils) library(polyglotr) source("inst/i18n/extract_labels.R") # Extraction of labels contained in the “/R” subdirectory extraction_labels <- c(extract_labels(folder = "R")) # Updating csv with previously extracted labels WITHOUT translation update_csv(labels = extraction_labels, lang = "fr", lang_csv = "fr", translation = FALSE) update_csv(labels = extraction_labels, lang = "es", lang_csv = "es", translation = FALSE) update_csv(labels = extraction_labels, lang = "de", lang_csv = "de", translation = FALSE) update_csv(labels = extraction_labels, lang = "sq", lang_csv = "al", translation = FALSE) update_csv(labels = extraction_labels, lang = "pl", lang_csv = "pl", translation = FALSE) update_csv(labels = extraction_labels, lang = "pt", lang_csv = "pt", translation = FALSE) update_csv(labels = extraction_labels, lang = "tr", lang_csv = "tr", translation = FALSE) update_csv(labels = extraction_labels, lang = "mk", lang_csv = "mk", translation = FALSE) # update_csv(labels = extraction_labels, lang = "ja", lang_csv = "ja", translation = FALSE, encoding = "ISO_2022,locale=ja,version=4") # review encoding update_csv(labels = extraction_labels, lang = "zh-CN", lang_csv = "cn", translation = FALSE, encoding = "GBK") #"UTF16_BigEndian" or "GB18030" update_csv(labels = extraction_labels, lang = "ko", lang_csv = "kr", translation = FALSE, encoding = "korean") # "GBK", "korean" # Updating csv with labels extracted previously WITH translation update_csv(labels = extraction_labels, lang = "fr", lang_csv = "fr") update_csv(labels = extraction_labels, lang = "es", lang_csv = "es") update_csv(labels = extraction_labels, lang = "de", lang_csv = "de") update_csv(labels = extraction_labels, lang = "sq", lang_csv = "al") update_csv(labels = extraction_labels, lang = "pl", lang_csv = "pl") update_csv(labels = extraction_labels, lang = "pt", lang_csv = "pt") update_csv(labels = extraction_labels, lang = "tr", lang_csv = "tr") update_csv(labels = extraction_labels, lang = "mk", lang_csv = "mk") # update_csv(labels = extraction_labels, lang = "ja", lang_csv = "ja", encoding = "ISO_2022,locale=ja,version=4") # review encoding update_csv(labels = extraction_labels, lang = "zh-CN", lang_csv = "cn", encoding = "GBK") #"UTF16_BigEndian" or "GB18030" update_csv(labels = extraction_labels, lang = "ko", lang_csv = "kr", encoding = "korean") # "GBK", "korean" ================================================ FILE: inst/i18n/mk.csv ================================================ "label","translation","comment" "% of the total","% од вкупниот број","Automatically translated" "% of the total, i.e.","% од вкупниот број, т.е.","Automatically translated" "A shareable link, in that case first sheet will be read","Врска којашто може да се сподели, во овој случај првата страна ќе биде прочитана","" "Add a label to data","Додадете етикета на податоците","#TODO #CHECK" "Add a row","Додадете ред","" "Apply changes","Применете ги промените","" "Browse...","Пребарувајте...","" "Cancel","Откажи","" "Choose a name for the column to be created or modified,","Изберете име за колоната што треба да се креира или измени,","Automatically translated" "Choose a number of rows :","Изберете голем број редови:","Automatically translated" "Choose a percentage :","Изберете процент:","Automatically translated" "Click on a column name to add it to the expression:","Кликнете на името на колоната за да го додадете во изразот:","Automatically translated" "Click to delete","Кликнете за да избришете","" "Click to edit","Кликнете за уредување","" "Close","Затвори","" "Close intervals on the right","Затворете ги интервалите од десната страна","Automatically translated" "Column added!","Колона е додадена!","Automatically translated" "Convert Numeric to Factor","Конвертирај нумерички во фактор","Automatically translated" "Copy & paste data","Копирајте и залепете податоци","" "Copy / Paste","Копирај / залепи","" "Count","брои","Automatically translated" "Create a new column","Направете нова колона","Automatically translated" "Create a new variable otherwise replaces the one selected","Креирајте нова променлива инаку ја заменува избраната","Automatically translated" "Create column","Креирај колона","Automatically translated" "Create factor variable","Креирај променлива на фактор","Automatically translated" "Data has %s observations and %s variables.","Податоците имаат %s опсервации и %s варијабли.","" "Data ready to be imported!","Податоци коишто се спремни за да бидат увезени","" "Data successfully imported!","Успешно увезени податоци","" "Data successfully updated!","Податоците се успешно ажурирани!","" "Data wasn't deleted","Податоците не се избришани","Automatically translated" "Data wasn't updated","Податоците не беа ажурирани","Automatically translated" "Dataset validation:","Потврда на сет на податоци:","Automatically translated" "Date format:","Формат на датум:","" "Date to use as origin to convert date/datetime:","Датум што треба да се користи за конвертирање на date/datetime променлива","" "Decimal separator:","Децимален раздвојник:","" "Delete","Избриши","" "Do you want to delete the selected row ?","Дали сакате да го избришете избраниот ред?","" "Encoding:","Енкодинг:","" "Enter URL to data:","Внесете URL на податоците:","Automatically translated" "Enter a shareable link to a GoogleSheet:","Внесете врска којшто може да се сподели во GoogleSheet:","" "Enter an expression to define new column:","Внесете израз за да дефинирате нова колона:","Automatically translated" "Environment","Околина","" "Error","Грешка","" "External file","Надворешна датотека","" "Factor variable to reorder:","Факторска променлива за прередување:","Automatically translated" "Failed","Неуспех","" "First five rows are shown below:","Првите пет реда се прикажани подолу:","" "Googlesheets","Googlesheets","" "Group calculation by:","Групна пресметка според:","Automatically translated" "Help","Помош","" "How to import data?","Како да се увезат податоците?","" "Import","Увези","" "Import Google Spreadsheet","Увезете GoogleSheet","" "Import Url","Увезете УРЛ","Automatically translated" "Import a dataset from an environment","Увезете податочен сет од околината","" "Import a file","Внесете документ","" "Import data","Внесете податоци","" "Imported data","Увезени податоци","Automatically translated" "Include lowest value","Вклучете ја најниската вредност","Automatically translated" "Information","Информации","" "Item has been modified","Ставката е изменета","" "Levels","Нивоа","Automatically translated" "List of data.frame...","Список на табели со податоци","" "Max:","Макс:","Automatically translated" "Mean:","Средно:","Automatically translated" "Method:","Метод:","Automatically translated" "Min:","Мин:","Automatically translated" "Missing values characters:","Недостасуваат знаци на вредности:","Automatically translated" "Missing:","Недостасува:","Automatically translated" "Most Common:","Најчесто:","Automatically translated" "New column name cannot be empty","Името на новата колона не може да биде празно","Automatically translated" "New column name:","Ново име на колона:","Automatically translated" "No","бр","" "No data selected!","Нема ниту еден избран податок","" "No data to display.","Нема податоци за приказ.","" "No data.frame here...","Тука нема податоци...","" "No file selected","Ниту еден документ не е избран","" "No file selected:","Ниту еден документ не е избран","" "Not a data.frame","Не е податок.рамка","Automatically translated" "Nothing pasted yet!","Ништо до сега не е залепено!","" "Number of breaks:","Број на паузи:","Automatically translated" "Number of rows:","Број на редови:","" "OK","Во ред","" "Ooops","Упс","" "Paste data here:","Залепете ги вашите податоци тука:","" "Please copy and paste some data in the dialog box above.","Ве молиме копирајте и залепете некои податоци во прозорчето за дијалог подолу","" "Please fill in the required fields","Ве молиме пополнете ги бараните полиња","" "Please paste a valid GoogleSheet link in the dialog box above.","Ве молиме залепете валидна врска до GoogleSheet во прозорчето подолу.","" "Please paste a valid link in the dialog box above.","Ве молиме залепете валидна врска во полето за дијалог погоре.","Automatically translated" "Registered","Регистриран","" "Required field","задолжително поле","" "Row has been saved","Редот е зачуван","" "Row was not deleted","Редот не беше избришан","" "Rows to skip before reading data:","Редови што треба да се прескокнат пред да се прочитаат податоците:","Automatically translated" "Sample data by :","Примерок на податоци од:","Automatically translated" "Save","Зачувај","" "Select","изберете","" "Select a data.frame:","Изберете data.frame","" "Select an environment in which to search:","Изберете околина за пребарување","" "Select environment","Изберете околина","" "Select sheet to import:","Изберете лист за да увезете","" "Some operations are not allowed","Некои операции не се дозволени","Automatically translated" "Something went wrong...","Тука нема податоци...","" "Sort count","Броење сортирање","Automatically translated" "The URL that appear in your browser, in that case the current sheet will be read","URL коешто се појавува во вашиот пребарарувач, во овој случај првата страна ќе биде прочитана","" "The row has been deleted","Редот е избришан","" "The row wasn't added to the data","Редот не беше додаден на податоците","Automatically translated" "URL","URL","Automatically translated" "Unable to add the row, contact the platform administrator","Не може да се додаде редот, контактирајте со администраторот на платформата","" "Unable to delete the row, contact platform administrator","Не може да се избрише редот, контактирајте со администраторот на платформата","" "Unable to modify the item, contact the platform administrator","Не може да се измени ставката, контактирајте со администраторот на платформата","" "Unique values:","Единствени вредности:","Automatically translated" "Unique:","Уникатно:","Automatically translated" "Update","Ажурирај","" "Update & select variables","Ажурирај и одбери променливи","" "Update factor variable","Ажурирајте ја променливата на факторот","Automatically translated" "Update levels of a factor","Ажурирајте ги нивоата на фактор","Automatically translated" "Update row","Ажурирајте го редот","" "Upload a file:","Прикачете документ","" "Use a data.frame from your environment or from the environment of a package.","Користете data.frame од твојата околина или од пакет","" "Valid number of columns","Валиден број на колони","Automatically translated" "Valid number of rows","Валиден број на редови","Automatically translated" "Validate","Провери","" "Validation:","Проверка:","" "Variable to cut:","Променлива за сечење:","Automatically translated" "View","Прегледај","" "Warning","Предупредување","Automatically translated" "Yes","Да","" "You can either use:","Можете да користите нешто од ова:","" "You can import %s files","Можете да увезувате документи %s","" "You can import from flat table format supported by","Можете да увезете од формат на рамна табела поддржан од","Automatically translated" "click to see data","Притиснете тука за да прикачите податоци","" "data has %s obs. of %s variables.","Податоците имаат % обсерваци и % променливи","" "if several use a comma ',' to separate them","ако неколку користат запирка ',' за да ги разделат","Automatically translated" "lines, i.e.","линии, т.е.","Automatically translated" "number of rows","број на редови","Automatically translated" "proportion of rows","пропорција на редови","Automatically translated" "rows","редови","Automatically translated" "then enter an expression before clicking on the button above to validate or on ","потоа внесете израз пред да кликнете на копчето погоре за да потврдите или да го вклучите","Automatically translated" "to delete it.","да го избришете.","Automatically translated" "Sort by count","Подреди по број","Automatically translated" "Sort by levels","Подреди по нивоа","Automatically translated" ================================================ FILE: inst/i18n/pl.csv ================================================ "label","translation","comment" "% of the total","% całkowitej","Automatically translated" "% of the total, i.e.","% całości, tj.","Automatically translated" "A shareable link, in that case first sheet will be read","Linku do udostępniania (zostanie odczytany pierwszy arkusz)","" "Add a label to data","Dodaj etykietę do danych","" "Add a row","Dodaj wiersz","" "Apply changes","Zastosuj zmiany","" "Browse...","Przeglądaj...","" "Cancel","Anuluj","" "Choose a name for the column to be created or modified,","Wybierz nazwę kolumny, która ma zostać utworzona lub zmodyfikowana,","Automatically translated" "Choose a number of rows :","Wybierz liczbę wierszy:","Automatically translated" "Choose a percentage :","Wybierz procent:","Automatically translated" "Click on a column name to add it to the expression:","Kliknij nazwę kolumny, aby dodać ją do wyrażenia:","Automatically translated" "Click to delete","Naciśnij by usunąć","" "Click to edit","Naciśnij by edytować","" "Close","Zamknij.","" "Close intervals on the right","Zamknij odstępy po prawej stronie","Automatically translated" "Column added!","Dodano kolumnę!","Automatically translated" "Convert Numeric to Factor","Zamień liczbę na współczynnik","Automatically translated" "Copy & paste data","Kopiuj i wklej dane","" "Copy / Paste","Kopiuj / wklej","" "Count","Liczyć","Automatically translated" "Create a new column","Utwórz nową kolumnę","Automatically translated" "Create a new variable otherwise replaces the one selected","Utwórz nową zmienną, w przeciwnym razie zastępuje wybraną","Automatically translated" "Create column","Utwórz kolumnę","Automatically translated" "Create factor variable","Utwórz zmienną czynnikową","Automatically translated" "Data has %s observations and %s variables.","dane zawierają %s obserwacji i %s zmiennych.","" "Data ready to be imported!","Dane gotowe do importu!","" "Data successfully imported!","Dane zaimportowano pomyślnie!","" "Data successfully updated!","Dane pomyślnie zaktualizowano","" "Data wasn't deleted","Dane nie zostały usunięte","Automatically translated" "Data wasn't updated","Dane nie zostały zaktualizowane","Automatically translated" "Dataset validation:","Walidacja zbioru danych:","Automatically translated" "Date format:","Format dat:","" "Date to use as origin to convert date/datetime:","Data do użycia jako początkowa do konwersji dat/dat i czasu","" "Decimal separator:","Separator dziesiętny:","" "Delete","Usuń","" "Do you want to delete the selected row ?","Czy na pewo chcesz usunąć ten wiersz?","" "Encoding:","Kodowanie:","" "Enter URL to data:","Wpisz adres URL do danych:","Automatically translated" "Enter a shareable link to a GoogleSheet:","Wprowadź link do udostępniania Arkusza Google:","" "Enter an expression to define new column:","Wprowadź wyrażenie definiujące nową kolumnę:","Automatically translated" "Environment","Środowisko","" "Error","Błąd","" "External file","Plik zewnętrzny","" "Factor variable to reorder:","Zmienna czynnikowa do zmiany kolejności:","Automatically translated" "Failed","Nie udało się","" "First five rows are shown below:","Poniżej pokazano pięć pierwszych wierszy:","" "Googlesheets","Arkusze Google","" "Group calculation by:","Obliczenia grupowe według:","Automatically translated" "Help","Pomoc","" "How to import data?","Jak zaimportować dane?","" "Import","Importuj","" "Import Google Spreadsheet","Importuj z Arkuszy Google","" "Import Url","Importuj adres URL","Automatically translated" "Import a dataset from an environment","Importuj dane ze środowiska","" "Import a file","Importuj plik","" "Import data","Importuj dane","" "Imported data","Zaimportowane dane","Automatically translated" "Include lowest value","Uwzględnij najniższą wartość","Automatically translated" "Information","Informacje","" "Item has been modified","Obiekt został zmieniony","" "Levels","Poziomy","Automatically translated" "List of data.frame...","Lista obiektów data.frame...","" "Max:","Maks:","Automatically translated" "Mean:","Mieć na myśli:","Automatically translated" "Method:","Metoda:","Automatically translated" "Min:","Min.:","Automatically translated" "Missing values characters:","Brakujące znaki wartości:","Automatically translated" "Missing:","Zaginiony:","Automatically translated" "Most Common:","Najczęściej:","Automatically translated" "New column name cannot be empty","Nazwa nowej kolumny nie może być pusta","Automatically translated" "New column name:","Nowa nazwa kolumny:","Automatically translated" "No","Nie","" "No data selected!","Nie wybrano danych!","" "No data to display.","Brak danych do wyświetlenia.","" "No data.frame here...","Brak obiektów data.frame","" "No file selected","Nie wybrano żadnego pliku","" "No file selected:","Nie wybrano pliku:","" "Not a data.frame","Nie ramka danych","Automatically translated" "Nothing pasted yet!","Jeszcze nic nie wklejono!","" "Number of breaks:","Liczba przerw:","Automatically translated" "Number of rows:","Liczba wierszy:","" "OK","OK","" "Ooops","Upsss","" "Paste data here:","Wklej dane tutaj:","" "Please copy and paste some data in the dialog box above.","Proszę skopiować i wkleić dane do okienka powyżej.","" "Please fill in the required fields","Proszę wypełnić wymagane pola","" "Please paste a valid GoogleSheet link in the dialog box above.","Proszę wprowadzić popeawny link do Arkusza Google w okienku powyżej.","" "Please paste a valid link in the dialog box above.","Wklej prawidłowy link w powyższym oknie dialogowym.","Automatically translated" "Registered","Zarejestrowano","" "Required field","Pole wymagane","" "Row has been saved","Wiersz został zapisany","" "Row was not deleted","Wiersz nie został usunięty","" "Rows to skip before reading data:","Wiersze do pominięcia przed odczytaniem danych:","Automatically translated" "Sample data by :","Przykładowe dane według:","Automatically translated" "Save","Ratować","" "Select","Wybierz","" "Select a data.frame:","Wybierz data.frame:","" "Select an environment in which to search:","Wybierz środowisko, w którym chcesz szukać:","" "Select environment","Wybierz środowisko","" "Select sheet to import:","Wybierz arkusz:","" "Some operations are not allowed","Niektóre operacje są niedozwolone","Automatically translated" "Something went wrong...","Coś poszło nie tak...","" "Sort count","Liczba sortowań","Automatically translated" "The URL that appear in your browser, in that case the current sheet will be read","Linku z przeglądarki (zostanie odczytany aktualny arkusz)","" "The row has been deleted","Wiersz został usunięty","" "The row wasn't added to the data","Wiersz nie został dodany do danych","Automatically translated" "URL","URL","" "Unable to add the row, contact the platform administrator","Brak możliwości dodania wiersza, skontaktuj się z administratorem","" "Unable to delete the row, contact platform administrator","Brak możliwości usunięcia wiersza, skontaktuj się z administratorem","" "Unable to modify the item, contact the platform administrator","Brak możliwości modyfikacji obiektu, skontaktuj się z administratorem","" "Unique values:","Unikalne wartości:","Automatically translated" "Unique:","Unikalny:","Automatically translated" "Update","Zaktualizuj","" "Update & select variables","Zaktualizuj i wybierz zmienne","" "Update factor variable","Aktualizuj zmienną współczynnika","Automatically translated" "Update levels of a factor","Aktualizuj poziomy czynnika","Automatically translated" "Update row","Zaktualizuj wiersz","" "Upload a file:","Wgraj plik:","" "Use a data.frame from your environment or from the environment of a package.","Użyj obiektu data.frame ze swojego środowiska lub ze środowiska pakietu.","" "Valid number of columns","Prawidłowa liczba kolumn","Automatically translated" "Valid number of rows","Prawidłowa liczba wierszy","Automatically translated" "Validate","Sprawdź","" "Validation:","Sprawdzenie:","" "Variable to cut:","Zmienna do cięcia:","Automatically translated" "View","Pokaż","" "Warning","Ostrzeżenie","Automatically translated" "Yes","Tak","" "You can either use:","Możesz użyć:","" "You can import %s files","Możesz zaimportować %s plików","" "You can import from flat table format supported by","Możesz importować z formatu płaskiej tabeli obsługiwanego przez","Automatically translated" "click to see data","kliknij by zobaczyć dane","" "data has %s obs. of %s variables.","dane zawierają %s obserwacji %s zmiennych.","" "if several use a comma ',' to separate them","jeśli kilka z nich użyje przecinka „,” aby je oddzielić","Automatically translated" "lines, i.e.","linie, tj.","Automatically translated" "number of rows","Liczba rzędów","Automatically translated" "proportion of rows","proporcja rzędów","Automatically translated" "rows","wydziwianie","Automatically translated" "then enter an expression before clicking on the button above to validate or on ","następnie wprowadź wyrażenie przed kliknięciem przycisku powyżej, aby zatwierdzić lub włączyć","Automatically translated" "to delete it.","aby go usunąć.","Automatically translated" "Sort by count","Sortuj według liczby","Automatically translated" "Sort by levels","Sortuj według poziomów","Automatically translated" ================================================ FILE: inst/i18n/pt.csv ================================================ "label","translation","comment" "% of the total","% do total","Automatically translated" "% of the total, i.e.","% do total, ou seja,","Automatically translated" "A shareable link, in that case first sheet will be read","Um link compartilhável, nesse caso a primeira aba será lida","" "Add a label to data","Adicionar uma etiqueta aos dados","#TODO #CHECK" "Add a row","Adicionar uma linha","" "Apply changes","Aplique as mudanças","" "Browse...","Buscar...","" "Cancel","Cancelar","" "Choose a name for the column to be created or modified,","Escolha um nome para a coluna a ser criada ou modificada,","Automatically translated" "Choose a number of rows :","Escolha um número de linhas:","Automatically translated" "Choose a percentage :","Escolha uma porcentagem:","Automatically translated" "Click on a column name to add it to the expression:","Clique no nome de uma coluna para adicioná-la à expressão:","Automatically translated" "Click to delete","Clique para deletar","" "Click to edit","Clique para editar","" "Close","Fechar","" "Close intervals on the right","Intervalos próximos à direita","Automatically translated" "Column added!","Coluna adicionada!","Automatically translated" "Convert Numeric to Factor","Converter numérico em fator","Automatically translated" "Copy & paste data","Copie & cole os dados","" "Copy / Paste","Copie / Cole","" "Count","Contar","Automatically translated" "Create a new column","Crie uma nova coluna","Automatically translated" "Create a new variable otherwise replaces the one selected","Crie uma nova variável, caso contrário substituirá a selecionada","Automatically translated" "Create column","Criar coluna","Automatically translated" "Create factor variable","Criar variável de fator","Automatically translated" "Data has %s observations and %s variables.","Os dados possuem %s observações de %s variáveis.","" "Data ready to be imported!","Dados prontos para serem importados!","" "Data successfully imported!","Dados importados com sucesso!","" "Data successfully updated!","Os dados foram modificados com sucesso!","" "Data wasn't deleted","Os dados não foram excluídos","Automatically translated" "Data wasn't updated","Os dados não foram atualizados","Automatically translated" "Dataset validation:","Validação do conjunto de dados:","Automatically translated" "Date format:","Formato dos dados:","" "Date to use as origin to convert date/datetime:","Data para usar como origem para converter date/datetime:","" "Decimal separator:","Separador decimal:","" "Delete","Excluir","" "Do you want to delete the selected row ?","Deseja excluir a linha selecionada?","" "Encoding:","Encoding:","" "Enter URL to data:","Insira o URL para os dados:","Automatically translated" "Enter a shareable link to a GoogleSheet:","Coloque um link compartilhável para o GoogleSheet:","" "Enter an expression to define new column:","Insira uma expressão para definir a nova coluna:","Automatically translated" "Environment","Ambiente local","" "Error","Erro","" "External file","Arquivo externo","" "Factor variable to reorder:","Variável de fator para reordenar:","Automatically translated" "Failed","Falha","" "First five rows are shown below:","As cinco primeiras linhas são mostradas abaixo:","" "Googlesheets","Googlesheets","" "Group calculation by:","Cálculo do grupo por:","Automatically translated" "Help","Ajuda","" "How to import data?","Como importar os dados?","" "Import","Importar","" "Import Google Spreadsheet","Importe um Spreadsheet do Google","" "Import Url","Url de importação","Automatically translated" "Import a dataset from an environment","Importe um conjunto de dados do ambiente local","" "Import a file","Importe um arquivo","" "Import data","Importe os dados","" "Imported data","Dados importados","Automatically translated" "Include lowest value","Incluir o valor mais baixo","Automatically translated" "Information","Em formação","" "Item has been modified","O item foi modificado","" "Levels","Níveis","Automatically translated" "List of data.frame...","Lista de data.frame...","" "Max:","Máx.:","Automatically translated" "Mean:","Significar:","Automatically translated" "Method:","Método:","Automatically translated" "Min:","Mínimo:","Automatically translated" "Missing values characters:","Caracteres de valores ausentes:","Automatically translated" "Missing:","Ausente:","Automatically translated" "Most Common:","Mais comum:","Automatically translated" "New column name cannot be empty","O novo nome da coluna não pode ficar vazio","Automatically translated" "New column name:","Novo nome de coluna:","Automatically translated" "No","Não","" "No data selected!","Nenhum dado foi selecionado!","" "No data to display.","Nenhum dado para mostrar.","" "No data.frame here...","Nenhum data.frame aqui...","" "No file selected","Nenhum arquivo selecionado","" "No file selected:","Nenhum arquivo selecionado:","" "Not a data.frame","Não é um data.frame","Automatically translated" "Nothing pasted yet!","Nada foi colado ainda!","" "Number of breaks:","Número de pausas:","Automatically translated" "Number of rows:","Número de linhas:","" "OK","OK","" "Ooops","Ooops","" "Paste data here:","Cole os dados aqui:","" "Please copy and paste some data in the dialog box above.","Por favor, copie e cole algum dado na caixa de diálogo acima.","" "Please fill in the required fields","Por favor, preencha os campos obrigatórios","" "Please paste a valid GoogleSheet link in the dialog box above.","Por favor, cole um link válido para o GoogleSheet na caixa de diálogo acima.","" "Please paste a valid link in the dialog box above.","Cole um link válido na caixa de diálogo acima.","Automatically translated" "Registered","Registrado","" "Required field","Campo obrigatório","" "Row has been saved","A linha foi salva","" "Row was not deleted","A linha não foi excluída","" "Rows to skip before reading data:","Linhas a serem ignoradas antes da leitura dos dados:","Automatically translated" "Sample data by :","Dados de amostra por:","Automatically translated" "Save","Salvar","" "Select","Selecionar","" "Select a data.frame:","Selecione um data.frame :","" "Select an environment in which to search:","Selecione um ambiente local para a busca:","" "Select environment","Selecione um ambiente local","" "Select sheet to import:","Selecione uma aba para importar:","" "Some operations are not allowed","Algumas operações não são permitidas","Automatically translated" "Something went wrong...","Algo deu errado...","" "Sort count","Contagem de classificação","Automatically translated" "The URL that appear in your browser, in that case the current sheet will be read","A URL que aparece no seu navegador, nesse caso a aba atual será lida","" "The row has been deleted","A linha foi excluída","" "The row wasn't added to the data","A linha não foi adicionada aos dados","Automatically translated" "URL","URL","Automatically translated" "Unable to add the row, contact the platform administrator","Não é possível adicionar a linha, entre em contato com o administrador da plataforma","" "Unable to delete the row, contact platform administrator","Não é possível excluir a linha, entre em contato com o administrador da plataforma","" "Unable to modify the item, contact the platform administrator","Não é possível modificar o item, entre em contato com o administrador da plataforma","" "Unique values:","Valores únicos:","Automatically translated" "Unique:","Exclusivo:","Automatically translated" "Update","Modifique","" "Update & select variables","Carregue & selecione variáveis","" "Update factor variable","Variável de fator de atualização","Automatically translated" "Update levels of a factor","Atualizar níveis de um fator","Automatically translated" "Update row","Atualizar linha","" "Upload a file:","Carregue um arquivo:","" "Use a data.frame from your environment or from the environment of a package.","Use um data.frame do seu ambiente local ou do ambiente de um pacote.","" "Valid number of columns","Número válido de colunas","Automatically translated" "Valid number of rows","Número válido de linhas","Automatically translated" "Validate","Validação","" "Validation:","Validação:","" "Variable to cut:","Variável para cortar:","Automatically translated" "View","Visualizar","" "Warning","Aviso","Automatically translated" "Yes","Sim","" "You can either use:","Você pode usar:","" "You can import %s files","Você pode importar %s","" "You can import from flat table format supported by","Você pode importar do formato de tabela plana suportado por","Automatically translated" "click to see data","clique para visualizar os dados","" "data has %s obs. of %s variables.","dados possuem %s observações de %s variáveis.","" "if several use a comma ',' to separate them","se vários usarem uma vírgula ',' para separá-los","Automatically translated" "lines, i.e.","linhas, ou seja,","Automatically translated" "number of rows","numero de linhas","Automatically translated" "proportion of rows","proporção de linhas","Automatically translated" "rows","linhas","Automatically translated" "then enter an expression before clicking on the button above to validate or on ","em seguida, insira uma expressão antes de clicar no botão acima para validar ou em","Automatically translated" "to delete it.","para excluí-lo.","Automatically translated" "Sort by count","Classificar por contagem","Automatically translated" "Sort by levels","Classificar por níveis","Automatically translated" ================================================ FILE: inst/i18n/tr.csv ================================================ "label","translation","comment" "% of the total","toplamın yüzdesi","Automatically translated" "% of the total, i.e.","Toplamın yüzdesi, yani","Automatically translated" "A shareable link, in that case first sheet will be read","Paylaşılabilir bir bağlantı, bu durumda ilk sayfa okunacak","" "Add a label to data","Veriyi etiketle","" "Add a row","satır ekle","" "Apply changes","Değişiklikleri uygula","" "Browse...","Gözat...","" "Cancel","İptal","" "Choose a name for the column to be created or modified,","Oluşturulacak veya değiştirilecek sütun için bir ad seçin,","Automatically translated" "Choose a number of rows :","Bir dizi satır seçin:","Automatically translated" "Choose a percentage :","Bir yüzde seçin:","Automatically translated" "Click on a column name to add it to the expression:","İfadeye eklemek için bir sütun adına tıklayın:","Automatically translated" "Click to delete","silmek için tıklayın","" "Click to edit","Düzenlemek için tıkla","" "Close","Kapat","" "Close intervals on the right","Sağdaki yakın aralıklar","Automatically translated" "Column added!","Sütun eklendi!","Automatically translated" "Convert Numeric to Factor","Sayıyı Faktöre Dönüştür","Automatically translated" "Copy & paste data","Veriyi kopyala & yapıştır","" "Copy / Paste","Kopyala / Yapıştır","" "Count","Saymak","Automatically translated" "Create a new column","Yeni bir sütun oluştur","Automatically translated" "Create a new variable otherwise replaces the one selected","Yeni bir değişken oluşturun, aksi halde seçilen değişkenin yerine geçer","Automatically translated" "Create column","Sütun oluştur","Automatically translated" "Create factor variable","Faktör değişkeni oluştur","Automatically translated" "Data has %s observations and %s variables.","Veri %s gözlem ve %s değişken içeriyor.","" "Data ready to be imported!","Veri içeri alınmak için hazır!","" "Data successfully imported!","Veri başarıyla içeri alındı!","" "Data successfully updated!","Veri başarıyla güncellendi!","" "Data wasn't deleted","Veriler silinmedi","Automatically translated" "Data wasn't updated","Veriler güncellenmedi","Automatically translated" "Dataset validation:","Veri kümesi doğrulaması:","Automatically translated" "Date format:","Tarih formatı:","" "Date to use as origin to convert date/datetime:","Tarih/tarihsaat için başlangıç olarak kullanılacak tarih:","" "Decimal separator:","Ondalık ayıracı:","" "Delete","Silmek","" "Do you want to delete the selected row ?","Seçilen satırı silmek istiyor musunuz?","" "Encoding:","Kodlama:","" "Enter URL to data:","Verilerin URL'sini girin:","Automatically translated" "Enter a shareable link to a GoogleSheet:","GoogleSheet için paylaşılabilir bir bağlantı girin:","" "Enter an expression to define new column:","Yeni sütunu tanımlamak için bir ifade girin:","Automatically translated" "Environment","Ortam","" "Error","Hata","" "External file","Dış dosya","" "Factor variable to reorder:","Yeniden sıralanacak faktör değişkeni:","Automatically translated" "Failed","Başarısız","" "First five rows are shown below:","İlk beş satır aşağıda gösterilmektedir:","" "Googlesheets","Googlesheets","" "Group calculation by:","Grup hesaplaması:","Automatically translated" "Help","Yardım","" "How to import data?","Veri alma nasıl yapılır?","" "Import","İçeri al","" "Import Google Spreadsheet","GoogleSheet verisi al","" "Import Url","URL'yi içe aktar","Automatically translated" "Import a dataset from an environment","Bir veri setini bir ortamdan al","" "Import a file","Dosya al","" "Import data","Veri al","" "Imported data","İçe aktarılan veriler","Automatically translated" "Include lowest value","En düşük değeri dahil et","Automatically translated" "Information","Bilgi","" "Item has been modified","Öğe değiştirildi","" "Levels","Seviyeler","Automatically translated" "List of data.frame...","Veri tablo listesi...","" "Max:","Maksimum:","Automatically translated" "Mean:","Anlam:","Automatically translated" "Method:","Yöntem:","Automatically translated" "Min:","Min:","Automatically translated" "Missing values characters:","Eksik değer karakterleri:","Automatically translated" "Missing:","Eksik:","Automatically translated" "Most Common:","En Yaygın:","Automatically translated" "New column name cannot be empty","Yeni sütun adı boş olamaz","Automatically translated" "New column name:","Yeni sütun adı:","Automatically translated" "No","Numara","" "No data selected!","Veri seçilmedi!","" "No data to display.","Gösterilecek veri yok.","" "No data.frame here...","Burada veri tablosu yok...","" "No file selected","Dosya seçilmedi","" "No file selected:","Dosya seçilmedi:","" "Not a data.frame","Bir data.frame değil","Automatically translated" "Nothing pasted yet!","Henüz hiçbir şey yapıştırılmadı!","" "Number of breaks:","Mola sayısı:","Automatically translated" "Number of rows:","Satır sayısı:","" "OK","Tamam","" "Ooops","Ooops","" "Paste data here:","Veriyi buraya yapıştır:","" "Please copy and paste some data in the dialog box above.","Yukarıdaki pencereye birkaç veri kopyalayıp yapıştırın.","" "Please fill in the required fields","Lütfen gerekli alanları doldurunuz","" "Please paste a valid GoogleSheet link in the dialog box above.","Yukarıdaki pencereye geçerli bir GoogleSheet bağlantısı yapıştırın.","" "Please paste a valid link in the dialog box above.","Lütfen yukarıdaki iletişim kutusuna geçerli bir bağlantı yapıştırın.","Automatically translated" "Registered","Kayıtlı","" "Required field","gerekli alan","" "Row has been saved","Satır kaydedildi","" "Row was not deleted","Satır silinmedi","" "Rows to skip before reading data:","Verileri okumadan önce atlanacak satırlar:","Automatically translated" "Sample data by :","Örnek veriler:","Automatically translated" "Save","Kaydetmek","" "Select","Seçme","" "Select a data.frame:","Veri tablosu seçin:","" "Select an environment in which to search:","Arama yapılacak ortamı seçin:","" "Select environment","Ortam seç","" "Select sheet to import:","İçeri almak için sayfa seçin:","" "Some operations are not allowed","Bazı işlemlere izin verilmiyor","Automatically translated" "Something went wrong...","Bir şeyler ters gitti...","" "Sort count","Sıralama sayısı","Automatically translated" "The URL that appear in your browser, in that case the current sheet will be read","Tarayıcınızda görünen URL, bu durumda geçerli sayfa okunacak","" "The row has been deleted","Satır silinemiyor, platform yöneticisiyle iletişime geçin","" "The row wasn't added to the data","Satır verilere eklenmedi","Automatically translated" "URL","URL'si","Automatically translated" "Unable to add the row, contact the platform administrator","Satır eklenemiyor, platform yöneticisiyle iletişime geçin","" "Unable to delete the row, contact platform administrator","","" "Unable to modify the item, contact the platform administrator","Öğe değiştirilemiyor, platform yöneticisiyle iletişime geçin","" "Unique values:","Benzersiz değerler:","Automatically translated" "Unique:","Eşsiz:","Automatically translated" "Update","Güncelle","" "Update & select variables","Güncelle & değişkenleri seç","" "Update factor variable","Faktör değişkenini güncelle","Automatically translated" "Update levels of a factor","Bir faktörün düzeylerini güncelleme","Automatically translated" "Update row","Satırı güncelle","" "Upload a file:","Dosya yükle:","" "Use a data.frame from your environment or from the environment of a package.","Kendi ortamanızdan ya da bir paket ortamanından bir veri tablosu kullanın.","" "Valid number of columns","Geçerli sütun sayısı","Automatically translated" "Valid number of rows","Geçerli satır sayısı","Automatically translated" "Validate","Doğrula","" "Validation:","Doğrulama:","" "Variable to cut:","Kesilecek değişken:","Automatically translated" "View","Görüntüle","" "Warning","Uyarı","Automatically translated" "Yes","Evet","" "You can either use:","Ya da şunu kullanın:","" "You can import %s files","%s dosya içeri alabilirsiniz","" "You can import from flat table format supported by","tarafından desteklenen düz tablo formatından içe aktarabilirsiniz.","Automatically translated" "click to see data","veriyi görmek için tıklayın","" "data has %s obs. of %s variables.","veri %s gözlem %s değişken içeriyor.","" "if several use a comma ',' to separate them","birkaç kişi onları ayırmak için ',' virgülünü kullanıyorsa","Automatically translated" "lines, i.e.","çizgiler, yani","Automatically translated" "number of rows","satır sayısı","Automatically translated" "proportion of rows","satırların oranı","Automatically translated" "rows","satırlar","Automatically translated" "then enter an expression before clicking on the button above to validate or on ","ardından doğrulamak için yukarıdaki düğmeye tıklamadan önce bir ifade girin veya","Automatically translated" "to delete it.","silmek için.","Automatically translated" "Sort by count","Sayıya göre sırala","Automatically translated" "Sort by levels","Düzeylere göre sırala","Automatically translated" ================================================ FILE: man/create-column.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/create-column.R, R/update-factor.R \name{create-column} \alias{create-column} \alias{create_column_ui} \alias{create_column_server} \alias{list_allowed_operations} \alias{modal_create_column} \alias{winbox_create_column} \alias{winbox_update_factor} \title{Create new column} \usage{ create_column_ui(id) create_column_server( id, data_r = reactive(NULL), allowed_operations = list_allowed_operations() ) list_allowed_operations() modal_create_column( id, title = i18n("Create a new column"), easyClose = TRUE, size = "l", footer = NULL ) winbox_create_column( id, title = i18n("Create a new column"), options = shinyWidgets::wbOptions(), controls = shinyWidgets::wbControls() ) winbox_update_factor( id, title = i18n("Update levels of a factor"), options = shinyWidgets::wbOptions(), controls = shinyWidgets::wbControls() ) } \arguments{ \item{id}{Module's ID.} \item{data_r}{A \code{\link[shiny:reactive]{shiny::reactive()}} function returning a \code{data.frame}.} \item{allowed_operations}{A \code{list} of allowed operations, see below for details.} \item{title}{An optional title for the dialog.} \item{easyClose}{If \code{TRUE}, the modal dialog can be dismissed by clicking outside the dialog box, or be pressing the Escape key. If \code{FALSE} (the default), the modal dialog can't be dismissed in those ways; instead it must be dismissed by clicking on a \code{modalButton()}, or from a call to \code{\link[shiny:removeModal]{removeModal()}} on the server.} \item{size}{One of \code{"s"} for small, \code{"m"} (the default) for medium, \code{"l"} for large, or \code{"xl"} for extra large. Note that \code{"xl"} only works with Bootstrap 4 and above (to opt-in to Bootstrap 4+, pass \code{\link[bslib:bs_theme]{bslib::bs_theme()}} to the \code{theme} argument of a page container like \code{\link[shiny:fluidPage]{fluidPage()}}).} \item{footer}{UI for footer. Use \code{NULL} for no footer.} \item{options}{List of options, see \code{\link[shinyWidgets:wbOptions]{wbOptions()}}.} \item{controls}{List of controls, see \code{\link[shinyWidgets:wbControls]{wbControls()}}.} } \value{ A \code{\link[shiny:reactive]{shiny::reactive()}} function returning the data. } \description{ This module allow to enter an expression to create a new column in a \code{data.frame}. } \note{ User can only use a subset of function: (, c, +, -, *, ^, \%\%, \%/\%, /, ==, >, <, !=, <=, >=, &, |, abs, sign, sqrt, ceiling, floor, trunc, cummax, cummin, cumprod, cumsum, exp, expm1, log, log10, log2, log1p, cos, cosh, sin, sinh, tan, tanh, acos, acosh, asin, asinh, atan, atanh, cospi, sinpi, tanpi, gamma, lgamma, digamma, trigamma, round, signif, max, min, range, prod, sum, any, all, pmin, pmax, mean, paste, paste0, substr, nchar, trimws, gsub, sub, grepl, ifelse, length, as.numeric, as.character, as.integer, as.Date, as.POSIXct, as.factor, factor. You can add more operations using the \code{allowed_operations} argument, for example if you want to allow to use package lubridate, you can do: \if{html}{\out{
}}\preformatted{c(list_allowed_operations(), getNamespaceExports("lubridate")) }\if{html}{\out{
}} } \examples{ library(shiny) library(datamods) library(reactable) ui <- fluidPage( theme = bslib::bs_theme(version = 5L, preset = "bootstrap"), shinyWidgets::html_dependency_winbox(), tags$h2("Create new column"), fluidRow( column( width = 4, create_column_ui("inline"), actionButton("modal", "Or click here to open a modal to create a column"), tags$br(), tags$br(), actionButton("winbox", "Or click here to open a WinBox to create a column") ), column( width = 8, reactableOutput(outputId = "table"), verbatimTextOutput("code") ) ) ) server <- function(input, output, session) { rv <- reactiveValues(data = MASS::Cars93[, c(1, 3, 4, 5, 6, 10)]) # inline mode data_inline_r <- create_column_server( id = "inline", data_r = reactive(rv$data) ) observeEvent(data_inline_r(), rv$data <- data_inline_r()) # modal window mode observeEvent(input$modal, modal_create_column("modal")) data_modal_r <- create_column_server( id = "modal", data_r = reactive(rv$data) ) observeEvent(data_modal_r(), rv$data <- data_modal_r()) # WinBox window mode observeEvent(input$winbox, winbox_create_column("winbox")) data_winbox_r <- create_column_server( id = "winbox", data_r = reactive(rv$data) ) observeEvent(data_winbox_r(), rv$data <- data_winbox_r()) # Show result output$table <- renderReactable({ data <- req(rv$data) reactable( data = data, bordered = TRUE, compact = TRUE, striped = TRUE ) }) output$code <- renderPrint({ attr(rv$data, "code") }) } if (interactive()) shinyApp(ui, server) } ================================================ FILE: man/cut-variable.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/cut-variable.R \name{cut-variable} \alias{cut-variable} \alias{cut_variable_ui} \alias{cut_variable_server} \alias{modal_cut_variable} \alias{winbox_cut_variable} \title{Module to Convert Numeric to Factor} \usage{ cut_variable_ui(id) cut_variable_server(id, data_r = reactive(NULL)) modal_cut_variable( id, title = i18n("Convert Numeric to Factor"), easyClose = TRUE, size = "l", footer = NULL ) winbox_cut_variable( id, title = i18n("Convert Numeric to Factor"), options = shinyWidgets::wbOptions(), controls = shinyWidgets::wbControls() ) } \arguments{ \item{id}{Module ID.} \item{data_r}{A \code{\link[shiny:reactive]{shiny::reactive()}} function returning a \code{data.frame}.} \item{title}{An optional title for the dialog.} \item{easyClose}{If \code{TRUE}, the modal dialog can be dismissed by clicking outside the dialog box, or be pressing the Escape key. If \code{FALSE} (the default), the modal dialog can't be dismissed in those ways; instead it must be dismissed by clicking on a \code{modalButton()}, or from a call to \code{\link[shiny:removeModal]{removeModal()}} on the server.} \item{size}{One of \code{"s"} for small, \code{"m"} (the default) for medium, \code{"l"} for large, or \code{"xl"} for extra large. Note that \code{"xl"} only works with Bootstrap 4 and above (to opt-in to Bootstrap 4+, pass \code{\link[bslib:bs_theme]{bslib::bs_theme()}} to the \code{theme} argument of a page container like \code{\link[shiny:fluidPage]{fluidPage()}}).} \item{footer}{UI for footer. Use \code{NULL} for no footer.} \item{options}{List of options, see \code{\link[shinyWidgets:wbOptions]{wbOptions()}}.} \item{controls}{List of controls, see \code{\link[shinyWidgets:wbControls]{wbControls()}}.} } \value{ A \code{\link[shiny:reactive]{shiny::reactive()}} function returning the data. } \description{ This module contain an interface to cut a numeric into several intervals. } \examples{ library(shiny) library(datamods) library(reactable) ui <- fluidPage( theme = bslib::bs_theme(version = 5L, preset = "bootstrap"), shinyWidgets::html_dependency_winbox(), tags$h2("Convert Numeric to Factor"), fluidRow( column( width = 6, cut_variable_ui("inline"), actionButton("modal", "Or click here to open a modal to cut a variable"), tags$br(), tags$br(), actionButton("winbox", "Or click here to open a WinBox to cut a variable") ), column( width = 6, reactableOutput(outputId = "table"), verbatimTextOutput("code") ) ) ) server <- function(input, output, session) { rv <- reactiveValues(data = MASS::Cars93[, c(1, 3, 4, 5, 6, 10)]) # inline mode data_inline_r <- cut_variable_server( id = "inline", data_r = reactive(rv$data) ) observeEvent(data_inline_r(), rv$data <- data_inline_r()) # modal window mode observeEvent(input$modal, modal_cut_variable("modal")) data_modal_r <- cut_variable_server( id = "modal", data_r = reactive(rv$data) ) observeEvent(data_modal_r(), rv$data <- data_modal_r()) # WinBox window mode observeEvent(input$winbox, winbox_cut_variable("winbox")) data_winbox_r <- cut_variable_server( id = "winbox", data_r = reactive(rv$data) ) observeEvent(data_winbox_r(), rv$data <- data_winbox_r()) # Show result output$table <- renderReactable({ data <- req(rv$data) reactable( data = data, bordered = TRUE, compact = TRUE, striped = TRUE ) }) output$code <- renderPrint({ attr(rv$data, "code") }) } if (interactive()) shinyApp(ui, server) } ================================================ FILE: man/demo_edit.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/data.R \docType{data} \name{demo_edit} \alias{demo_edit} \title{Customer Credit Card Information} \format{ \subsection{\code{demo_edit}}{ A data frame with 20 rows and 6 columns: \describe{ \item{name}{Customer name} \item{job}{Customer job} \item{credit_card_provider}{Credit card provider} \item{credit_card_security_code}{Credit card security code} \item{date_obtained}{Date of obtaining the credit card} \item{contactless_card}{Contactless card} } } } \source{ \url{https://CRAN.R-project.org/package=charlatan} } \usage{ demo_edit } \description{ A subset of fake customer credit card information inspired by the \code{{charlatan}} package. } \keyword{datasets} ================================================ FILE: man/edit-data.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/edit-data.R \name{edit-data} \alias{edit-data} \alias{edit_data_ui} \alias{edit_data_server} \title{Shiny module to interactively edit a \code{data.frame}} \usage{ edit_data_ui(id) edit_data_server( id, data_r = reactive(NULL), add = TRUE, update = TRUE, delete = TRUE, download_csv = TRUE, download_excel = TRUE, file_name_export = "data", var_edit = NULL, var_mandatory = NULL, var_labels = NULL, add_default_values = list(), n_column = 1, return_class = c("data.frame", "data.table", "tbl_df", "raw"), reactable_options = NULL, modal_size = c("m", "s", "l", "xl"), modal_easy_close = TRUE, callback_add = NULL, callback_update = NULL, callback_delete = NULL, only_callback = FALSE, use_notify = TRUE ) } \arguments{ \item{id}{Module ID} \item{data_r}{data_r \code{reactive} function containing a \code{data.frame} to use in the module.} \item{add}{\code{boolean}, if \code{TRUE}, allows you to add a row in the table via a button at the top right.} \item{update}{\code{boolean}, if \code{TRUE}, allows you to modify a row of the table via a button located in the table on the row you want to edit.} \item{delete}{\code{boolean}, if \code{TRUE}, allows a row to be deleted from the table via a button in the table.} \item{download_csv}{if \code{TRUE}, allows to export the table in csv format via a download button.} \item{download_excel}{if \code{TRUE}, allows to export the table in excel format via a download button.} \item{file_name_export}{\code{character} that allows you to choose the export name of the downloaded file.} \item{var_edit}{vector of \code{character} which allows to choose the names of the editable columns.} \item{var_mandatory}{vector of \code{character} which allows to choose obligatory fields to fill.} \item{var_labels}{named list, where names are colnames and values are labels to be used in edit modal.} \item{add_default_values}{Default values to use for input control when adding new data, e.g. \code{list(my_var_text = "Default text to display")}.} \item{n_column}{Number of column in the edit modal window, must be a number that divide 12 since it use Bootstrap grid system with \code{\link[shiny:column]{shiny::column()}}.} \item{return_class}{Class of returned data: \code{data.frame}, \code{data.table}, \code{tbl_df} (tibble) or \code{raw}.} \item{reactable_options}{Options passed to \code{\link[reactable:reactable]{reactable::reactable()}}.} \item{modal_size}{\code{character} which allows to choose the size of the modalDialog. One of "s" for small, "m" (the default) for medium, "l" for large, or "xl" for extra large.} \item{modal_easy_close}{\code{boolean} If TRUE, modalDialog can be dismissed by clicking outside the dialog box, or be pressing the Escape key. If FALSE (the default), modalDialog can't be dismissed in those ways; instead it must be dismissed by clicking on a modalButton(), or from a call to removeModal() on the server.} \item{callback_add, callback_update, callback_delete}{Functions to be executed just before an action (add, update or delete) is performed on the data. Functions used must be like \code{function(data, row) {...}} where : \itemize{ \item \code{data} will be the data in the table at the moment the function is called \item \code{row} will contain either a new row of data (add), an updated row (update) or the row that will be deleted (delete). } If the return value of a callback function is not truthy (see \code{\link[shiny:isTruthy]{shiny::isTruthy()}}) then the action is cancelled.} \item{only_callback}{Only use callbacks, don't alter data within the module.} \item{use_notify}{Display information or not to user through \code{\link[shinybusy:notify]{shinybusy::notify()}}.} } \value{ the edited \code{data.frame} in reactable format with the user modifications } \description{ The module generates different options to edit a \code{data.frame}: adding, deleting and modifying rows, exporting data (csv and excel), choosing editable columns, choosing mandatory columns. This module returns the edited table with the user modifications. } \examples{ library(shiny) library(datamods) library(bslib) library(reactable) ui <- fluidPage( theme = bs_theme( version = 5 ), tags$h2("Edit data", align = "center"), edit_data_ui(id = "id"), verbatimTextOutput("result") ) server <- function(input, output, session) { edited_r <- edit_data_server( id = "id", data_r = reactive(demo_edit), add = TRUE, update = TRUE, delete = TRUE, download_csv = TRUE, download_excel = TRUE, file_name_export = "datas", # var_edit = c("name", "job", "credit_card_provider", "credit_card_security_code"), var_mandatory = c("name", "job"), var_labels = list( name = "Name", credit_card_security_code = "Credit card security code", date_obtained = "Date obtained", contactless_card = "Contactless Card", credit_card_provider = "Credit card provider" ), add_default_values = list( name = "Please enter your name here", date_obtained = Sys.Date() ), n_column = 2, modal_size = "l", modal_easy_close = TRUE, reactable_options = list( defaultColDef = colDef(filterable = TRUE), selection = "single", columns = list( name = colDef(name = "Name", style = list(fontWeight = "bold")), credit_card_security_code = colDef(name = "Credit card security code"), date_obtained = colDef(name = "Date obtained", format = colFormat(date = TRUE)), contactless_card = colDef( name = "Contactless Card", cell = htmlwidgets::JS( "function(cellInfo) { return cellInfo.value ? '\u2714\ufe0f Yes' : '\u274c No'; }" ) ), credit_card_provider = colDef( name = "Credit card provider", style = htmlwidgets::JS( "function(rowInfo) { console.log(rowInfo); var value = rowInfo.values['credit_card_provider']; var color; if (value == 'Mastercard') { color = '#e06631'; } else if (value == 'VISA 16 digit') { color = '#0c13cf'; } else if (value == 'American Express') { color = '#4d8be8'; } else if (value == 'JCB 16 digit') { color = '#23c45e'; } else { color = '#777' } return {color: color, fontWeight: 'bold'} }" ) ) ), bordered = TRUE, compact = TRUE, searchable = TRUE, highlight = TRUE ) ) output$result <- renderPrint({ str(edited_r()) }) } if (interactive()) shinyApp(ui, server) } ================================================ FILE: man/filter-data.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/filter-data.R \name{filter-data} \alias{filter-data} \alias{filter_data_ui} \alias{filter_data_server} \title{Shiny module to interactively filter a \code{data.frame}} \usage{ filter_data_ui(id, show_nrow = TRUE, max_height = NULL) filter_data_server( id, data = reactive(NULL), vars = reactive(NULL), name = reactive("data"), defaults = reactive(NULL), drop_ids = getOption("datamods.filter.drop_ids", default = TRUE), widget_char = c("virtualSelect", "select", "picker"), widget_num = c("slider", "range"), widget_date = c("slider", "range"), label_na = "NA", value_na = TRUE ) } \arguments{ \item{id}{Module id. See \code{\link[shiny:moduleServer]{shiny::moduleServer()}}.} \item{show_nrow}{Show number of filtered rows and total.} \item{max_height}{Maximum height for filters panel, useful if you have many variables to filter and limited space.} \item{data}{\code{\link[shiny:reactive]{shiny::reactive()}} function returning a \code{data.frame} to filter.} \item{vars}{\code{\link[shiny:reactive]{shiny::reactive()}} function returning a \code{character} vector of variables for which to add a filter. If a named \code{list}, names are used as labels.} \item{name}{\code{\link[shiny:reactive]{shiny::reactive()}} function returning a \code{character} string representing \code{data} name, only used for code generated.} \item{defaults}{\code{\link[shiny:reactive]{shiny::reactive()}} function returning a named \code{list} of variable:value pairs which will be used to set the filters.} \item{drop_ids}{Drop columns containing more than 90\% of unique values, or than 50 distinct values. Use \code{FALSE} to disable or use \code{list(p = 0.9, n = 50)} to customize threshold values.} \item{widget_char}{Widget to use for \code{character} variables: \code{\link[shinyWidgets:pickerInput]{shinyWidgets::pickerInput()}} or \code{\link[shiny:selectInput]{shiny::selectInput()}} (default).} \item{widget_num}{Widget to use for \code{numeric} variables: \code{\link[shinyWidgets:numericRangeInput]{shinyWidgets::numericRangeInput()}} or \code{\link[shiny:sliderInput]{shiny::sliderInput()}} (default).} \item{widget_date}{Widget to use for \code{date/time} variables: \code{\link[shiny:dateRangeInput]{shiny::dateRangeInput()}} or \code{\link[shiny:sliderInput]{shiny::sliderInput()}} (default).} \item{label_na}{Label for missing value widget.} \item{value_na}{Default value for all NA's filters.} } \value{ \itemize{ \item UI: HTML tags that can be included in shiny's UI \item Server: a \code{list} with four slots: \itemize{ \item \strong{filtered}: a \code{reactive} function returning the data filtered. \item \strong{code}: a \code{reactive} function returning the dplyr pipeline to filter data. \item \strong{expr}: a \code{reactive} function returning an expression to filter data. \item \strong{values}: a \code{reactive} function returning a named list of variables and filter values. } } } \description{ Module generate inputs to filter \code{data.frame} according column's type. Code to reproduce the filter is returned as an expression with filtered data. } \examples{ library(shiny) library(shinyWidgets) library(datamods) library(MASS) # Add some NAs to mpg mtcars_na <- mtcars mtcars_na[] <- lapply( X = mtcars_na, FUN = function(x) { x[sample.int(n = length(x), size = sample(5:10, 1))] <- NA x } ) datetime <- data.frame( date = seq(Sys.Date(), by = "day", length.out = 300), datetime = seq(Sys.time(), by = "hour", length.out = 300), num = sample.int(1e5, 300) ) one_column_numeric <- data.frame( var1 = rnorm(100) ) ui <- fluidPage( tags$h2("Filter data.frame"), actionButton("saveFilterButton","Save Filter Values"), actionButton("loadFilterButton","Load Filter Values"), radioButtons( inputId = "dataset", label = "Data:", choices = c( "iris", "mtcars", "mtcars_na", "Cars93", "datetime", "one_column_numeric" ), inline = TRUE ), fluidRow( column( width = 3, filter_data_ui("filtering", max_height = "500px") ), column( width = 9, progressBar( id = "pbar", value = 100, total = 100, display_pct = TRUE ), reactable::reactableOutput(outputId = "table"), tags$b("Code dplyr:"), verbatimTextOutput(outputId = "code_dplyr"), tags$b("Expression:"), verbatimTextOutput(outputId = "code"), tags$b("Filtered data:"), verbatimTextOutput(outputId = "res_str") ) ) ) server <- function(input, output, session) { savedFilterValues <- reactiveVal() data <- reactive({ get(input$dataset) }) vars <- reactive({ if (identical(input$dataset, "mtcars")) { setNames(as.list(names(mtcars)[1:5]), c( "Miles/(US) gallon", "Number of cylinders", "Displacement (cu.in.)", "Gross horsepower", "Rear axle ratio" )) } else { NULL } }) observeEvent(input$saveFilterButton,{ savedFilterValues <<- res_filter$values() },ignoreInit = T) defaults <- reactive({ input$loadFilterButton savedFilterValues }) res_filter <- filter_data_server( id = "filtering", data = data, name = reactive(input$dataset), vars = vars, defaults = defaults, widget_num = "slider", widget_date = "slider", label_na = "Missing" ) observeEvent(res_filter$filtered(), { updateProgressBar( session = session, id = "pbar", value = nrow(res_filter$filtered()), total = nrow(data()) ) }) output$table <- reactable::renderReactable({ reactable::reactable(res_filter$filtered()) }) output$code_dplyr <- renderPrint({ res_filter$code() }) output$code <- renderPrint({ res_filter$expr() }) output$res_str <- renderPrint({ str(res_filter$filtered()) }) } if (interactive()) shinyApp(ui, server) } ================================================ FILE: man/get_data_packages.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/import-globalenv.R \name{get_data_packages} \alias{get_data_packages} \title{Get packages containing datasets} \usage{ get_data_packages() } \value{ a character vector of packages names } \description{ Get packages containing datasets } \examples{ if (interactive()) { get_data_packages() } } ================================================ FILE: man/i18n.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/i18n.R \name{i18n} \alias{i18n} \alias{i18n_translations} \alias{set_i18n} \title{Internationalization} \usage{ i18n(x, translations = i18n_translations()) i18n_translations(package = packageName(parent.frame(2))) set_i18n(value, packages = c("datamods", "esquisse")) } \arguments{ \item{x}{Label to translate.} \item{translations}{Either a \code{list} or a \code{data.frame} with translations.} \item{package}{Name of the package where the function is called, use \code{NULL} outside a package. It will retrieve option \code{"i18n."} (or \code{"i18n"} if no package) to returns appropriate labels.} \item{value}{Value to set for translation. Can be: \itemize{ \item single \code{character} to use a supported language (\code{"fr"}, \code{"mk"}, \code{"al"}, \code{"pt"} for esquisse and datamods packages). \item a \code{list} with labels as names and translations as values. \item a \code{data.frame} with 2 column: \code{label} & \code{translation}. \item path to a CSV file with same structure as for \code{data.frame} above. }} \item{packages}{Name of packages for which to set i18n, default to esquisse and datamods} } \value{ \code{i18n()} returns a \code{character}, \code{i18n_translations()} returns a \code{list} or a \code{data.frame}. } \description{ Simple mechanism to translate labels in a Shiny application. } \examples{ library(datamods) # Use with an objet my.translations <- list( "Hello" = "Bonjour" ) i18n("Hello", my.translations) # Use with options() options("i18n" = list( "Hello" = "Bonjour" )) i18n("Hello") # With a package options("datamods.i18n" = "fr") i18n("Browse...", translations = i18n_translations("datamods")) # If you call i18n() from within a function of your package # you don't need second argument, e.g.: # i18n("Browse...") } ================================================ FILE: man/import-copypaste.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/import-copypaste.R \name{import-copypaste} \alias{import-copypaste} \alias{import_copypaste_ui} \alias{import_copypaste_server} \title{Import data with copy & paste} \usage{ import_copypaste_ui(id, title = TRUE, name_field = TRUE) import_copypaste_server( id, btn_show_data = TRUE, show_data_in = c("popup", "modal"), trigger_return = c("button", "change"), return_class = c("data.frame", "data.table", "tbl_df", "raw"), reset = reactive(NULL), fread_args = list() ) } \arguments{ \item{id}{Module's ID.} \item{title}{Module's title, if \code{TRUE} use the default title, use \code{NULL} for no title or a \code{shiny.tag} for a custom one.} \item{name_field}{Show or not a field to add a name to data (that is returned server-side).} \item{btn_show_data}{Display or not a button to display data in a modal window if import is successful.} \item{show_data_in}{Where to display data: in a \code{"popup"} or in a \code{"modal"} window.} \item{trigger_return}{When to update selected data: \code{"button"} (when user click on button) or \code{"change"} (each time user select a dataset in the list).} \item{return_class}{Class of returned data: \code{data.frame}, \code{data.table}, \code{tbl_df} (tibble) or \code{raw}.} \item{reset}{A \code{reactive} function that when triggered resets the data.} \item{fread_args}{\code{list} of additional arguments to pass to \code{\link[data.table:fread]{data.table::fread()}} when reading data.} } \value{ \itemize{ \item UI: HTML tags that can be included in shiny's UI \item Server: a \code{list} with three slots: \itemize{ \item \strong{status}: a \code{reactive} function returning the status: \code{NULL}, \code{error} or \code{success}. \item \strong{name}: a \code{reactive} function returning the name of the imported data as \code{character}. \item \strong{data}: a \code{reactive} function returning the imported \code{data.frame}. } } } \description{ Let the user copy data from Excel or text file then paste it into a text area to import it. } \examples{ library(shiny) library(datamods) ui <- fluidPage( tags$h3("Import data with copy & paste"), fluidRow( column( width = 4, import_copypaste_ui("myid") ), column( width = 8, tags$b("Import status:"), verbatimTextOutput(outputId = "status"), tags$b("Name:"), verbatimTextOutput(outputId = "name"), tags$b("Data:"), verbatimTextOutput(outputId = "data") ) ) ) server <- function(input, output, session) { imported <- import_copypaste_server("myid") output$status <- renderPrint({ imported$status() }) output$name <- renderPrint({ imported$name() }) output$data <- renderPrint({ imported$data() }) } if (interactive()) shinyApp(ui, server) } ================================================ FILE: man/import-file.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/import-file.R \name{import-file} \alias{import-file} \alias{import_file_ui} \alias{import_file_server} \title{Import data from a file} \usage{ import_file_ui( id, title = TRUE, preview_data = TRUE, file_extensions = c(".csv", ".txt", ".xls", ".xlsx", ".rds", ".fst", ".sas7bdat", ".sav"), layout_params = c("dropdown", "inline") ) import_file_server( id, btn_show_data = TRUE, show_data_in = "winbox", trigger_return = c("button", "change"), return_class = c("data.frame", "data.table", "tbl_df", "raw"), reset = reactive(NULL), read_fns = list() ) } \arguments{ \item{id}{Module's ID.} \item{title}{Module's title, if \code{TRUE} use the default title, use \code{NULL} for no title or a \code{shiny.tag} for a custom one.} \item{preview_data}{Show or not a preview of the data under the file input.} \item{file_extensions}{File extensions accepted by \code{\link[shiny:fileInput]{shiny::fileInput()}}, can also be MIME type.} \item{layout_params}{How to display import parameters : in a dropdown button or inline below file input.} \item{btn_show_data}{Display or not a button to display data in a modal window if import is successful.} \item{show_data_in}{Where to display data: in a \code{"popup"} or in a \code{"modal"} window.} \item{trigger_return}{When to update selected data: \code{"button"} (when user click on button) or \code{"change"} (each time user select a dataset in the list).} \item{return_class}{Class of returned data: \code{data.frame}, \code{data.table}, \code{tbl_df} (tibble) or \code{raw}.} \item{reset}{A \code{reactive} function that when triggered resets the data.} \item{read_fns}{Named list with custom function(s) to read data: \itemize{ \item the name must be the extension of the files to which the function will be applied \item the value must be a function that can have 5 arguments (you can ignore some of them, but you have to use the same names), passed by user through the interface: \itemize{ \item \code{file}: path to the file \item \code{sheet}: for Excel files, sheet to read \item \code{skip}: number of row to skip \item \code{dec}: decimal separator \item \code{encoding}: file encoding \item \code{na.strings}: character(s) to interpret as missing values. } }} } \value{ \itemize{ \item UI: HTML tags that can be included in shiny's UI \item Server: a \code{list} with three slots: \itemize{ \item \strong{status}: a \code{reactive} function returning the status: \code{NULL}, \code{error} or \code{success}. \item \strong{name}: a \code{reactive} function returning the name of the imported data as \code{character}. \item \strong{data}: a \code{reactive} function returning the imported \code{data.frame}. } } } \description{ Let user upload a file and import data } \examples{ library(shiny) library(datamods) ui <- fluidPage( # theme = bslib::bs_theme(version = 5L), # theme = bslib::bs_theme(version = 5L, preset = "bootstrap"), tags$h3("Import data from a file"), fluidRow( column( width = 4, import_file_ui( id = "myid", file_extensions = c(".csv", ".txt", ".xls", ".xlsx", ".json"), layout_params = "inline" # or "dropdown" ) ), column( width = 8, tags$b("Import status:"), verbatimTextOutput(outputId = "status"), tags$b("Name:"), verbatimTextOutput(outputId = "name"), tags$b("Code:"), verbatimTextOutput(outputId = "code"), tags$b("Data:"), verbatimTextOutput(outputId = "data") ) ) ) server <- function(input, output, session) { imported <- import_file_server( id = "myid", # Custom functions to read data read_fns = list( xls = function(file, sheet, skip, encoding) { readxl::read_xls(path = file, sheet = sheet, skip = skip) }, json = function(file) { jsonlite::read_json(file, simplifyVector = TRUE) } ), show_data_in = "modal" ) output$status <- renderPrint({ imported$status() }) output$name <- renderPrint({ imported$name() }) output$code <- renderPrint({ imported$code() }) output$data <- renderPrint({ imported$data() }) } if (interactive()) shinyApp(ui, server) } ================================================ FILE: man/import-globalenv.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/import-globalenv.R \name{import-globalenv} \alias{import-globalenv} \alias{import_globalenv_ui} \alias{import_globalenv_server} \title{Import data from an Environment} \usage{ import_globalenv_ui( id, globalenv = TRUE, packages = get_data_packages(), title = TRUE ) import_globalenv_server( id, btn_show_data = TRUE, show_data_in = c("popup", "modal"), trigger_return = c("button", "change"), return_class = c("data.frame", "data.table", "tbl_df", "raw"), reset = reactive(NULL) ) } \arguments{ \item{id}{Module's ID.} \item{globalenv}{Search for data in Global environment.} \item{packages}{Name of packages in which to search data.} \item{title}{Module's title, if \code{TRUE} use the default title, use \code{NULL} for no title or a \code{shiny.tag} for a custom one.} \item{btn_show_data}{Display or not a button to display data in a modal window if import is successful.} \item{show_data_in}{Where to display data: in a \code{"popup"} or in a \code{"modal"} window.} \item{trigger_return}{When to update selected data: \code{"button"} (when user click on button) or \code{"change"} (each time user select a dataset in the list).} \item{return_class}{Class of returned data: \code{data.frame}, \code{data.table}, \code{tbl_df} (tibble) or \code{raw}.} \item{reset}{A \code{reactive} function that when triggered resets the data.} } \value{ \itemize{ \item UI: HTML tags that can be included in shiny's UI \item Server: a \code{list} with three slots: \itemize{ \item \strong{status}: a \code{reactive} function returning the status: \code{NULL}, \code{error} or \code{success}. \item \strong{name}: a \code{reactive} function returning the name of the imported data as \code{character}. \item \strong{data}: a \code{reactive} function returning the imported \code{data.frame}. } } } \description{ Let the user select a dataset from its own environment or from a package's environment. } \examples{ if (interactive()) { library(shiny) library(datamods) # Create some data.frames my_df <- data.frame( variable1 = sample(letters, 20, TRUE), variable2 = sample(1:100, 20, TRUE) ) results_analysis <- data.frame( id = sample(letters, 20, TRUE), measure = sample(1:100, 20, TRUE), response = sample(1:100, 20, TRUE) ) # Application ui <- fluidPage( fluidRow( column( width = 4, import_globalenv_ui("myid") ), column( width = 8, tags$b("Import status:"), verbatimTextOutput(outputId = "status"), tags$b("Name:"), verbatimTextOutput(outputId = "name"), tags$b("Data:"), verbatimTextOutput(outputId = "data") ) ) ) server <- function(input, output, session) { imported <- import_globalenv_server("myid") output$status <- renderPrint({ imported$status() }) output$name <- renderPrint({ imported$name() }) output$data <- renderPrint({ imported$data() }) } shinyApp(ui, server) } } ================================================ FILE: man/import-googlesheets.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/import-googlesheets.R \name{import-googlesheets} \alias{import-googlesheets} \alias{import_googlesheets_ui} \alias{import_googlesheets_server} \title{Import data from Googlesheet} \usage{ import_googlesheets_ui(id, title = TRUE) import_googlesheets_server( id, btn_show_data = TRUE, show_data_in = c("popup", "modal"), trigger_return = c("button", "change"), return_class = c("data.frame", "data.table", "tbl_df", "raw"), reset = reactive(NULL) ) } \arguments{ \item{id}{Module's ID.} \item{title}{Module's title, if \code{TRUE} use the default title, use \code{NULL} for no title or a \code{shiny.tag} for a custom one.} \item{btn_show_data}{Display or not a button to display data in a modal window if import is successful.} \item{show_data_in}{Where to display data: in a \code{"popup"} or in a \code{"modal"} window.} \item{trigger_return}{When to update selected data: \code{"button"} (when user click on button) or \code{"change"} (each time user select a dataset in the list).} \item{return_class}{Class of returned data: \code{data.frame}, \code{data.table}, \code{tbl_df} (tibble) or \code{raw}.} \item{reset}{A \code{reactive} function that when triggered resets the data.} } \value{ \itemize{ \item UI: HTML tags that can be included in shiny's UI \item Server: a \code{list} with three slots: \itemize{ \item \strong{status}: a \code{reactive} function returning the status: \code{NULL}, \code{error} or \code{success}. \item \strong{name}: a \code{reactive} function returning the name of the imported data as \code{character}. \item \strong{data}: a \code{reactive} function returning the imported \code{data.frame}. } } } \description{ Let user paste link to a Google sheet then import the data. } \examples{ library(shiny) library(datamods) ui <- fluidPage( tags$h3("Import data from Googlesheets"), fluidRow( column( width = 4, import_googlesheets_ui("myid") ), column( width = 8, tags$b("Import status:"), verbatimTextOutput(outputId = "status"), tags$b("Name:"), verbatimTextOutput(outputId = "name"), tags$b("Data:"), verbatimTextOutput(outputId = "data") ) ) ) server <- function(input, output, session) { imported <- import_googlesheets_server("myid") output$status <- renderPrint({ imported$status() }) output$name <- renderPrint({ imported$name() }) output$data <- renderPrint({ imported$data() }) } if (interactive()) shinyApp(ui, server) } ================================================ FILE: man/import-modal.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/import-modal.R \name{import-modal} \alias{import-modal} \alias{import_ui} \alias{import_server} \alias{import_modal} \title{Import from all sources} \usage{ import_ui( id, from = c("env", "file", "copypaste", "googlesheets", "url"), file_extensions = c(".csv", ".txt", ".xls", ".xlsx", ".rds", ".fst", ".sas7bdat", ".sav") ) import_server( id, validation_opts = NULL, allowed_status = c("OK", "Failed", "Error"), return_class = c("data.frame", "data.table", "tbl_df", "raw"), read_fns = list() ) import_modal( id, from, title = i18n("Import data"), size = "l", file_extensions = c(".csv", ".txt", ".xls", ".xlsx", ".rds", ".fst", ".sas7bdat", ".sav") ) } \arguments{ \item{id}{Module's id} \item{from}{The import_ui & server to use, i.e. the method. There are 5 options to choose from. ("env", "file", "copypaste", "googlesheets", "url")} \item{file_extensions}{File extensions accepted by \code{\link[shiny:fileInput]{shiny::fileInput()}}, can also be MIME type.} \item{validation_opts}{\code{list} of arguments passed to [validation_server().} \item{allowed_status}{Vector of statuses allowed to confirm dataset imported, if you want that all validation rules are successful before importing data use \code{allowed_status = "OK"}.} \item{return_class}{Class of returned data: \code{data.frame}, \code{data.table}, \code{tbl_df} (tibble) or \code{raw}.} \item{read_fns}{Named list with custom function(s) to read data: \itemize{ \item the name must be the extension of the files to which the function will be applied \item the value must be a function that can have 5 arguments (you can ignore some of them, but you have to use the same names), passed by user through the interface: \itemize{ \item \code{file}: path to the file \item \code{sheet}: for Excel files, sheet to read \item \code{skip}: number of row to skip \item \code{dec}: decimal separator \item \code{encoding}: file encoding \item \code{na.strings}: character(s) to interpret as missing values. } }} \item{title}{Modal window title.} \item{size}{Modal window size, default to \code{"l"} (large).} } \value{ \itemize{ \item UI: HTML tags that can be included in shiny's UI \item Server: a \code{list} with three slots: \itemize{ \item \strong{status}: a \code{reactive} function returning the status: \code{NULL}, \code{error} or \code{success}. \item \strong{name}: a \code{reactive} function returning the name of the imported data as \code{character}. \item \strong{data}: a \code{reactive} function returning the imported \code{data.frame}. } } } \description{ Wrap all import modules into one, can be displayed inline or in a modal window.. } \examples{ library(shiny) library(datamods) ui <- fluidPage( # Try with different Bootstrap version theme = bslib::bs_theme(version = 5, preset = "bootstrap"), fluidRow( column( width = 4, checkboxGroupInput( inputId = "from", label = "From", choices = c("env", "file", "copypaste", "googlesheets", "url"), selected = c("file", "copypaste") ), actionButton("launch_modal", "Launch modal window") ), column( width = 8, tags$b("Imported data:"), verbatimTextOutput(outputId = "name"), verbatimTextOutput(outputId = "data"), verbatimTextOutput(outputId = "str_data") ) ) ) server <- function(input, output, session) { observeEvent(input$launch_modal, { req(input$from) import_modal( id = "myid", from = input$from, title = "Import data to be used in application" ) }) imported <- import_server("myid", return_class = "tbl_df") output$name <- renderPrint({ req(imported$name()) imported$name() }) output$data <- renderPrint({ req(imported$data()) imported$data() }) output$str_data <- renderPrint({ req(imported$data()) str(imported$data()) }) } if (interactive()) shinyApp(ui, server) } ================================================ FILE: man/import-url.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/import-url.R \name{import-url} \alias{import-url} \alias{import_url_ui} \alias{import_url_server} \title{Import data from a URL} \usage{ import_url_ui(id, title = TRUE) import_url_server( id, btn_show_data = TRUE, show_data_in = c("popup", "modal"), trigger_return = c("button", "change"), return_class = c("data.frame", "data.table", "tbl_df", "raw"), reset = reactive(NULL) ) } \arguments{ \item{id}{Module's ID.} \item{title}{Module's title, if \code{TRUE} use the default title, use \code{NULL} for no title or a \code{shiny.tag} for a custom one.} \item{btn_show_data}{Display or not a button to display data in a modal window if import is successful.} \item{show_data_in}{Where to display data: in a \code{"popup"} or in a \code{"modal"} window.} \item{trigger_return}{When to update selected data: \code{"button"} (when user click on button) or \code{"change"} (each time user select a dataset in the list).} \item{return_class}{Class of returned data: \code{data.frame}, \code{data.table}, \code{tbl_df} (tibble) or \code{raw}.} \item{reset}{A \code{reactive} function that when triggered resets the data.} } \value{ \itemize{ \item UI: HTML tags that can be included in shiny's UI \item Server: a \code{list} with three slots: \itemize{ \item \strong{status}: a \code{reactive} function returning the status: \code{NULL}, \code{error} or \code{success}. \item \strong{name}: a \code{reactive} function returning the name of the imported data as \code{character}. \item \strong{data}: a \code{reactive} function returning the imported \code{data.frame}. } } } \description{ Let user paste link to a JSON then import the data. } \examples{ library(shiny) library(datamods) ui <- fluidPage( tags$h3("Import data from URL"), fluidRow( column( width = 4, import_url_ui("myid") ), column( width = 8, tags$b("Import status:"), verbatimTextOutput(outputId = "status"), tags$b("Name:"), verbatimTextOutput(outputId = "name"), tags$b("Data:"), verbatimTextOutput(outputId = "data") ) ) ) server <- function(input, output, session) { imported <- import_url_server( "myid", btn_show_data = FALSE, return_class = "raw" ) output$status <- renderPrint({ imported$status() }) output$name <- renderPrint({ imported$name() }) output$data <- renderPrint({ imported$data() }) } if (interactive()) shinyApp(ui, server) } ================================================ FILE: man/list_pkg_data.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/import-globalenv.R \name{list_pkg_data} \alias{list_pkg_data} \title{List dataset contained in a package} \usage{ list_pkg_data(pkg) } \arguments{ \item{pkg}{Name of the package, must be installed.} } \value{ a \code{character} vector or \code{NULL}. } \description{ List dataset contained in a package } \examples{ list_pkg_data("ggplot2") } ================================================ FILE: man/module-sample.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/sample-data.R \name{module-sample} \alias{module-sample} \alias{sample_ui} \alias{sample_server} \title{Shiny module to interactively sample a \code{data.frame}} \usage{ sample_ui(id) sample_server(id, data_r = reactive(NULL)) } \arguments{ \item{id}{Module id. See \code{\link[shiny:moduleServer]{shiny::moduleServer()}}.} \item{data_r}{\code{reactive} containing a \code{data.frame} to use in the module.} } \value{ \itemize{ \item UI: HTML tags that can be included in shiny's UI \item Server: a \code{reactive} fgunction with the sampled data. } } \description{ Allow to take a sample of \code{data.frame} for a given number or proportion of rows to keep. } \examples{ library(shiny) library(datamods) library(reactable) ui <- fluidPage( tags$h2("Sampling"), fluidRow( column( width = 3, sample_ui("myID") ), column( width = 9, reactableOutput("table") ) ) ) server <- function(input, output, session) { result_sample <- sample_server("myID", reactive(iris)) output$table <- renderReactable({ table_sample <- reactable( data = result_sample(), defaultColDef = colDef( align = "center" ), borderless = TRUE, highlight = TRUE, striped = TRUE ) return(table_sample) }) } if (interactive()) shinyApp(ui, server) } ================================================ FILE: man/select-group.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/select-group.R \name{select-group} \alias{select-group} \alias{select_group_ui} \alias{select_group_server} \title{Select Group Input Module} \usage{ select_group_ui( id, params, label = NULL, btn_reset_label = "Reset filters", inline = TRUE, vs_args = list() ) select_group_server(id, data_r, vars_r, selected_r = reactive(list())) } \arguments{ \item{id}{Module's id.} \item{params}{A list of parameters passed to each \code{\link[shinyWidgets:virtualSelectInput]{shinyWidgets::virtualSelectInput()}}, you can use : \itemize{ \item \code{inputId}: mandatory, must correspond to variable name. \item \code{label}: Display label for the control. \item \code{placeholder}: Text to show when no options selected. }} \item{label}{Character, global label on top of all labels.} \item{btn_reset_label}{Character, reset button label. If \code{NULL} no button is added.} \item{inline}{If \code{TRUE} (the default), select menus are horizontally positioned, otherwise vertically.} \item{vs_args}{Arguments passed to all \code{\link[shinyWidgets:virtualSelectInput]{shinyWidgets::virtualSelectInput()}} created.} \item{data_r}{Either a \code{\link[=data.frame]{data.frame()}} or a \code{\link[shiny:reactive]{shiny::reactive()}} function returning a \code{data.frame} (do not use parentheses).} \item{vars_r}{character, columns to use to create filters, must correspond to variables listed in \code{params}. Can be a \code{\link[shiny:reactive]{shiny::reactive()}} function, but values must be included in the initial ones (in \code{params}).} \item{selected_r}{\code{\link[shiny:reactive]{shiny::reactive()}} function returning a named list with selected values to set.} } \value{ A \code{\link[shiny:reactive]{shiny::reactive()}} function containing data filtered with an attribute \code{inputs} containing a named list of selected inputs. } \description{ Group of mutually dependent select menus for filtering \code{data.frame}'s columns (like in Excel). } \examples{ # Default ----------------------------------------------------------------- library(shiny) library(datamods) library(shinyWidgets) ui <- fluidPage( # theme = bslib::bs_theme(version = 5L), fluidRow( column( width = 10, offset = 1, tags$h3("Filter data with select group module"), shinyWidgets::panel( select_group_ui( id = "my-filters", params = list( list(inputId = "Manufacturer", label = "Manufacturer:"), list(inputId = "Type", label = "Type:"), list(inputId = "AirBags", label = "AirBags:"), list(inputId = "DriveTrain", label = "DriveTrain:") ), vs_args = list(disableSelectAll = FALSE) ), status = "primary" ), reactable::reactableOutput(outputId = "table"), tags$b("Inputs values:"), verbatimTextOutput("inputs") ) ) ) server <- function(input, output, session) { res_mod <- select_group_server( id = "my-filters", data = reactive(MASS::Cars93), vars = reactive(c("Manufacturer", "Type", "AirBags", "DriveTrain")) ) output$table <- reactable::renderReactable({ reactable::reactable(res_mod()) }) output$inputs <- renderPrint({ attr(res_mod(), "inputs") }) } if (interactive()) shinyApp(ui, server) # Selected value -------------------------------------------------------------------- library(shiny) library(datamods) ui <- fluidPage( select_group_ui( id = "my-filters", params = list( list(inputId = "Manufacturer", label = "Manufacturer:"), list(inputId = "Type", label = "Type:") ), vs_args = list( disableSelectAll = FALSE ) ), actionButton("set_sel", "Set Manufacturer=Acura"), verbatimTextOutput("res") ) server <- function(input, output, session) { # We use a reactiveValue so that it can be updated rv <- reactiveValues(selected = list(Manufacturer = "Audi")) # for init res_r <- select_group_server( id = "my-filters", data = reactive(MASS::Cars93), vars = reactive(c("Manufacturer", "Type")), selected_r = reactive(rv$selected) ) output$res <- renderPrint({ res_r() }) observeEvent(input$set_sel, { rv$selected <- list(Manufacturer = "Acura") }) } if (interactive()) shinyApp(ui, server) } ================================================ FILE: man/show_data.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/show_data.R \name{show_data} \alias{show_data} \title{Display a table in a window} \usage{ show_data( data, title = NULL, options = NULL, show_classes = TRUE, type = c("popup", "modal", "winbox"), width = "65\%", ..., session = shiny::getDefaultReactiveDomain() ) } \arguments{ \item{data}{a data object (either a \code{matrix} or a \code{data.frame}).} \item{title}{Title to be displayed in window.} \item{options}{Arguments passed to \code{\link[toastui:datagrid]{toastui::datagrid()}}.} \item{show_classes}{Show variables classes under variables names in table header.} \item{type}{Display table in a pop-up with \code{\link[shinyWidgets:sweetalert]{shinyWidgets::show_alert()}}, in modal window with \code{\link[shiny:showModal]{shiny::showModal()}} or in a WinBox window with \code{\link[shinyWidgets:WinBox]{shinyWidgets::WinBox()}}.} \item{width}{Width of the window, only used if \code{type = "popup"} or \code{type = "winbox"}.} \item{...}{Additional options, such as \code{wbOptions = wbOptions()} or \code{wbControls = wbControls()}.} \item{session}{The \code{session} object passed to function given to \code{shinyServer}.} } \value{ No value. } \description{ Display a table in a window } \note{ If you use \code{type = "winbox"}, you'll need to use \code{shinyWidgets::html_dependency_winbox()} somewhere in your UI. } \examples{ library(shiny) library(datamods) ui <- fluidPage( theme = bslib::bs_theme(version = 5L), shinyWidgets::html_dependency_winbox(), actionButton( inputId = "show1", label = "Show data in popup", icon = icon("eye") ), actionButton( inputId = "show2", label = "Show data in modal", icon = icon("eye") ), actionButton( inputId = "show3", label = "Show data without classes", icon = icon("eye") ), actionButton( inputId = "show4", label = "Show data in Winbox", icon = icon("eye") ) ) server <- function(input, output, session) { observeEvent(input$show1, { show_data(MASS::Cars93, title = "MASS::Cars93 dataset", type = "popup") }) observeEvent(input$show2, { show_data(MASS::Cars93, title = "MASS::Cars93 dataset", type = "modal") }) observeEvent(input$show3, { show_data( data = MASS::Cars93, title = "MASS::Cars93 dataset", show_classes = FALSE, options = list(pagination = 10), type = "modal" ) }) observeEvent(input$show4, { show_data( MASS::Cars93, title = "MASS::Cars93 dataset", type = "winbox", wbOptions = shinyWidgets::wbOptions(background = "forestgreen") ) }) } if (interactive()) shinyApp(ui, server) } ================================================ FILE: man/update-factor.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/update-factor.R \name{update-factor} \alias{update-factor} \alias{update_factor_ui} \alias{update_factor_server} \alias{modal_update_factor} \title{Module to Reorder the Levels of a Factor Variable} \usage{ update_factor_ui(id) update_factor_server(id, data_r = reactive(NULL)) modal_update_factor( id, title = i18n("Update levels of a factor"), easyClose = TRUE, size = "l", footer = NULL ) } \arguments{ \item{id}{Module ID.} \item{data_r}{A \code{\link[shiny:reactive]{shiny::reactive()}} function returning a \code{data.frame}.} \item{title}{An optional title for the dialog.} \item{easyClose}{If \code{TRUE}, the modal dialog can be dismissed by clicking outside the dialog box, or be pressing the Escape key. If \code{FALSE} (the default), the modal dialog can't be dismissed in those ways; instead it must be dismissed by clicking on a \code{modalButton()}, or from a call to \code{\link[shiny:removeModal]{removeModal()}} on the server.} \item{size}{One of \code{"s"} for small, \code{"m"} (the default) for medium, \code{"l"} for large, or \code{"xl"} for extra large. Note that \code{"xl"} only works with Bootstrap 4 and above (to opt-in to Bootstrap 4+, pass \code{\link[bslib:bs_theme]{bslib::bs_theme()}} to the \code{theme} argument of a page container like \code{\link[shiny:fluidPage]{fluidPage()}}).} \item{footer}{UI for footer. Use \code{NULL} for no footer.} } \value{ A \code{\link[shiny:reactive]{shiny::reactive()}} function returning the data. } \description{ This module contain an interface to reorder the levels of a factor variable. } \examples{ library(shiny) library(datamods) library(ggplot2) ui <- fluidPage( theme = bslib::bs_theme(version = 5L, preset = "bootstrap"), shinyWidgets::html_dependency_winbox(), tags$h2("Reorder the Levels of a Factor"), fluidRow( column( width = 6, update_factor_ui("id"), actionButton("modal", "Or click here to open a modal to update factor's level"), tags$br(), tags$br(), actionButton("winbox", "Or click here to open a WinBox to create a column") ), column( width = 6, selectInput( "var", label = "Variable to plot:", choices = NULL ), plotOutput("plot"), verbatimTextOutput("res") ) ) ) server <- function(input, output, session) { rv <- reactiveValues(data = MASS::Cars93[c(1, 2, 3, 9, 10, 11, 16, 26, 27)]) observe( updateSelectInput(inputId = "var", choices = names(rv$data)) ) # Inline mode data_inline_r <- update_factor_server( id = "id", data_r = reactive(rv$data) ) observeEvent(data_inline_r(), rv$data <- data_inline_r()) # modal window mode observeEvent(input$modal, modal_update_factor("modal")) data_modal_r <- update_factor_server( id = "modal", data_r = reactive(rv$data) ) observeEvent(data_modal_r(), { shiny::removeModal() rv$data <- data_modal_r() }) # winbox mode observeEvent(input$winbox, winbox_update_factor("winbox")) data_winbox_r <- update_factor_server( id = "winbox", data_r = reactive(rv$data) ) observeEvent(data_winbox_r(), rv$data <- data_winbox_r()) # Plot results output$plot <- renderPlot({ req(input$var, rv$data) ggplot(rv$data) + aes(x = !!sym(input$var)) + geom_bar() }) # Show results output$res <- renderPrint({ data <- req(rv$data) str(data) }) } if (interactive()) shinyApp(ui, server) } ================================================ FILE: man/update-variables.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/update-variables.R \name{update-variables} \alias{update-variables} \alias{update_variables_ui} \alias{update_variables_server} \title{Select, rename and convert variables} \usage{ update_variables_ui(id, title = TRUE) update_variables_server( id, data, height = NULL, return_data_on_init = FALSE, try_silent = FALSE ) } \arguments{ \item{id}{Module's ID} \item{title}{Module's title, if \code{TRUE} use the default title, use \code{NULL} for no title or a \code{shiny.tag} for a custom one.} \item{data}{a \code{data.frame} or a \code{reactive} function returning a \code{data.frame}.} \item{height}{Height for the table.} \item{return_data_on_init}{Return initial data when module is called.} \item{try_silent}{logical: should the report of error messages be suppressed?} } \value{ A \code{\link[shiny:reactive]{shiny::reactive()}} function returning the updated data. } \description{ Select, rename and convert variables } \examples{ library(shiny) library(datamods) testdata <- data.frame( date_as_char = as.character(Sys.Date() + 0:9), date_as_num = as.numeric(Sys.Date() + 0:9), datetime_as_char = as.character(Sys.time() + 0:9 * 3600*24), datetime_as_num = as.numeric(Sys.time() + 0:9 * 3600*24), num_as_char = as.character(1:10), char = month.name[1:10], char_na = c("A", "A", "B", NA, "B", "A", NA, "B", "A", "B"), stringsAsFactors = FALSE ) ui <- fluidPage( theme = bslib::bs_theme(version = 5L, preset = "bootstrap"), tags$h3("Select, rename and convert variables"), fluidRow( column( width = 6, # radioButtons() update_variables_ui("vars") ), column( width = 6, tags$b("original data:"), verbatimTextOutput("original"), verbatimTextOutput("original_str"), tags$b("Modified data:"), verbatimTextOutput("modified"), verbatimTextOutput("modified_str") ) ) ) server <- function(input, output, session) { updated_data <- update_variables_server( id = "vars", data = reactive(testdata), return_data_on_init = FALSE ) output$original <- renderPrint({ testdata }) output$original_str <- renderPrint({ str(testdata) }) output$modified <- renderPrint({ updated_data() }) output$modified_str <- renderPrint({ str(updated_data()) }) } if (interactive()) shinyApp(ui, server) } ================================================ FILE: man/validation.Rd ================================================ % Generated by roxygen2: do not edit by hand % Please edit documentation in R/validation.R \name{validation_ui} \alias{validation_ui} \alias{validation_server} \title{Validation module} \usage{ validation_ui(id, display = c("dropdown", "inline"), max_height = NULL, ...) validation_server( id, data, n_row = NULL, n_col = NULL, n_row_label = i18n("Valid number of rows"), n_col_label = i18n("Valid number of columns"), btn_label = i18n("Dataset validation:"), rules = NULL, bs_version = 3 ) } \arguments{ \item{id}{Module's ID.} \item{display}{Display validation results in a dropdown menu by clicking on a button or display results directly in interface.} \item{max_height}{Maximum height for validation results element, useful if you have many rules.} \item{...}{Arguments passed to \code{actionButton} or \code{uiOutput} depending on display mode, you cannot use \code{inputId}/\code{outputId}, \code{label} or \code{icon} (button only).} \item{data}{a \code{reactive} function returning a \code{data.frame}.} \item{n_row, n_col}{A one-sided formula to check number of rows and columns respectively, see below for examples.} \item{n_row_label, n_col_label}{Text to be displayed with the result of the check for number of rows/columns.} \item{btn_label}{Label for the dropdown button, will be followed by validation result.} \item{rules}{An object of class \code{validator} created with \code{validate::validator}.} \item{bs_version}{Bootstrap version used, it may affect rendering, especially status badges.} } \value{ \itemize{ \item UI: HTML tags that can be included in shiny's UI \item Server: a \code{list} with two slots: \itemize{ \item \strong{status}: a \code{reactive} function returning the best status available between \code{"OK"}, \code{"Failed"} or \code{"Error"}. \item \strong{details}: a \code{reactive} function returning a \code{list} with validation details. } } } \description{ Check that a dataset respect some validation expectations. } \examples{ library(datamods) library(shiny) if (requireNamespace("validate")) { library(validate) # Define some rules to be applied to data myrules <- validator( is.character(Manufacturer) | is.factor(Manufacturer), is.numeric(Price), Price > 12, # we should use 0 for testing positivity, but that's for the example !is.na(Luggage.room), in_range(Cylinders, min = 4, max = 8), Man.trans.avail \%in\% c("Yes", "No") ) # Add some labels label(myrules) <- c( "Variable Manufacturer must be character", "Variable Price must be numeric", "Variable Price must be strictly positive", "Luggage.room must not contain any missing values", "Cylinders must be between 4 and 8", "Man.trans.avail must be 'Yes' or 'No'" ) # you can also add a description() ui <- fluidPage( tags$h2("Validation"), fluidRow( column( width = 4, radioButtons( inputId = "dataset", label = "Choose dataset:", choices = c("mtcars", "MASS::Cars93") ), tags$p("Dropdown example:"), validation_ui("validation1"), tags$br(), tags$p("Inline example:"), validation_ui("validation2", display = "inline") ), column( width = 8, tags$b("Status:"), verbatimTextOutput("status"), tags$b("Details:"), verbatimTextOutput("details") ) ) ) server <- function(input, output, session) { dataset <- reactive({ if (input$dataset == "mtcars") { mtcars } else { MASS::Cars93 } }) results <- validation_server( id = "validation1", data = dataset, n_row = ~ . > 20, # more than 20 rows n_col = ~ . >= 3, # at least 3 columns rules = myrules ) validation_server( id = "validation2", data = dataset, n_row = ~ . > 20, # more than 20 rows n_col = ~ . >= 3, # at least 3 columns rules = myrules ) output$status <- renderPrint(results$status()) output$details <- renderPrint(results$details()) } if (interactive()) shinyApp(ui, server) } } ================================================ FILE: man-roxygen/module-import.R ================================================ #' @return #' * UI: HTML tags that can be included in shiny's UI #' * Server: a `list` with three slots: #' + **status**: a `reactive` function returning the status: `NULL`, `error` or `success`. #' + **name**: a `reactive` function returning the name of the imported data as `character`. #' + **data**: a `reactive` function returning the imported `data.frame`. ================================================ FILE: tests/testthat/test-edit-data.R ================================================ test_that("edit_data_ui works", { expect_is(edit_data_ui("ID"), "shiny.tag.list") }) test_that("table_display works", { mydata <- iris mydata <- as.data.table(mydata) mydata[, .datamods_edit_update := as.character(seq_len(.N))] mydata[, .datamods_edit_delete := as.character(seq_len(.N))] mydata[, .datamods_id := seq_len(.N)] mydata <- table_display(mydata, colnames = NULL) expect_is(mydata, "reactable") expect_is(mydata, "htmlwidget") expect_length(mydata$x$tag$attribs$columns, 8) expect_equal(length(mydata$x$tag$attribs$columns), 8) }) test_that("col_def_update works", { col_def_update <- col_def_update() expect_is(col_def_update, "colDef") expect_equal(col_def_update$name, "Update") expect_named(col_def_update, c('name', 'sortable', 'filterable', 'html', 'width')) }) test_that("col_def_delete works", { col_def_delete <- col_def_delete() expect_is(col_def_delete, "colDef") expect_equal(col_def_delete$name, "Delete") expect_named(col_def_delete, c('name', 'sortable', 'filterable', 'html', 'width')) }) test_that("btn_update works", { expect_is(btn_update("input"), "function") expect_is(btn_update("input")(1), "html") expect_is(btn_update("input")(1), "character") }) test_that("btn_delete works", { expect_is(btn_delete("input"), "function") expect_is(btn_delete("input")(1), "html") expect_is(btn_delete("input")(1), "character") }) test_that("confirmation_window works", { expect_is(confirmation_window(inputId = "input", title = "titre"), "shiny.tag") }) ================================================ FILE: tests/testthat/test-filter-data.R ================================================ test_that("filter_data_ui works", { expect_is(filter_data_ui("ID"), "shiny.tag.list") }) test_that("create_filters works", { filters <- create_filters(iris, session = list(ns = identity)) expect_length(filters, 3) expect_named(filters, c("ui", "filters_id", "filters_na_id")) expect_is(filters$ui, "shiny.tag") expect_length(filters$filters_id, ncol(iris)) expect_equal(length(filters$ui$children[[1]]), length(filters$filters_id)) expect_equal(length(filters$filters_id), length(filters$filters_na_id)) }) test_that("create_filters with options works", { filters <- create_filters(iris, vars = names(iris)[1:3], widget_num = "range", session = list(ns = identity)) expect_length(filters, 3) expect_named(filters, c("ui", "filters_id", "filters_na_id")) expect_is(filters$ui, "shiny.tag") expect_length(filters$filters_id, 3) expect_equal(length(filters$ui$children[[1]]), length(filters$filters_id)) expect_equal(length(filters$filters_id), length(filters$filters_na_id)) }) test_that("create_filters with dates and ids works", { mydata <- data.frame( date = seq(as.Date("2021-01-01"), by = "1 month", length.out = 12), name = month.name, num = rep(c(1, 2), each = 6) ) filters <- create_filters(mydata, session = list(ns = identity)) expect_length(filters, 3) expect_named(filters, c("ui", "filters_id", "filters_na_id")) expect_is(filters$ui, "shiny.tag") expect_length(filters$filters_id, 2) expect_equal(length(filters$ui$children[[1]]), length(filters$filters_id)) expect_equal(length(filters$filters_id), length(filters$filters_na_id)) }) test_that("create_filters with dates and ids works (bis)", { mydata <- data.frame( date = seq(as.Date("2021-01-01"), by = "1 month", length.out = 12), name = month.name, num = rep(c(1, 2), each = 6) ) filters <- create_filters(mydata, widget_date = "range", session = list(ns = identity)) expect_length(filters, 3) expect_named(filters, c("ui", "filters_id", "filters_na_id")) expect_is(filters$ui, "shiny.tag") expect_length(filters$filters_id, 2) expect_equal(length(filters$ui$children[[1]]), length(filters$filters_id)) expect_equal(length(filters$filters_id), length(filters$filters_na_id)) }) test_that("make_expr_filter works", { filter_inputs <- lapply( X = iris, FUN = function(x) { sort(sample(unique(x), 2)) } ) filter_nas <- lapply( X = iris, FUN = function(x) { sample(c(TRUE, FALSE), 2) } ) filters <- make_expr_filter( filters = filter_inputs, filters_na = filter_nas, data = iris, data_name = "iris" ) expect_length(filters, 2) expect_named(filters, c("expr_dplyr", "expr")) expect_is(filters$expr_dplyr, "call") expect_is(filters$expr, "call") }) test_that("make_expr_filter with dates works", { mydata <- data.frame( date = seq(as.Date("2021-01-01"), by = "1 month", length.out = 12), name = month.name, num = rep(c(1, 2), each = 6) ) filter_inputs <- lapply( X = mydata, FUN = function(x) { sort(sample(unique(x), 2)) } ) filter_nas <- lapply( X = mydata, FUN = function(x) { sample(c(TRUE, FALSE), 2) } ) filters <- make_expr_filter( filters = filter_inputs, filters_na = filter_nas, data = mydata, data_name = "mydata" ) expect_length(filters, 2) expect_named(filters, c("expr_dplyr", "expr")) expect_is(filters$expr_dplyr, "call") expect_is(filters$expr, "call") }) ================================================ FILE: tests/testthat/test-i18n.R ================================================ test_that("i18n works if option not set", { options("datamods.i18n" = NULL) label <- "something" expect_identical(i18n(label), label) }) test_that("i18n works with translation argument", { options("datamods.i18n" = NULL) label <- "something" translation <- "quelque chose" l <- list(translation) names(l) <- label expect_identical(i18n(label, l), translation) }) test_that("i18n works with option set", { options("datamods.i18n" = NULL) label <- "something" translation <- "quelque chose" l <- list(translation) names(l) <- label options("i18n" = l) expect_identical(i18n(label, i18n_translations(NULL)), translation) }) test_that("i18n works with list", { label <- "something" translation <- "quelque chose" l <- list(translation) names(l) <- label options("datamods.i18n" = l) expect_identical(i18n_test(label), translation) expect_warning(i18n_test("label")) }) test_that("i18n works with data.frame", { label <- "something" translation <- "quelque chose" options("datamods.i18n" = data.frame( label = label, translation = translation, stringsAsFactors = FALSE )) expect_identical(i18n_test(label), translation) expect_warning(i18n_test("label")) }) test_that("i18n works with file", { options("datamods.i18n" = system.file( "i18n", "fr.csv", package = "datamods" )) expect_identical(i18n_test("Help"), "Aide") }) test_that("i18n works with supported language", { options("datamods.i18n" = "fr") expect_identical(i18n_test("Help"), "Aide") options("datamods.i18n" = "mk") expect_type(i18n_test("Help"), "character") options("datamods.i18n" = "pt") expect_type(i18n_test("Help"), "character") options("datamods.i18n" = "al") expect_type(i18n_test("Help"), "character") }) test_that("i18n dont work if no list, no data.frame, no file", { options("datamods.i18n" = Sys.Date()) on.exit(options("datamods.i18n" = NULL)) label <- "something" expect_error(i18n_test(label)) }) ================================================ FILE: tests/testthat/test-import-copypaste.R ================================================ test_that("import_copypaste_ui works", { expect_is(import_copypaste_ui("ID"), "shiny.tag") }) test_that("import_copypaste_server works", { shiny::testServer(import_copypaste_server, { session$setInputs(data_pasted = 0) #to bypass ignoreInit = TRUE session$setInputs( data_pasted = "x y z 1 2 3", confirm = 0 ) expect_is(imported_rv$data, "data.frame") expect_is(session$getReturned()$data(), "data.frame") }) }) ================================================ FILE: tests/testthat/test-import-file.R ================================================ test_that("import_file_ui works", { expect_is(import_file_ui("ID"), "shiny.tag") }) test_that("import_file_server works", { shiny::testServer(import_file_server, { session$setInputs(sheet = 0) #to bypass ignoreInit = TRUE session$setInputs( file = data.frame( datapath = system.file("extdata", "mtcars.csv", package = "datamods"), stringsAsFactors = FALSE ), sheet = 0, skip_rows = 0, confirm = 0, dec = ".", encoding = "UTF-8", na_label = ",NA" ) expect_is(imported_rv$data, "data.frame") expect_is(session$getReturned()$data(), "data.frame") }) }) ================================================ FILE: tests/testthat/test-import-globalenv.R ================================================ test_that("import_globalenv_ui works", { expect_is(import_globalenv_ui("ID"), "shiny.tag") }) test_that("import_globalenv_server works", { data(mtcars) shiny::testServer(import_globalenv_server, { session$setInputs(data = 0) #to bypass ignoreInit = TRUE session$setInputs( data = "mtcars", confirm = 0 ) expect_equal(imported_rv$name, input$data) expect_equal(session$getReturned()$name(), input$data) expect_is(imported_rv$data, "data.frame") expect_is(session$getReturned()$data(), "data.frame") session$setInputs(env = "datasets") session$setInputs(data = "faithful", confirm = 1) expect_is(session$getReturned()$data(), "data.frame") expect_equivalent(session$getReturned()$data(), faithful) }) }) test_that("get_dimensions works", { expect_null(get_dimensions(NULL)) mydata <- mtcars mydata2 <- mtcars expect_is(get_dimensions("mydata"), "character") expect_length(get_dimensions(c("mydata", "mydata2")), 2) mylist <- list(a = 1) expect_identical(unname(get_dimensions("mylist")), "Not a data.frame") }) test_that("list_pkg_data works", { expect_null(list_pkg_data("not.a.package")) expect_is(list_pkg_data("datasets"), "character") }) ================================================ FILE: tests/testthat/test-import-googlesheets.R ================================================ test_that("import_googlesheets_ui works", { expect_is(import_googlesheets_ui("ID"), "shiny.tag") }) test_that("import_googlesheets_server works", { testthat::skip_on_cran() testthat::skip_if_offline() shiny::testServer(import_googlesheets_server, { session$setInputs(link = 0) #to bypass ignoreInit = TRUE session$setInputs( link = "https://docs.google.com/spreadsheets/d/1U6Cf_qEOhiR9AZqTqS3mbMF3zt2db48ZP5v3rkrAEJY/edit?usp=sharing", confirm = 0 ) expect_is(imported_rv$data, "data.frame") expect_is(session$getReturned()$data(), "data.frame") }) }) ================================================ FILE: tests/testthat/test-import-modal.R ================================================ test_that("import_ui works", { expect_is(import_ui("ID"), "shiny.tag") }) test_that("import_server works", { shiny::testServer(import_server, args = list(return_class = "data.table"), { data(mtcars) session$env$data_rv$data = mtcars session$env$data_rv$name = "mtcars" session$setInputs(confirm = 1) expect_is(session$getReturned()$data(), "data.table") expect_equal(session$getReturned()$name(), "mtcars") }) }) ================================================ FILE: tests/testthat/test-import-url.R ================================================ test_that("import_url_ui works", { expect_is(import_url_ui("ID"), "shiny.tag") }) test_that("import_url_server works with json", { testthat::skip_on_cran() testthat::skip_if_offline() shiny::testServer(import_url_server, { session$setInputs(link = 0) #to bypass ignoreInit = TRUE session$setInputs( link = "https://raw.githubusercontent.com/dreamRs/datamods/master/inst/extdata/mtcars.json", confirm = 0 ) expect_is(imported_rv$data, "data.frame") expect_is(session$getReturned()$data(), "data.frame") }) }) test_that("import_url_server works with csv", { testthat::skip_on_cran() testthat::skip_if_offline() shiny::testServer(import_url_server, { session$setInputs(link = 0) #to bypass ignoreInit = TRUE session$setInputs( link = "https://raw.githubusercontent.com/dreamRs/datamods/master/inst/extdata/mtcars.csv", confirm = 0 ) expect_is(imported_rv$data, "data.frame") expect_is(session$getReturned()$data(), "data.frame") }) }) test_that("import_url_server works with shortened URL", { testthat::skip_on_cran() testthat::skip_if_offline() shiny::testServer(import_url_server, { session$setInputs(link = 0) #to bypass ignoreInit = TRUE session$setInputs( link = "https://tinyurl.com/datamodsjson", confirm = 0 ) expect_is(imported_rv$data, "data.frame") expect_is(session$getReturned()$data(), "data.frame") }) }) ================================================ FILE: tests/testthat/test-onLoad.R ================================================ test_that("onLoad works", { .onLoad() x <- shiny::resourcePaths() expect_true("datamods" %in% names(x)) }) ================================================ FILE: tests/testthat/test-update-variables.R ================================================ test_that("update_variables_ui works", { expect_is(update_variables_ui("ID"), "shiny.tag") }) # test_that("update_variables_server works", { # data(mtcars) # shiny::testServer(update_variables_server, args = list(data = mtcars), { # tok <- token$x # selection <- as.list(rep(TRUE, ncol(mtcars))) # names(selection) <- paste("selection", tok, pad0(seq_along(mtcars)), sep = "-") # # name <- as.list(names(mtcars)) # names(name) <- paste("name", tok, pad0(seq_along(mtcars)), sep = "-") # # class_toset <- as.list(rep("numeric", ncol(mtcars))) # names(class_toset) <- paste("class_to_set", tok, pad0(seq_along(mtcars)), sep = "-") # # do.call(session$setInputs, selection) # do.call(session$setInputs, name) # do.call(session$setInputs, class_toset) # # session$setInputs(validate = 1) # # # print(session$getReturned()()) # expect_is(session$getReturned()(), "data.frame") # expect_equal(session$getReturned()(), mtcars) # }) # }) test_that("get_classes works", { expect_is(get_classes(mtcars), "character") expect_length(get_classes(mtcars), ncol(mtcars)) }) test_that("get_classes works", { expect_is(get_n_unique(mtcars), "integer") expect_length(get_n_unique(mtcars), ncol(mtcars)) }) test_that("pad0 works", { expect_is(pad0(c(1, 15, 150, NA)), "character") expect_length(pad0(c(1, 15, 150, NA)), 4) }) test_that("summary_vars works", { expect_is(summary_vars(mtcars), "data.frame") expect_identical(nrow(summary_vars(mtcars)), ncol(mtcars)) }) test_that("update_variables_datagrid works", { variables <- summary_vars(iris) dt <- update_variables_datagrid(variables) expect_is(dt, "htmlwidget") }) test_that("convert_to works", { dat <- data.frame( v1 = month.name, v2 = month.abb, v3 = 1:12, v4 = as.numeric(Sys.Date() + 0:11), v5 = as.character(Sys.Date() + 0:11), v6 = as.factor(c("a", "a", "b", "a", "b", "a", "a", "b", "a", "b", "b", "a")), v7 = as.character(11:22), stringsAsFactors = FALSE ) expect_is( convert_to(dat, "v3", "character")$v3, "character" ) expect_is( convert_to(dat, "v6", "character")$v6, "character" ) expect_is( convert_to(dat, "v7", "numeric")$v7, "numeric" ) expect_is( convert_to(dat, "v4", "date", origin = "1970-01-01")$v4, "Date" ) expect_is( convert_to(dat, "v5", "date")$v5, "Date" ) }) test_that("get_vars_to_convert works", { # 2 variables to convert new_classes <- list( "Sepal.Length" = "numeric", "Sepal.Width" = "numeric", "Petal.Length" = "character", "Petal.Width" = "numeric", "Species" = "character" ) res <- get_vars_to_convert(summary_vars(iris), new_classes) expect_is(res, "data.frame") expect_identical(nrow(res), 2L) # No changes new_classes <- list( "Sepal.Length" = "numeric", "Sepal.Width" = "numeric", "Petal.Length" = "numeric", "Petal.Width" = "numeric", "Species" = "factor" ) res <- get_vars_to_convert(summary_vars(iris), new_classes) expect_is(res, "data.frame") expect_identical(nrow(res), 0L) new_classes <- list( "mpg" = "character", "cyl" = "numeric", "disp" = "character", "hp" = "numeric", "drat" = "character", "wt" = "character", "qsec" = "numeric", "vs" = "character", "am" = "numeric", "gear" = "character", "carb" = "integer" ) res <- get_vars_to_convert(summary_vars(mtcars), new_classes) expect_is(res, "data.frame") expect_identical(nrow(res), 7L) }) ================================================ FILE: tests/testthat/test-validation.R ================================================ test_that("validation_ui works", { expect_is(validation_ui("ID"), "shiny.tag.list") expect_is(validation_ui("ID", display = "inline"), "shiny.tag.list") }) # test_that("validation_server works", { # rv <- shiny::reactiveVal(cars) # shiny::testServer( # validation_server, # args = list( # data = rv, # n_row = ~ . > 20, # n_col = ~ . >= 3, # rules = validate::validator( # speed >= 0 # , dist >= 0 # , speed/dist <= 1.5 # ) # ), { # rv(cars) # expect_is(valid_ui$x, "shiny.tag") # expect_is(valid_rv$status, "character") # } # ) # }) ================================================ FILE: tests/testthat.R ================================================ library(testthat) library(datamods) test_check("datamods") ================================================ FILE: vignettes/.gitignore ================================================ *.html *.R ================================================ FILE: vignettes/datamods.Rmd ================================================ --- title: "Getting started with datamods" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Getting started with datamods} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", eval = FALSE ) ``` ```{r setup} library(datamods) ``` The {datamods} package contains modules to work with data in Shiny application, currently the following modules are implemented : * Import modules : import data from various sources * Update table structure: select columns to keep, rename variable and convert from a class to anoter (e.g. numeric to character) * Filter data : interactively filter a `data.frame` * Validate : check that data respect some expectations (with [package {validate}](https://cran.r-project.org/package=validate)) * Sample data: interactively sample a `data.frame`. ## Import ### From environment Imports data from the user's global environment or a package environment to retrieve included in it. It searches for data sets in the global environment and lets the user choose the data to use. ```r # UI import_globalenv_ui("myid") # Server imported <- import_globalenv_server("myid") ``` ### From file Imports data from an external file. The file can be of any format, csv, xlsx, tsv etc.. Import is performed by package [rio](https://github.com/gesistsa/rio). In case of Excel files, it gives an option to choose the sheet. ```r # UI import_file_ui("myid") # Server imported <- import_file_server("myid") ``` ### From clipboard Imports data via copy/paste. Simply copy and paste data from any source. ```r # UI import_copypaste_ui("myid") # Server imported <- import_copypaste_server("myid") ``` ### From Googlesheet Imports data from a Googlesheet. Use the shareable link to read data. ```r # UI import_googlesheets_ui("myid") # Server imported <- import_googlesheets_server("myid") ``` ### From URL Imports data from a URL. Only flat data in any format supported by [package rio](https://CRAN.R-project.org/package=rio/vignettes/rio.html#Supported_file_formats). ```r # UI import_url_ui("myid") # Server imported <- import_url_server("myid") ``` ### Usage All modules are used in the same way in a Shiny application, here is an example: ```r library(shiny) library(datamods) ui <- fluidPage( tags$h3("Import data with copy & paste"), fluidRow( column( width = 4, import_copypaste_ui("myid") ), column( width = 8, tags$b("Imported data:"), verbatimTextOutput(outputId = "status"), verbatimTextOutput(outputId = "data") ) ) ) server <- function(input, output, session) { imported <- import_copypaste_server("myid") output$status <- renderPrint({ imported$status() }) output$data <- renderPrint({ imported$data() }) } shinyApp(ui, server) ``` All modules have the same return value server-side, a `list` with three slots: * **status**: a `reactive` function returning the status: `NULL`, `error` or `success`. * **name**: a `reactive` function returning the name of the imported data as `character`. * **data**: a `reactive` function returning the imported `data.frame`. ### Modal Window All modules can be launched at once in a modal window: Launch the modal server-side with: ```r observeEvent(input$launch_modal, { import_modal( id = "myid", title = "Import data to be used in application" ) }) ``` See `?import_modal` for a complete example. ## Update Modules This module allow to dynamically select, rename and convert variables of a dataset. Some options for converting to date and numeric are available in a dropdown menu. Return value of the module is a `reactive` function with the update data. ## Validate When importing data into an application it can be useful to check that data respect some expectations: number of rows/columns, existence of a variable, ... This module allow to validate rules defined with package [validate](https://github.com/data-cleaning/validate). ```r # UI validation_ui("validation", display = "inline") # Server results <- validation_server( id = "validation", data = dataset, n_row = ~ . > 20, # more than 20 rows n_col = ~ . >= 3, # at least 3 columns rules = myrules ) # Rules are defined as follow: myrules <- validator( is.character(Manufacturer) | is.factor(Manufacturer), is.numeric(Price), Price > 12, # we should use 0 for testing positivity, but that's for the example !is.na(Luggage.room), in_range(Cylinders, min = 4, max = 8), Man.trans.avail %in% c("Yes", "No") ) # Add some labels label(myrules) <- c( "Variable Manufacturer must be character", "Variable Price must be numeric", "Variable Price must be strictly positive", "Luggage.room must not contain any missing values", "Cylinders must be between 4 and 8", "Man.trans.avail must be 'Yes' or 'No'" ) # you can also add a description() ``` Validation results can be displayed in a dropdown menu (above left) or inline where the module is called. The return value server-side is a list with the following items: * **status**: a reactive function returning the best status available between "OK", "Failed" or "Error". * **details**: a reactive function returning a list with validation details. ## Filter Interactively filter a `data.frame` and generate code to reproduce filters applied: ```r # UI filter_data_ui("filtering", max_height = "500px") # Server res_filter <- filter_data_server( id = "filtering", data = reactive(mtcars), name = reactive("mtcars"), vars = reactive(names(mtcars)), widget_num = "slider", widget_date = "slider", label_na = "Missing" ) ``` You can select variables for which to create a filter and choose widgets used to create the UI filter. The return value server-side is a list with the following items: * **filtered**: a `reactive` function returning the data filtered. * **code**: a `reactive` function returning the dplyr pipeline to filter data. * **expr**: a `reactive` function returning an expression to filter data. ## Sample Interactively sample a `data.frame` to keep only part of the data, depending on the number or proportion of rows to keep. ```r # UI sample_ui("myID") # Server result_sample <- sample_server("myID", reactive(iris)) ``` ## Edit Interactively edit a `data.frame`, this module also allow to : * **adding**, **deleting** and **modifying** rows * choosing **editable columns** and choosing **mandatory columns** * **exporting data** (csv and Excel) ```r # UI edit_data_ui(id = "id") # Server res_edited <- edit_data_server( id = "id", data_r = reactive(demo_edit), add = TRUE, update = TRUE, delete = TRUE, download_csv = TRUE, download_excel = TRUE, file_name_export = "datas", var_edit = c("name", "job", "credit_card_provider", "credit_card_security_code"), var_mandatory = c("name", "job") ) ``` This module returns the edited table with the user modifications. See ?demo_edit to see the data created for this data edit example. ================================================ FILE: vignettes/i18n.Rmd ================================================ --- title: "Internationalization" output: rmarkdown::html_vignette vignette: > %\VignetteIndexEntry{Internationalization} %\VignetteEngine{knitr::rmarkdown} %\VignetteEncoding{UTF-8} --- ```{r, include = FALSE} knitr::opts_chunk$set( collapse = TRUE, comment = "#>", eval = FALSE ) ``` ```{r setup} library(datamods) ``` When using {datamods} modules, a simple way to modify labels display is provided for using a different language or simply use other labels. There are 4 different ways to use new labels: ```r # Using a supported language set_i18n("fr") # Using a named list set_i18n(list("Some label" = "Its translation", ...)) # Using a data.framewith 2 columns set_i18n(data.frame(label = c(...), translation = c(...))) # Using a CSV file set_i18n("path/to/file.csv") ``` ## Integrated languages The following languages are integrated in {datamods} : * ![](figures/i18n/gb.svg){height=16, style="height:16px"} english, the default. * ![](figures/i18n/fr.svg){height=16, style="height:16px"} french, activate with: ```{r} set_i18n("fr") ``` * ![](figures/i18n/mk.svg){height=16, style="height:16px"} macedonian, activate with: ```{r} set_i18n("mk") ``` * ![](figures/i18n/br.svg){height=16, style="height:16px"} ![](figures/i18n/pt.svg){height=16, style="height:16px"} brazilian portuguese, activate with: ```{r} set_i18n("pt") ``` * ![](figures/i18n/al.svg){height=16, style="height:16px"} albanian, activate with: ```{r} set_i18n("al") ``` * ![](figures/i18n/cn.svg){height=16, style="height:16px"} chinese, activate with: ```{r} set_i18n("cn") ``` * ![](figures/i18n/es.svg){height=16, style="height:16px"} spanish, activate with: ```{r} set_i18n("es") ``` * ![](figures/i18n/de.svg){height=16, style="height:16px"} german, activate with: ```{r} set_i18n("de") ``` * ![](figures/i18n/tr.svg){height=16, style="height:16px"} turkish, activate with: ```{r} set_i18n("tr") ``` * ![](figures/i18n/kr.svg){height=16, style="height:16px"} korean, activate with: ```{r} set_i18n("kr") ``` * ![](figures/i18n/pl.svg){height=16, style="height:16px"} polish, activate with: ```{r} set_i18n("pl") ``` * ![](figures/i18n/ja.svg){height=16, style="height:16px"} japanese, activate with: ```{r} set_i18n("ja") ``` If you want another language to be supported, you can submit a Pull Request to add a CSV file like the one used for french (file is located in `inst/i18n` folder in the package, you can see it [here on GitHub](https://github.com/dreamRs/datamods/blob/master/inst/i18n/fr.csv)). ## Using a list You can change labels with a named `list`, where names correspond to the labels and values to the translation to use: ```r options("datamods.i18n" = list( "Import a dataset from an environment" = "Importer un jeu de données depuis l'environnement global", "Select a data.frame:" = "Sélectionner un data.frame :", ... )) ``` ## Using a data.frame You can change labels with a `data.frame` with two columns `label` (the original label) and `translation` (the new label to display): ```r set_i18n(data.frame( label = c("Import a dataset from an environment", "Select a data.frame:", ...), translation = c("Importer un jeu de données depuis l'environnement global", "Sélectionner un data.frame :", ...) )) ``` ## Using a file Use a CSV file with same structure than `data.frame` above: ```r set_i18n("path/to/file.csv") ``` An example of file is shown below. ## All labels Here's the file used for french translation with all labels used in the package: ```{r, echo=FALSE, eval=TRUE, comment=""} cat(readLines(system.file("i18n", "fr.csv", package = "datamods"), encoding = "UTF-8"), sep = '\n') ```