lynguine 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of lynguine might be problematic. Click here for more details.
- lynguine-0.1.0/PKG-INFO +191 -0
- lynguine-0.1.0/README.md +152 -0
- lynguine-0.1.0/lynguine/.coverage +0 -0
- lynguine-0.1.0/lynguine/__init__.py +10 -0
- lynguine-0.1.0/lynguine/access/__pycache__/__init__.cpython-311.pyc +0 -0
- lynguine-0.1.0/lynguine/access/__pycache__/download.cpython-311.pyc +0 -0
- lynguine-0.1.0/lynguine/access/__pycache__/io.cpython-311.pyc +0 -0
- lynguine-0.1.0/lynguine/access/__pycache__/io.cpython-312.pyc +0 -0
- lynguine-0.1.0/lynguine/access/download.py +311 -0
- lynguine-0.1.0/lynguine/access/io.py +1912 -0
- lynguine-0.1.0/lynguine/assess/__pycache__/compute.cpython-311.pyc +0 -0
- lynguine-0.1.0/lynguine/assess/__pycache__/compute.cpython-312.pyc +0 -0
- lynguine-0.1.0/lynguine/assess/__pycache__/data.cpython-311.pyc +0 -0
- lynguine-0.1.0/lynguine/assess/__pycache__/data.cpython-312.pyc +0 -0
- lynguine-0.1.0/lynguine/assess/compute.py +412 -0
- lynguine-0.1.0/lynguine/assess/data.py +2957 -0
- lynguine-0.1.0/lynguine/clone_or_pull.py +31 -0
- lynguine-0.1.0/lynguine/compute.py +225 -0
- lynguine-0.1.0/lynguine/config/__pycache__/context.cpython-311.pyc +0 -0
- lynguine-0.1.0/lynguine/config/__pycache__/context.cpython-312.pyc +0 -0
- lynguine-0.1.0/lynguine/config/__pycache__/interface.cpython-311.pyc +0 -0
- lynguine-0.1.0/lynguine/config/__pycache__/interface.cpython-312.pyc +0 -0
- lynguine-0.1.0/lynguine/config/__pycache__/settings.cpython-311.pyc +0 -0
- lynguine-0.1.0/lynguine/config/context.py +213 -0
- lynguine-0.1.0/lynguine/config/defaults.yml +31 -0
- lynguine-0.1.0/lynguine/config/interface.py +467 -0
- lynguine-0.1.0/lynguine/convert.py +36 -0
- lynguine-0.1.0/lynguine/coverage.xml +3226 -0
- lynguine-0.1.0/lynguine/log.py +44 -0
- lynguine-0.1.0/lynguine/ndlappointment.py +309 -0
- lynguine-0.1.0/lynguine/ndlcal.py +98 -0
- lynguine-0.1.0/lynguine/ndlcgi.py +285 -0
- lynguine-0.1.0/lynguine/ndlconvert.py +25 -0
- lynguine-0.1.0/lynguine/ndlexpenses.py +51 -0
- lynguine-0.1.0/lynguine/ndlimage.py +51 -0
- lynguine-0.1.0/lynguine/ndlmarkdown.py +51 -0
- lynguine-0.1.0/lynguine/ndlstrutil.py +53 -0
- lynguine-0.1.0/lynguine/ndltext.py +42 -0
- lynguine-0.1.0/lynguine/old_data.py +84 -0
- lynguine-0.1.0/lynguine/ruby/Gemfile +4 -0
- lynguine-0.1.0/lynguine/ruby/json2yaml.rb +39 -0
- lynguine-0.1.0/lynguine/test.xlsx +0 -0
- lynguine-0.1.0/lynguine/tests/__pycache__/test_access_download.cpython-311-pytest-6.2.5.pyc +0 -0
- lynguine-0.1.0/lynguine/tests/__pycache__/test_access_io.cpython-311-pytest-6.2.5.pyc +0 -0
- lynguine-0.1.0/lynguine/tests/__pycache__/test_access_io.cpython-311.pyc +0 -0
- lynguine-0.1.0/lynguine/tests/__pycache__/test_assess_compute.cpython-311-pytest-6.2.5.pyc +0 -0
- lynguine-0.1.0/lynguine/tests/__pycache__/test_assess_data.cpython-311-pytest-6.2.5.pyc +0 -0
- lynguine-0.1.0/lynguine/tests/__pycache__/test_assess_data.cpython-311-pytest-7.4.0.pyc +0 -0
- lynguine-0.1.0/lynguine/tests/__pycache__/test_assess_data.cpython-311.pyc +0 -0
- lynguine-0.1.0/lynguine/tests/__pycache__/test_assess_data_compute.cpython-311-pytest-6.2.5.pyc +0 -0
- lynguine-0.1.0/lynguine/tests/__pycache__/test_assess_data_compute.cpython-311.pyc +0 -0
- lynguine-0.1.0/lynguine/tests/__pycache__/test_config_context.cpython-311-pytest-6.2.5.pyc +0 -0
- lynguine-0.1.0/lynguine/tests/__pycache__/test_config_interface.cpython-311-pytest-6.2.5.pyc +0 -0
- lynguine-0.1.0/lynguine/tests/__pycache__/test_config_settings.cpython-311-pytest-6.2.5.pyc +0 -0
- lynguine-0.1.0/lynguine/tests/__pycache__/test_customdataframe_integration.cpython-311-pytest-6.2.5.pyc +0 -0
- lynguine-0.1.0/lynguine/tests/__pycache__/test_util_dataframe.cpython-311-pytest-6.2.5.pyc +0 -0
- lynguine-0.1.0/lynguine/tests/__pycache__/test_util_files.cpython-311-pytest-6.2.5.pyc +0 -0
- lynguine-0.1.0/lynguine/tests/__pycache__/test_util_liquid.cpython-311-pytest-6.2.5.pyc +0 -0
- lynguine-0.1.0/lynguine/tests/__pycache__/test_util_misc.cpython-311-pytest-6.2.5.pyc +0 -0
- lynguine-0.1.0/lynguine/tests/__pycache__/test_util_talk.cpython-311-pytest-6.2.5.pyc +0 -0
- lynguine-0.1.0/lynguine/tests/__pycache__/test_util_tex.cpython-311-pytest-6.2.5.pyc +0 -0
- lynguine-0.1.0/lynguine/tests/__pycache__/test_util_yaml.cpython-311-pytest-6.2.5.pyc +0 -0
- lynguine-0.1.0/lynguine/tests/linguine.log +31 -0
- lynguine-0.1.0/lynguine/tests/talk_tests.py +27 -0
- lynguine-0.1.0/lynguine/tests/test.xlsx +0 -0
- lynguine-0.1.0/lynguine/tests/test_access_download.py +220 -0
- lynguine-0.1.0/lynguine/tests/test_access_io.py +1320 -0
- lynguine-0.1.0/lynguine/tests/test_assess_compute.py +193 -0
- lynguine-0.1.0/lynguine/tests/test_assess_data.py +634 -0
- lynguine-0.1.0/lynguine/tests/test_assess_data_compute.py +88 -0
- lynguine-0.1.0/lynguine/tests/test_config_context.py +54 -0
- lynguine-0.1.0/lynguine/tests/test_config_interface.py +254 -0
- lynguine-0.1.0/lynguine/tests/test_customdataframe_integration.py +70 -0
- lynguine-0.1.0/lynguine/tests/test_util_dataframe.py +162 -0
- lynguine-0.1.0/lynguine/tests/test_util_files.py +70 -0
- lynguine-0.1.0/lynguine/tests/test_util_liquid.py +70 -0
- lynguine-0.1.0/lynguine/tests/test_util_misc.py +253 -0
- lynguine-0.1.0/lynguine/tests/test_util_talk.py +126 -0
- lynguine-0.1.0/lynguine/tests/test_util_tex.py +113 -0
- lynguine-0.1.0/lynguine/tests/test_util_yaml.py +78 -0
- lynguine-0.1.0/lynguine/util/.coverage +0 -0
- lynguine-0.1.0/lynguine/util/__pycache__/dataframe.cpython-311.pyc +0 -0
- lynguine-0.1.0/lynguine/util/__pycache__/dataframe.cpython-312.pyc +0 -0
- lynguine-0.1.0/lynguine/util/__pycache__/fake.cpython-311.pyc +0 -0
- lynguine-0.1.0/lynguine/util/__pycache__/fake.cpython-312.pyc +0 -0
- lynguine-0.1.0/lynguine/util/__pycache__/files.cpython-311.pyc +0 -0
- lynguine-0.1.0/lynguine/util/__pycache__/files.cpython-312.pyc +0 -0
- lynguine-0.1.0/lynguine/util/__pycache__/html.cpython-311.pyc +0 -0
- lynguine-0.1.0/lynguine/util/__pycache__/html.cpython-312.pyc +0 -0
- lynguine-0.1.0/lynguine/util/__pycache__/liquid.cpython-311.pyc +0 -0
- lynguine-0.1.0/lynguine/util/__pycache__/liquid.cpython-312.pyc +0 -0
- lynguine-0.1.0/lynguine/util/__pycache__/misc.cpython-311.pyc +0 -0
- lynguine-0.1.0/lynguine/util/__pycache__/misc.cpython-312.pyc +0 -0
- lynguine-0.1.0/lynguine/util/__pycache__/talk.cpython-311.pyc +0 -0
- lynguine-0.1.0/lynguine/util/__pycache__/talk.cpython-312.pyc +0 -0
- lynguine-0.1.0/lynguine/util/__pycache__/tex.cpython-311.pyc +0 -0
- lynguine-0.1.0/lynguine/util/__pycache__/tex.cpython-312.pyc +0 -0
- lynguine-0.1.0/lynguine/util/__pycache__/text.cpython-311.pyc +0 -0
- lynguine-0.1.0/lynguine/util/__pycache__/text.cpython-312.pyc +0 -0
- lynguine-0.1.0/lynguine/util/__pycache__/yaml.cpython-311.pyc +0 -0
- lynguine-0.1.0/lynguine/util/__pycache__/yaml.cpython-312.pyc +0 -0
- lynguine-0.1.0/lynguine/util/dataframe.py +401 -0
- lynguine-0.1.0/lynguine/util/fake.py +713 -0
- lynguine-0.1.0/lynguine/util/files.py +177 -0
- lynguine-0.1.0/lynguine/util/html.py +75 -0
- lynguine-0.1.0/lynguine/util/liquid.py +104 -0
- lynguine-0.1.0/lynguine/util/misc.py +374 -0
- lynguine-0.1.0/lynguine/util/talk.py +192 -0
- lynguine-0.1.0/lynguine/util/tex.py +394 -0
- lynguine-0.1.0/lynguine/util/text.py +12 -0
- lynguine-0.1.0/lynguine/util/yaml.py +81 -0
- lynguine-0.1.0/pyproject.toml +60 -0
lynguine-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
Metadata-Version: 2.1
|
|
2
|
+
Name: lynguine
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Package for linguine data oriented architecture interfaces.
|
|
5
|
+
Home-page: https://github.com/lawrennd/linguine
|
|
6
|
+
License: MIT
|
|
7
|
+
Author: Neil D. Lawrence
|
|
8
|
+
Author-email: lawrennd@gmail.com
|
|
9
|
+
Requires-Python: >=3.11,<4.0
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
16
|
+
Requires-Dist: XlsxWriter
|
|
17
|
+
Requires-Dist: anyascii
|
|
18
|
+
Requires-Dist: bibtexparser
|
|
19
|
+
Requires-Dist: gitpython
|
|
20
|
+
Requires-Dist: google-api-python-client
|
|
21
|
+
Requires-Dist: google-auth-httplib2
|
|
22
|
+
Requires-Dist: gspread-pandas
|
|
23
|
+
Requires-Dist: markdown
|
|
24
|
+
Requires-Dist: markdownify
|
|
25
|
+
Requires-Dist: mimesis (>11)
|
|
26
|
+
Requires-Dist: numpy (>=1.0.0,<2.0.0)
|
|
27
|
+
Requires-Dist: openpyxl
|
|
28
|
+
Requires-Dist: pandas
|
|
29
|
+
Requires-Dist: pylatexenc
|
|
30
|
+
Requires-Dist: pyminizip
|
|
31
|
+
Requires-Dist: pypandoc
|
|
32
|
+
Requires-Dist: python-frontmatter
|
|
33
|
+
Requires-Dist: python-liquid
|
|
34
|
+
Requires-Dist: pyyaml
|
|
35
|
+
Requires-Dist: pyzmq (>=25.1.2,<26.0.0)
|
|
36
|
+
Requires-Dist: wget
|
|
37
|
+
Description-Content-Type: text/markdown
|
|
38
|
+
|
|
39
|
+
# linguine
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+

|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
[](https://codecov.io/gh/lawrennd/linguine)
|
|
46
|
+
|
|
47
|
+
The `linguine` library is a library based on DOA principles for managing data. It provides these capabilities to support other libraries like `lamd` and `referia`.
|
|
48
|
+
To install use
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
%pip install linguine
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
The softare consists of TK principle parts.
|
|
55
|
+
|
|
56
|
+
### Config
|
|
57
|
+
|
|
58
|
+
First `config` which consists of `interface` and `context`.
|
|
59
|
+
|
|
60
|
+
`context` defines the `Context` object which is used to store information about the context, such as machine type etc. On the other hand`interface` defines the `Interface` object that's used for defining inputs and outputs which defines connections to other 'black box processes'.
|
|
61
|
+
|
|
62
|
+
#### A short example
|
|
63
|
+
|
|
64
|
+
The local context can be loaded in using the following command.
|
|
65
|
+
```
|
|
66
|
+
import linguine as ln
|
|
67
|
+
|
|
68
|
+
ctxt = ln.config.context.Context()
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
The `interface` module contains the key structure of `linguine`. It specifies incoming and outgoing flows, as well as computational operations. Each flow is specified in the following form.
|
|
72
|
+
|
|
73
|
+
```yaml
|
|
74
|
+
input:
|
|
75
|
+
source:
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Preprocessing can be done with a compute field.
|
|
79
|
+
|
|
80
|
+
```yaml
|
|
81
|
+
input:
|
|
82
|
+
compute:
|
|
83
|
+
field: ColumnName0
|
|
84
|
+
function: computeFunction
|
|
85
|
+
args:
|
|
86
|
+
arg1: argument1
|
|
87
|
+
arg2: argument2
|
|
88
|
+
row_args:
|
|
89
|
+
arg3: ColumnName1
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Often the data will be stored in another file (csv, excel, yaml etc) but sometimes it's convenient to store it as a `local` in a field calld 'data'. In the next example we do this to illustrate how the `compute` capability can be used to augment the file. Here two fields are added, the full name (used as an index) and today's date as an access date.
|
|
93
|
+
|
|
94
|
+
```python
|
|
95
|
+
import yaml
|
|
96
|
+
from linguine.config.interface import Interface
|
|
97
|
+
from linguine.assess.data import CustomDataFrame
|
|
98
|
+
|
|
99
|
+
# Let's assume this is the text stored in the interface file
|
|
100
|
+
yaml_text = """input:
|
|
101
|
+
type: local
|
|
102
|
+
index: fullName
|
|
103
|
+
data:
|
|
104
|
+
- familyName: Xing
|
|
105
|
+
givenName: Pei
|
|
106
|
+
- familyName: Venkatasubramanian
|
|
107
|
+
givenName: Siva
|
|
108
|
+
- familyName: Paz Luiz
|
|
109
|
+
givenName: Miguel
|
|
110
|
+
compute:
|
|
111
|
+
- field: fullName
|
|
112
|
+
function: render_liquid
|
|
113
|
+
args:
|
|
114
|
+
template: '{{familyName | replace: " ", "-"}}_{{givenName | replace: " ", "-"}}'
|
|
115
|
+
row_args:
|
|
116
|
+
givenName: givenName
|
|
117
|
+
familyName: familyName
|
|
118
|
+
- field: accessDate
|
|
119
|
+
function: today"""
|
|
120
|
+
|
|
121
|
+
interface = Interface(yaml.safe_load(yaml_text))
|
|
122
|
+
|
|
123
|
+
data = CustomDataFrame.from_flow(interface)
|
|
124
|
+
print(data)
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
would create a new field fullname which is then used as the index.
|
|
128
|
+
|
|
129
|
+
### Access
|
|
130
|
+
|
|
131
|
+
Secondly the software uses the access, assess, address decomposition. Where `access` is used for accessing data and consists of `io` and `download`. `io` allows for reading from and writing to various different file formats such as `json`, `yaml`, `markdown`, `csv`, `xls`, `bibtex`.
|
|
132
|
+
|
|
133
|
+
`download` is for accessing resources from the web, such as downloading a specific url.
|
|
134
|
+
|
|
135
|
+
#### A short example
|
|
136
|
+
|
|
137
|
+
Perhaps you would like to create a bibtex file from the PMLR proceedings volume 1, Gaussian processes in practice. In the short example below, we use `linguine` to first download the relevant URL, then we load it in and save as bibtex.
|
|
138
|
+
|
|
139
|
+
```python
|
|
140
|
+
import linguine
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
### Assess
|
|
145
|
+
|
|
146
|
+
Assess is about taking the raw data and processing it. Under `assess` `linguine` provides `data` and `compute`. The `data` module provides a `CustomDataFrame` object that provides access to the data and manipulation capabilities. the `compute` module wraps various compute capabilities for preprocessing and processing the data.
|
|
147
|
+
|
|
148
|
+
#### A short example
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
import linguine
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Util
|
|
155
|
+
|
|
156
|
+
The `util` module provides various utilities for working with data. They include
|
|
157
|
+
|
|
158
|
+
* `dataframe` for operating on data frames.
|
|
159
|
+
* `fake` for generating fake data.
|
|
160
|
+
* `files` for interacting with files.
|
|
161
|
+
* `html` for working with html.
|
|
162
|
+
* `liquid` for working with the liquid template language.
|
|
163
|
+
* `talk` for working with Neil's talk format.
|
|
164
|
+
* `tex` for working with latex.
|
|
165
|
+
* `text` for working with text.
|
|
166
|
+
* `yaml` for working with yaml.
|
|
167
|
+
* `misc` for other miscellaneous utilities.
|
|
168
|
+
|
|
169
|
+
### A short example
|
|
170
|
+
|
|
171
|
+
```python
|
|
172
|
+
import linguine
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
### Tests
|
|
176
|
+
|
|
177
|
+
The tests are stored in the `tests` subdirectory. They use `pytest`.
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
#### A short example
|
|
181
|
+
|
|
182
|
+
If you have poetry installed you can run the tests using
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
poetry run pytest
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Why linguine?
|
|
189
|
+
|
|
190
|
+
The name comes from the idea that data oriented architecture is like a set of streams of data, like linguine pasta. In Italian the word also means "little toungues" so there's also a connocation of translation between services.
|
|
191
|
+
|
lynguine-0.1.0/README.md
ADDED
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
# linguine
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+

|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
[](https://codecov.io/gh/lawrennd/linguine)
|
|
8
|
+
|
|
9
|
+
The `linguine` library is a library based on DOA principles for managing data. It provides these capabilities to support other libraries like `lamd` and `referia`.
|
|
10
|
+
To install use
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
%pip install linguine
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
The softare consists of TK principle parts.
|
|
17
|
+
|
|
18
|
+
### Config
|
|
19
|
+
|
|
20
|
+
First `config` which consists of `interface` and `context`.
|
|
21
|
+
|
|
22
|
+
`context` defines the `Context` object which is used to store information about the context, such as machine type etc. On the other hand`interface` defines the `Interface` object that's used for defining inputs and outputs which defines connections to other 'black box processes'.
|
|
23
|
+
|
|
24
|
+
#### A short example
|
|
25
|
+
|
|
26
|
+
The local context can be loaded in using the following command.
|
|
27
|
+
```
|
|
28
|
+
import linguine as ln
|
|
29
|
+
|
|
30
|
+
ctxt = ln.config.context.Context()
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
The `interface` module contains the key structure of `linguine`. It specifies incoming and outgoing flows, as well as computational operations. Each flow is specified in the following form.
|
|
34
|
+
|
|
35
|
+
```yaml
|
|
36
|
+
input:
|
|
37
|
+
source:
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Preprocessing can be done with a compute field.
|
|
41
|
+
|
|
42
|
+
```yaml
|
|
43
|
+
input:
|
|
44
|
+
compute:
|
|
45
|
+
field: ColumnName0
|
|
46
|
+
function: computeFunction
|
|
47
|
+
args:
|
|
48
|
+
arg1: argument1
|
|
49
|
+
arg2: argument2
|
|
50
|
+
row_args:
|
|
51
|
+
arg3: ColumnName1
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Often the data will be stored in another file (csv, excel, yaml etc) but sometimes it's convenient to store it as a `local` in a field calld 'data'. In the next example we do this to illustrate how the `compute` capability can be used to augment the file. Here two fields are added, the full name (used as an index) and today's date as an access date.
|
|
55
|
+
|
|
56
|
+
```python
|
|
57
|
+
import yaml
|
|
58
|
+
from linguine.config.interface import Interface
|
|
59
|
+
from linguine.assess.data import CustomDataFrame
|
|
60
|
+
|
|
61
|
+
# Let's assume this is the text stored in the interface file
|
|
62
|
+
yaml_text = """input:
|
|
63
|
+
type: local
|
|
64
|
+
index: fullName
|
|
65
|
+
data:
|
|
66
|
+
- familyName: Xing
|
|
67
|
+
givenName: Pei
|
|
68
|
+
- familyName: Venkatasubramanian
|
|
69
|
+
givenName: Siva
|
|
70
|
+
- familyName: Paz Luiz
|
|
71
|
+
givenName: Miguel
|
|
72
|
+
compute:
|
|
73
|
+
- field: fullName
|
|
74
|
+
function: render_liquid
|
|
75
|
+
args:
|
|
76
|
+
template: '{{familyName | replace: " ", "-"}}_{{givenName | replace: " ", "-"}}'
|
|
77
|
+
row_args:
|
|
78
|
+
givenName: givenName
|
|
79
|
+
familyName: familyName
|
|
80
|
+
- field: accessDate
|
|
81
|
+
function: today"""
|
|
82
|
+
|
|
83
|
+
interface = Interface(yaml.safe_load(yaml_text))
|
|
84
|
+
|
|
85
|
+
data = CustomDataFrame.from_flow(interface)
|
|
86
|
+
print(data)
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
would create a new field fullname which is then used as the index.
|
|
90
|
+
|
|
91
|
+
### Access
|
|
92
|
+
|
|
93
|
+
Secondly the software uses the access, assess, address decomposition. Where `access` is used for accessing data and consists of `io` and `download`. `io` allows for reading from and writing to various different file formats such as `json`, `yaml`, `markdown`, `csv`, `xls`, `bibtex`.
|
|
94
|
+
|
|
95
|
+
`download` is for accessing resources from the web, such as downloading a specific url.
|
|
96
|
+
|
|
97
|
+
#### A short example
|
|
98
|
+
|
|
99
|
+
Perhaps you would like to create a bibtex file from the PMLR proceedings volume 1, Gaussian processes in practice. In the short example below, we use `linguine` to first download the relevant URL, then we load it in and save as bibtex.
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
import linguine
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
### Assess
|
|
107
|
+
|
|
108
|
+
Assess is about taking the raw data and processing it. Under `assess` `linguine` provides `data` and `compute`. The `data` module provides a `CustomDataFrame` object that provides access to the data and manipulation capabilities. the `compute` module wraps various compute capabilities for preprocessing and processing the data.
|
|
109
|
+
|
|
110
|
+
#### A short example
|
|
111
|
+
|
|
112
|
+
```python
|
|
113
|
+
import linguine
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Util
|
|
117
|
+
|
|
118
|
+
The `util` module provides various utilities for working with data. They include
|
|
119
|
+
|
|
120
|
+
* `dataframe` for operating on data frames.
|
|
121
|
+
* `fake` for generating fake data.
|
|
122
|
+
* `files` for interacting with files.
|
|
123
|
+
* `html` for working with html.
|
|
124
|
+
* `liquid` for working with the liquid template language.
|
|
125
|
+
* `talk` for working with Neil's talk format.
|
|
126
|
+
* `tex` for working with latex.
|
|
127
|
+
* `text` for working with text.
|
|
128
|
+
* `yaml` for working with yaml.
|
|
129
|
+
* `misc` for other miscellaneous utilities.
|
|
130
|
+
|
|
131
|
+
### A short example
|
|
132
|
+
|
|
133
|
+
```python
|
|
134
|
+
import linguine
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Tests
|
|
138
|
+
|
|
139
|
+
The tests are stored in the `tests` subdirectory. They use `pytest`.
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
#### A short example
|
|
143
|
+
|
|
144
|
+
If you have poetry installed you can run the tests using
|
|
145
|
+
|
|
146
|
+
```bash
|
|
147
|
+
poetry run pytest
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### Why linguine?
|
|
151
|
+
|
|
152
|
+
The name comes from the idea that data oriented architecture is like a set of streams of data, like linguine pasta. In Italian the word also means "little toungues" so there's also a connocation of translation between services.
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from urllib.request import urlopen
|
|
4
|
+
from urllib.error import HTTPError, URLError
|
|
5
|
+
import git
|
|
6
|
+
|
|
7
|
+
from linguine.util.misc import prompt_stdin
|
|
8
|
+
from ..log import Logger
|
|
9
|
+
from ..config.context import Context
|
|
10
|
+
|
|
11
|
+
from ..config.interface import Interface
|
|
12
|
+
|
|
13
|
+
ctxt = Context()
|
|
14
|
+
log = Logger(
|
|
15
|
+
name=__name__,
|
|
16
|
+
level=ctxt._data["logging"]["level"],
|
|
17
|
+
filename=ctxt._data["logging"]["filename"],
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class FileDownloader:
|
|
22
|
+
"""
|
|
23
|
+
A class for downloading data files from a url.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
def __init__(self, interface, data_resources, data_name):
|
|
27
|
+
"""
|
|
28
|
+
Initialize the FileDownloader class.
|
|
29
|
+
:param data_resources: The data resources dictionary.
|
|
30
|
+
:param data_name: The name of the data to download.
|
|
31
|
+
"""
|
|
32
|
+
self.interface = interface
|
|
33
|
+
self.data_resources = data_resources
|
|
34
|
+
self.data_name = data_name
|
|
35
|
+
if self.data_name not in self.data_resources:
|
|
36
|
+
raise ValueError(f'Data "{self.data_name}" not found.')
|
|
37
|
+
self._dr = self.data_resources[self.data_name]
|
|
38
|
+
|
|
39
|
+
@property
|
|
40
|
+
def interface(self):
|
|
41
|
+
"""
|
|
42
|
+
Return the interface object.
|
|
43
|
+
:return: The interface object.
|
|
44
|
+
"""
|
|
45
|
+
return self._interface
|
|
46
|
+
|
|
47
|
+
@interface.setter
|
|
48
|
+
def interface(self, value):
|
|
49
|
+
"""
|
|
50
|
+
Set the interface object.
|
|
51
|
+
:param value: The interface object.
|
|
52
|
+
:return: None
|
|
53
|
+
"""
|
|
54
|
+
if not isinstance(value, Interface):
|
|
55
|
+
raise TypeError("interface must be of type Interface.")
|
|
56
|
+
self._interface = value
|
|
57
|
+
|
|
58
|
+
@property
|
|
59
|
+
def data_name(self):
|
|
60
|
+
"""
|
|
61
|
+
Return the name of the data to download.
|
|
62
|
+
:return: The name of the data to download.
|
|
63
|
+
"""
|
|
64
|
+
return self._data_name
|
|
65
|
+
|
|
66
|
+
@data_name.setter
|
|
67
|
+
def data_name(self, value):
|
|
68
|
+
"""
|
|
69
|
+
Set the name of the data to download.
|
|
70
|
+
:param value: The name of the data to download.
|
|
71
|
+
:return: None
|
|
72
|
+
"""
|
|
73
|
+
self._data_name = value
|
|
74
|
+
|
|
75
|
+
@property
|
|
76
|
+
def data_resources(self):
|
|
77
|
+
"""
|
|
78
|
+
Return the data resources dictionary.
|
|
79
|
+
:return: The data resources dictionary.
|
|
80
|
+
"""
|
|
81
|
+
return self._data_resources
|
|
82
|
+
|
|
83
|
+
@data_resources.setter
|
|
84
|
+
def data_resources(self, value):
|
|
85
|
+
"""
|
|
86
|
+
Set the data resources dictionary.
|
|
87
|
+
:param value: The data resources dictionary.
|
|
88
|
+
:return: None
|
|
89
|
+
"""
|
|
90
|
+
self._data_resources = value
|
|
91
|
+
|
|
92
|
+
def _authorize_download(self, prompt=None):
|
|
93
|
+
"""
|
|
94
|
+
Check with the user that they agree to terms and conditions for the data.
|
|
95
|
+
:param data_name: The name of the data to download.
|
|
96
|
+
:param prompt: A function that takes a string and returns a boolean.
|
|
97
|
+
:return: True if the user agrees to the terms and conditions.
|
|
98
|
+
:raises: ValueError if the data is not found.
|
|
99
|
+
"""
|
|
100
|
+
|
|
101
|
+
if prompt is None:
|
|
102
|
+
prompt = prompt_stdin
|
|
103
|
+
|
|
104
|
+
print(f"Acquiring resource: {self.data_name}\n")
|
|
105
|
+
print("Details of data: ")
|
|
106
|
+
print(self._dr.get("details", "No details available."))
|
|
107
|
+
|
|
108
|
+
if self._dr.get("citation"):
|
|
109
|
+
print("\nPlease cite:\n" + self._dr["citation"])
|
|
110
|
+
if self._dr.get("size"):
|
|
111
|
+
print(
|
|
112
|
+
f"\nAfter downloading, the data will take up {self._dr['size']} bytes of space."
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
storage_path = os.path.join(self.interface["default_cache_path"], self.data_name)
|
|
116
|
+
print(f"\nData will be stored in {storage_path}.\n")
|
|
117
|
+
|
|
118
|
+
if self.interface["overide_manual_authorize"]:
|
|
119
|
+
if self._dr.get("license"):
|
|
120
|
+
print(
|
|
121
|
+
"You have agreed to the following license:\n" + self._dr["license"]
|
|
122
|
+
)
|
|
123
|
+
return True
|
|
124
|
+
else:
|
|
125
|
+
if self._dr.get("license"):
|
|
126
|
+
print(
|
|
127
|
+
"You must also agree to the following license:\n"
|
|
128
|
+
+ self._dr["license"]
|
|
129
|
+
)
|
|
130
|
+
return prompt("Do you wish to proceed with the download? [yes/no] ")
|
|
131
|
+
|
|
132
|
+
def _download_url(
|
|
133
|
+
self,
|
|
134
|
+
url,
|
|
135
|
+
dir_name=".",
|
|
136
|
+
save_name=None,
|
|
137
|
+
store_directory=None,
|
|
138
|
+
messages=True,
|
|
139
|
+
suffix="",
|
|
140
|
+
):
|
|
141
|
+
"""
|
|
142
|
+
Download a file from a url and save it to disk.
|
|
143
|
+
|
|
144
|
+
:param url: The url to download from.
|
|
145
|
+
:param dir_name: The directory to save the file to.
|
|
146
|
+
:param save_name: The name to save the file as.
|
|
147
|
+
:param store_directory: The directory to store the file in.
|
|
148
|
+
:param messages: Whether to print messages to the console.
|
|
149
|
+
:param suffix: The suffix to add to the url.
|
|
150
|
+
:return: None
|
|
151
|
+
"""
|
|
152
|
+
file_name = os.path.basename(url)
|
|
153
|
+
if store_directory is not None:
|
|
154
|
+
dir_name = os.path.join(dir_name, store_directory)
|
|
155
|
+
if save_name is None:
|
|
156
|
+
save_name = file_name
|
|
157
|
+
save_path = os.path.join(dir_name, save_name)
|
|
158
|
+
|
|
159
|
+
if not os.path.exists(dir_name):
|
|
160
|
+
os.makedirs(dir_name)
|
|
161
|
+
|
|
162
|
+
try:
|
|
163
|
+
response = urlopen(url + suffix)
|
|
164
|
+
except HTTPError as e:
|
|
165
|
+
raise ValueError(f"HTTP error {e.code} when accessing {url}")
|
|
166
|
+
except URLError as e:
|
|
167
|
+
raise ValueError(f"URL error {e.reason} when accessing {url}")
|
|
168
|
+
|
|
169
|
+
self._save_file(response, save_path)
|
|
170
|
+
|
|
171
|
+
def _save_file(self, response, save_path):
|
|
172
|
+
"""
|
|
173
|
+
Save the file with progress bar.
|
|
174
|
+
|
|
175
|
+
:param response: The response object from urlopen.
|
|
176
|
+
:param save_path: The path to save the file to.
|
|
177
|
+
:return: None
|
|
178
|
+
"""
|
|
179
|
+
try:
|
|
180
|
+
file_size = int(response.info().get("Content-Length"))
|
|
181
|
+
except (TypeError, ValueError):
|
|
182
|
+
file_size = None
|
|
183
|
+
|
|
184
|
+
with open(save_path, "wb") as f:
|
|
185
|
+
if file_size:
|
|
186
|
+
from tqdm import tqdm
|
|
187
|
+
|
|
188
|
+
with tqdm(total=file_size, unit="B", unit_scale=True) as bar:
|
|
189
|
+
for buff in iter(lambda: response.read(1024), b""):
|
|
190
|
+
f.write(buff)
|
|
191
|
+
bar.update(len(buff))
|
|
192
|
+
else:
|
|
193
|
+
f.write(response.read())
|
|
194
|
+
|
|
195
|
+
def download_data(self, prompt=prompt_stdin):
|
|
196
|
+
"""
|
|
197
|
+
Check with the user that they are happy with terms and conditions for the data, then download it.
|
|
198
|
+
:param prompt: A function that takes a string and returns a boolean.
|
|
199
|
+
:return: None
|
|
200
|
+
:raises: ValueError if the data is not found.
|
|
201
|
+
"""
|
|
202
|
+
if not self._authorize_download(prompt):
|
|
203
|
+
raise Exception("Permission to download data denied.")
|
|
204
|
+
|
|
205
|
+
self._process_data()
|
|
206
|
+
|
|
207
|
+
def _process_data(self):
|
|
208
|
+
"""
|
|
209
|
+
Process the data for downloading based on its configuration.
|
|
210
|
+
|
|
211
|
+
:return: None
|
|
212
|
+
"""
|
|
213
|
+
if "suffices" in self._dr:
|
|
214
|
+
self._download_suffices()
|
|
215
|
+
elif "dirs" in self._dr:
|
|
216
|
+
self._download_dirs()
|
|
217
|
+
else:
|
|
218
|
+
self._download_simple()
|
|
219
|
+
|
|
220
|
+
def _download_suffices(self):
|
|
221
|
+
"""
|
|
222
|
+
Download a data with a suffices.
|
|
223
|
+
|
|
224
|
+
:return: None
|
|
225
|
+
"""
|
|
226
|
+
for url, filenames, suffices in zip(
|
|
227
|
+
self._dr["urls"], self._dr["files"], self._dr["suffices"]
|
|
228
|
+
):
|
|
229
|
+
for filename, suffix in zip(filenames, suffices):
|
|
230
|
+
self._download_file(url, filename, suffix=suffix)
|
|
231
|
+
|
|
232
|
+
def _download_dirs(self):
|
|
233
|
+
"""
|
|
234
|
+
Download a data with directories.
|
|
235
|
+
|
|
236
|
+
:return: None
|
|
237
|
+
"""
|
|
238
|
+
for url, dirnames, filenames in zip(
|
|
239
|
+
self._dr["urls"], self._dr["dirs"], self._dr["files"]
|
|
240
|
+
):
|
|
241
|
+
for filename, dirname in zip(filenames, dirnames):
|
|
242
|
+
self._download_file(url, filename, dirname=dirname)
|
|
243
|
+
|
|
244
|
+
def _download_simple(self):
|
|
245
|
+
"""
|
|
246
|
+
Download the data.
|
|
247
|
+
|
|
248
|
+
:return: None
|
|
249
|
+
"""
|
|
250
|
+
for url, filenames in zip(self._dr["urls"], self._dr["files"]):
|
|
251
|
+
for filename in filenames:
|
|
252
|
+
self._download_file(url, filename)
|
|
253
|
+
|
|
254
|
+
def _download_file(self, url, filename, dirname=None, suffix=""):
|
|
255
|
+
"""
|
|
256
|
+
Download a file from a url and save it to disk.
|
|
257
|
+
|
|
258
|
+
:param url: The url to download from.
|
|
259
|
+
:param filename: The name to save the file as.
|
|
260
|
+
:param dirname: The directory to store the file in.
|
|
261
|
+
:param suffix: The suffix to add to the url.
|
|
262
|
+
:return: None
|
|
263
|
+
"""
|
|
264
|
+
|
|
265
|
+
full_url = os.path.join(url, dirname if dirname else "", filename).replace(
|
|
266
|
+
" ", "%20"
|
|
267
|
+
)
|
|
268
|
+
save_name = filename if not suffix else filename + suffix
|
|
269
|
+
self._download_url(
|
|
270
|
+
url=full_url,
|
|
271
|
+
store_directory=self.interface["default_cache_path"],
|
|
272
|
+
save_name=save_name,
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
class GitDownloader(FileDownloader):
|
|
277
|
+
def __init__(self, interface, data_resources, data_name, git_url):
|
|
278
|
+
super().__init__(interface, data_resources, data_name)
|
|
279
|
+
self._git_url = git_url
|
|
280
|
+
self._repo_path = self.interface["default_cache_path"]
|
|
281
|
+
|
|
282
|
+
def _process_data(self):
|
|
283
|
+
"""
|
|
284
|
+
Process the data for downloading based on its configuration.
|
|
285
|
+
|
|
286
|
+
:return: None
|
|
287
|
+
"""
|
|
288
|
+
self._clone_or_pull_repo()
|
|
289
|
+
|
|
290
|
+
def _clone_or_pull_repo(self):
|
|
291
|
+
"""
|
|
292
|
+
Clone the repository or pull updates if it already exists.
|
|
293
|
+
|
|
294
|
+
:return: None
|
|
295
|
+
"""
|
|
296
|
+
# Ensure the cache path exists
|
|
297
|
+
if not os.path.exists(self._repo_path):
|
|
298
|
+
os.makedirs(self._repo_path)
|
|
299
|
+
|
|
300
|
+
# Check if the repo already exists
|
|
301
|
+
if os.path.isdir(os.path.join(self._repo_path, '.git')):
|
|
302
|
+
try:
|
|
303
|
+
repo = git.Repo(self._repo_path)
|
|
304
|
+
repo.git.pull()
|
|
305
|
+
except Exception as e:
|
|
306
|
+
raise ValueError(f"Error pulling repository: {e}")
|
|
307
|
+
else:
|
|
308
|
+
try:
|
|
309
|
+
git.Repo.clone_from(self._git_url, self._repo_path)
|
|
310
|
+
except Exception as e:
|
|
311
|
+
raise ValueError(f"Error cloning repository: {e}")
|