ics-query 0.1.0a0__py3-none-any.whl → 0.1.1a0__py3-none-any.whl
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.
- ics_query/_version.py +2 -2
- ics_query/cli.py +175 -7
- ics_query/parse.py +38 -1
- ics_query/tests/conftest.py +24 -12
- ics_query/tests/runs/at 2019-03-04 multiple-calendars.ics -.run +20 -0
- ics_query/tests/runs/at 2019-03-04 one-event-twice.ics -.run +18 -0
- ics_query/tests/runs/at 2019-03-07 multiple-calendars.ics -.run +11 -0
- ics_query/tests/runs/calendars/multiple-calendars.ics +71 -0
- ics_query/tests/runs/calendars/one-event-twice.ics +68 -0
- ics_query/tests/test_command_line.py +9 -0
- ics_query/tests/test_parse_date.py +63 -0
- {ics_query-0.1.0a0.dist-info → ics_query-0.1.1a0.dist-info}/METADATA +91 -12
- ics_query-0.1.1a0.dist-info/RECORD +22 -0
- ics_query-0.1.0a0.dist-info/RECORD +0 -16
- {ics_query-0.1.0a0.dist-info → ics_query-0.1.1a0.dist-info}/WHEEL +0 -0
- {ics_query-0.1.0a0.dist-info → ics_query-0.1.1a0.dist-info}/entry_points.txt +0 -0
- {ics_query-0.1.0a0.dist-info → ics_query-0.1.1a0.dist-info}/licenses/LICENSE +0 -0
ics_query/_version.py
CHANGED
ics_query/cli.py
CHANGED
|
@@ -9,10 +9,11 @@ import typing as t
|
|
|
9
9
|
|
|
10
10
|
import click
|
|
11
11
|
import recurring_ical_events
|
|
12
|
-
from icalendar import Calendar
|
|
12
|
+
from icalendar.cal import Calendar, Component
|
|
13
13
|
from recurring_ical_events import CalendarQuery
|
|
14
14
|
|
|
15
15
|
from . import parse
|
|
16
|
+
from .version import __version__
|
|
16
17
|
|
|
17
18
|
if t.TYPE_CHECKING:
|
|
18
19
|
from io import FileIO
|
|
@@ -50,6 +51,17 @@ class ComponentsResultArgument(click.File):
|
|
|
50
51
|
return ComponentsResult(file)
|
|
51
52
|
|
|
52
53
|
|
|
54
|
+
class JoinedCalendars:
|
|
55
|
+
def __init__(self, calendars: list[Calendar]):
|
|
56
|
+
"""Join multiple calendars."""
|
|
57
|
+
self.queries = [recurring_ical_events.of(calendar) for calendar in calendars]
|
|
58
|
+
|
|
59
|
+
def at(self, dt: tuple[int]) -> t.Generator[Component]:
|
|
60
|
+
"""Return the components."""
|
|
61
|
+
for query in self.queries:
|
|
62
|
+
yield from query.at(dt)
|
|
63
|
+
|
|
64
|
+
|
|
53
65
|
class CalendarQueryInputArgument(click.File):
|
|
54
66
|
"""Argument for the result."""
|
|
55
67
|
|
|
@@ -61,8 +73,8 @@ class CalendarQueryInputArgument(click.File):
|
|
|
61
73
|
) -> recurring_ical_events.CalendarQuery:
|
|
62
74
|
"""Return a CalendarQuery."""
|
|
63
75
|
file = super().convert(value, param, ctx)
|
|
64
|
-
|
|
65
|
-
return
|
|
76
|
+
calendars = Calendar.from_ical(file.read(), multiple=True)
|
|
77
|
+
return JoinedCalendars(calendars)
|
|
66
78
|
|
|
67
79
|
|
|
68
80
|
arg_calendar = click.argument("calendar", type=CalendarQueryInputArgument("rb"))
|
|
@@ -70,9 +82,48 @@ arg_output = click.argument("output", type=ComponentsResultArgument("wb"))
|
|
|
70
82
|
|
|
71
83
|
|
|
72
84
|
@click.group()
|
|
85
|
+
@click.version_option(__version__)
|
|
73
86
|
def main():
|
|
74
|
-
"""
|
|
75
|
-
|
|
87
|
+
"""Find out what happens in ICS calendar files.
|
|
88
|
+
|
|
89
|
+
ics-query can query and filter RFC 5545 compatible .ics files.
|
|
90
|
+
Components are events, journal entries and TODOs.
|
|
91
|
+
|
|
92
|
+
\b
|
|
93
|
+
Common Parameters
|
|
94
|
+
-----------------
|
|
95
|
+
|
|
96
|
+
Common parameters are described below.
|
|
97
|
+
|
|
98
|
+
CALENDAR
|
|
99
|
+
|
|
100
|
+
The CALENDAR is a readable file with one or more ICS calendars in it.
|
|
101
|
+
If CALENDAR is "-", then the standard input is used.
|
|
102
|
+
|
|
103
|
+
OUTPUT
|
|
104
|
+
|
|
105
|
+
This is the OUTPUT file for the result.
|
|
106
|
+
It is usually a path to a file that can be written to.
|
|
107
|
+
If OUTPUT is "-", then the standard output is used.
|
|
108
|
+
|
|
109
|
+
\b
|
|
110
|
+
Notes on Calculation
|
|
111
|
+
--------------------
|
|
112
|
+
|
|
113
|
+
An event can be very long. If you request smaller time spans or a time as
|
|
114
|
+
exact as a second, the event will still occur within this time span if it
|
|
115
|
+
happens during that time.
|
|
116
|
+
|
|
117
|
+
Generally, an event occurs within a time span if this applies:
|
|
118
|
+
|
|
119
|
+
event.DTSTART <= span.DTEND and span.DTSTART < event.DTEND
|
|
120
|
+
|
|
121
|
+
The START is INCLUSIVE, then END is EXCLUSIVE.
|
|
122
|
+
|
|
123
|
+
\b
|
|
124
|
+
Notes on Timezones
|
|
125
|
+
------------------
|
|
126
|
+
""" # noqa: D301
|
|
76
127
|
|
|
77
128
|
|
|
78
129
|
pass_datetime = click.make_pass_decorator(parse.to_time)
|
|
@@ -83,9 +134,126 @@ pass_datetime = click.make_pass_decorator(parse.to_time)
|
|
|
83
134
|
@arg_calendar
|
|
84
135
|
@arg_output
|
|
85
136
|
def at(calendar: CalendarQuery, output: ComponentsResult, date: DateArgument):
|
|
86
|
-
"""
|
|
137
|
+
"""Occurrences at a certain dates.
|
|
138
|
+
|
|
139
|
+
YEAR
|
|
140
|
+
|
|
141
|
+
All occurrences in this year.
|
|
142
|
+
|
|
143
|
+
\b
|
|
144
|
+
Formats:
|
|
145
|
+
\b
|
|
146
|
+
YYYY
|
|
147
|
+
\b
|
|
148
|
+
Examples:
|
|
149
|
+
\b
|
|
150
|
+
ics-query at 2024 # all occurrences in year 2024
|
|
151
|
+
ics-query at `date +%Y` # all occurrences in this year
|
|
152
|
+
|
|
153
|
+
MONTH
|
|
154
|
+
|
|
155
|
+
All occurrences in this month.
|
|
156
|
+
|
|
157
|
+
\b
|
|
158
|
+
Formats:
|
|
159
|
+
|
|
160
|
+
YYYY-MM
|
|
161
|
+
YYYY-M
|
|
162
|
+
YYYYMM
|
|
163
|
+
\b
|
|
164
|
+
Examples:
|
|
165
|
+
\b
|
|
166
|
+
ics-query at 2019-10 # October 2019
|
|
167
|
+
ics-query at 1990-01 # January 1990
|
|
168
|
+
ics-query at 1990-1 # January 1990
|
|
169
|
+
ics-query at 199001 # January 1990
|
|
170
|
+
ics-query at `date +%Y%m` # this month
|
|
171
|
+
|
|
172
|
+
DAY
|
|
173
|
+
|
|
174
|
+
All occurrences in one day.
|
|
175
|
+
|
|
176
|
+
\b
|
|
177
|
+
Formats:
|
|
178
|
+
|
|
179
|
+
YYYY-MM-DD
|
|
180
|
+
YYYY-M-D
|
|
181
|
+
YYYYMMDD
|
|
182
|
+
\b
|
|
183
|
+
Examples:
|
|
184
|
+
\b
|
|
185
|
+
ics-query at 1990-01-01 # 1st January 1990
|
|
186
|
+
ics-query at 1990-1-1 # 1st January 1990
|
|
187
|
+
ics-query at 19900101 # 1st January 1990
|
|
188
|
+
ics-query at `date +%Y%m%d` # today
|
|
189
|
+
|
|
190
|
+
HOUR
|
|
191
|
+
|
|
192
|
+
All occurrences within one hour.
|
|
193
|
+
|
|
194
|
+
\b
|
|
195
|
+
Formats:
|
|
196
|
+
\b
|
|
197
|
+
YYYY-MM-DD HH
|
|
198
|
+
YYYY-MM-DDTHH
|
|
199
|
+
YYYY-M-DTH
|
|
200
|
+
YYYYMMDDTHH
|
|
201
|
+
YYYYMMDDHH
|
|
202
|
+
\b
|
|
203
|
+
Examples:
|
|
204
|
+
\b
|
|
205
|
+
ics-query at 1990-01-01 00 # 1st January 1990, 12am - 1am
|
|
206
|
+
ics-query at 1990-01-01T00 # 1st January 1990, 12am - 1am
|
|
207
|
+
ics-query at 1990-1-1T17 # 1st January 1990, 17:00 - 18:00
|
|
208
|
+
ics-query at 19900101T23 # 1st January 1990, 23:00 - midnight
|
|
209
|
+
ics-query at 1990010123 # 1st January 1990, 23:00 - midnight
|
|
210
|
+
ics-query at `date +%Y%m%d%H` # this hour
|
|
211
|
+
|
|
212
|
+
MINUTE
|
|
213
|
+
|
|
214
|
+
All occurrences within one minute.
|
|
215
|
+
|
|
216
|
+
\b
|
|
217
|
+
Formats:
|
|
218
|
+
\b
|
|
219
|
+
YYYY-MM-DD HH:MM
|
|
220
|
+
YYYY-MM-DDTHH:MM
|
|
221
|
+
YYYY-M-DTH:M
|
|
222
|
+
YYYYMMDDTHHMM
|
|
223
|
+
YYYYMMDDHHMM
|
|
224
|
+
\b
|
|
225
|
+
Examples:
|
|
226
|
+
\b
|
|
227
|
+
ics-query at 1990-01-01 10:10 # 1st January 1990, 10:10am - 10:11am
|
|
228
|
+
ics-query at 1990-01-01T10:10 # 1st January 1990, 10:10am - 10:11am
|
|
229
|
+
ics-query at 1990-1-1T7:2 # 1st January 1990, 07:02 - 07:03
|
|
230
|
+
ics-query at 19900101T2359 # 1st January 1990, 23:59 - midnight
|
|
231
|
+
ics-query at 199001012359 # 1st January 1990, 23:59 - midnight
|
|
232
|
+
ics-query at `date +%Y%m%d%H%M` # this minute
|
|
233
|
+
|
|
234
|
+
SECOND
|
|
235
|
+
|
|
236
|
+
All occurrences at a precise time.
|
|
237
|
+
|
|
238
|
+
\b
|
|
239
|
+
Formats:
|
|
240
|
+
\b
|
|
241
|
+
YYYY-MM-DD HH:MM:SS
|
|
242
|
+
YYYY-MM-DDTHH:MM:SS
|
|
243
|
+
YYYY-M-DTH:M:S
|
|
244
|
+
YYYYMMDDTHHMMSS
|
|
245
|
+
YYYYMMDDHHMMSS
|
|
246
|
+
\b
|
|
247
|
+
Examples:
|
|
248
|
+
\b
|
|
249
|
+
ics-query at 1990-01-01 10:10:00 # 1st January 1990, 10:10am
|
|
250
|
+
ics-query at 1990-01-01T10:10:00 # 1st January 1990, 10:10am
|
|
251
|
+
ics-query at 1990-1-1T7:2:30 # 1st January 1990, 07:02:30
|
|
252
|
+
ics-query at 19901231T235959 # 31st December 1990, 23:59:59
|
|
253
|
+
ics-query at 19900101235959 # 1st January 1990, 23:59:59
|
|
254
|
+
ics-query at `date +%Y%m%d%H%M%S` # now
|
|
255
|
+
""" # noqa: D301
|
|
87
256
|
for event in calendar.at(date):
|
|
88
|
-
print("debug")
|
|
89
257
|
output.add_component(event)
|
|
90
258
|
|
|
91
259
|
|
ics_query/parse.py
CHANGED
|
@@ -2,12 +2,49 @@
|
|
|
2
2
|
|
|
3
3
|
from __future__ import annotations
|
|
4
4
|
|
|
5
|
+
import re
|
|
6
|
+
|
|
5
7
|
DateArgument = tuple[int]
|
|
6
8
|
|
|
7
9
|
|
|
10
|
+
class InvalidTimeFormat(ValueError):
|
|
11
|
+
"""The value provided does not yield a precise time."""
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
REGEX_TIME = re.compile(
|
|
15
|
+
r"^(?P<year>\d\d\d\d)"
|
|
16
|
+
r"(?P<month>-\d?\d|\d\d)?"
|
|
17
|
+
r"(?P<day>-\d?\d|\d\d)?"
|
|
18
|
+
r"(?P<hour>[ T]\d?\d|\d\d)?"
|
|
19
|
+
r"(?P<minute>:\d?\d|\d\d)?"
|
|
20
|
+
r"(?P<second>:\d?\d|\d\d)?"
|
|
21
|
+
r"$"
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
|
|
8
25
|
def to_time(dt: str) -> DateArgument:
|
|
9
26
|
"""Parse the time and date."""
|
|
10
|
-
|
|
27
|
+
parsed_dt = REGEX_TIME.match(dt)
|
|
28
|
+
if parsed_dt is None:
|
|
29
|
+
raise InvalidTimeFormat(dt)
|
|
30
|
+
|
|
31
|
+
def group(group_name: str) -> tuple[int]:
|
|
32
|
+
"""Return a group's value."""
|
|
33
|
+
result = parsed_dt.group(group_name)
|
|
34
|
+
while result and result[0] not in "0123456789":
|
|
35
|
+
result = result[1:]
|
|
36
|
+
if result is None:
|
|
37
|
+
return ()
|
|
38
|
+
return (int(result),)
|
|
39
|
+
|
|
40
|
+
return (
|
|
41
|
+
group("year")
|
|
42
|
+
+ group("month")
|
|
43
|
+
+ group("day")
|
|
44
|
+
+ group("hour")
|
|
45
|
+
+ group("minute")
|
|
46
|
+
+ group("second")
|
|
47
|
+
)
|
|
11
48
|
|
|
12
49
|
|
|
13
50
|
__all__ = ["to_time", "DateArgument"]
|
ics_query/tests/conftest.py
CHANGED
|
@@ -5,12 +5,13 @@ from __future__ import annotations
|
|
|
5
5
|
import subprocess
|
|
6
6
|
from copy import deepcopy
|
|
7
7
|
from pathlib import Path
|
|
8
|
-
from typing import NamedTuple
|
|
8
|
+
from typing import Callable, NamedTuple
|
|
9
9
|
|
|
10
10
|
import pytest
|
|
11
11
|
|
|
12
12
|
HERE = Path(__file__).parent
|
|
13
13
|
IO_DIRECTORY = HERE / "runs"
|
|
14
|
+
CALENDARS_DIRECTORY = IO_DIRECTORY / "calendars"
|
|
14
15
|
|
|
15
16
|
|
|
16
17
|
class TestRun(NamedTuple):
|
|
@@ -34,12 +35,26 @@ class TestRun(NamedTuple):
|
|
|
34
35
|
)
|
|
35
36
|
|
|
36
37
|
|
|
38
|
+
def run_ics_query(*command, cwd=CALENDARS_DIRECTORY) -> TestRun:
|
|
39
|
+
"""Run ics-qeury with a command."""
|
|
40
|
+
cmd = ["ics-query", *command]
|
|
41
|
+
print(" ".join(cmd))
|
|
42
|
+
completed_process = subprocess.run( # noqa: S603, RUF100
|
|
43
|
+
cmd, # noqa: S603, RUF100
|
|
44
|
+
capture_output=True,
|
|
45
|
+
timeout=3,
|
|
46
|
+
check=False,
|
|
47
|
+
cwd=cwd,
|
|
48
|
+
)
|
|
49
|
+
return TestRun.from_completed_process(completed_process)
|
|
50
|
+
|
|
51
|
+
|
|
37
52
|
class IOTestCase(NamedTuple):
|
|
38
53
|
"""An example test case."""
|
|
39
54
|
|
|
40
55
|
name: str
|
|
41
56
|
command: list[str]
|
|
42
|
-
|
|
57
|
+
location: Path
|
|
43
58
|
expected_output: str
|
|
44
59
|
|
|
45
60
|
@classmethod
|
|
@@ -50,16 +65,7 @@ class IOTestCase(NamedTuple):
|
|
|
50
65
|
|
|
51
66
|
def run(self) -> TestRun:
|
|
52
67
|
"""Run this test case and return the result."""
|
|
53
|
-
|
|
54
|
-
print(" ".join(command))
|
|
55
|
-
completed_process = subprocess.run( # noqa: S603, RUF100
|
|
56
|
-
command, # noqa: S603, RUF100
|
|
57
|
-
capture_output=True,
|
|
58
|
-
timeout=3,
|
|
59
|
-
check=False,
|
|
60
|
-
cwd=self.cwd / "calendars",
|
|
61
|
-
)
|
|
62
|
-
return TestRun.from_completed_process(completed_process)
|
|
68
|
+
return run_ics_query(*self.command)
|
|
63
69
|
|
|
64
70
|
|
|
65
71
|
io_test_cases = [
|
|
@@ -75,4 +81,10 @@ def io_testcase(request) -> IOTestCase:
|
|
|
75
81
|
return deepcopy(request.param)
|
|
76
82
|
|
|
77
83
|
|
|
84
|
+
@pytest.fixture
|
|
85
|
+
def run() -> Callable[..., TestRun]:
|
|
86
|
+
"""Return a runner function."""
|
|
87
|
+
return run_ics_query
|
|
88
|
+
|
|
89
|
+
|
|
78
90
|
__all__ = ["IOTestCase", "TestRun"]
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
BEGIN:VEVENT
|
|
2
|
+
SUMMARY:test4
|
|
3
|
+
DTSTART;TZID=Europe/Berlin:20190304T000000
|
|
4
|
+
DTEND;TZID=Europe/Berlin:20190304T010000
|
|
5
|
+
DTSTAMP:20190303T111937
|
|
6
|
+
UID:UYDQSG9TH4DE0WM3QFL2J
|
|
7
|
+
CLASS:PUBLIC
|
|
8
|
+
CREATED:20190303T111937
|
|
9
|
+
LAST-MODIFIED:20190303T111937
|
|
10
|
+
STATUS:CONFIRMED
|
|
11
|
+
END:VEVENT
|
|
12
|
+
BEGIN:VEVENT
|
|
13
|
+
SUMMARY:test1
|
|
14
|
+
DTSTART;TZID=Europe/Berlin:20190304T080000
|
|
15
|
+
DTEND;TZID=Europe/Berlin:20190304T083000
|
|
16
|
+
DTSTAMP:20190303T111937
|
|
17
|
+
UID:UYDQSG9TH4DE0WM3QFL2J
|
|
18
|
+
CREATED:20190303T111937
|
|
19
|
+
LAST-MODIFIED:20190303T111937
|
|
20
|
+
END:VEVENT
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
BEGIN:VEVENT
|
|
2
|
+
SUMMARY:test1
|
|
3
|
+
DTSTART;TZID=Europe/Berlin:20190304T080000
|
|
4
|
+
DTEND;TZID=Europe/Berlin:20190304T083000
|
|
5
|
+
DTSTAMP:20190303T111937
|
|
6
|
+
UID:UYDQSG9TH4DE0WM3QFL2J
|
|
7
|
+
CREATED:20190303T111937
|
|
8
|
+
LAST-MODIFIED:20190303T111937
|
|
9
|
+
END:VEVENT
|
|
10
|
+
BEGIN:VEVENT
|
|
11
|
+
SUMMARY:test1
|
|
12
|
+
DTSTART;TZID=Europe/Berlin:20190304T080000
|
|
13
|
+
DTEND;TZID=Europe/Berlin:20190304T083000
|
|
14
|
+
DTSTAMP:20190303T111937
|
|
15
|
+
UID:UYDQSG9TH4DE0WM3QFL2J
|
|
16
|
+
CREATED:20190303T111937
|
|
17
|
+
LAST-MODIFIED:20190303T111937
|
|
18
|
+
END:VEVENT
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
BEGIN:VEVENT
|
|
2
|
+
SUMMARY:test4
|
|
3
|
+
DTSTART;TZID=Europe/Berlin:20190307T000000
|
|
4
|
+
DTEND;TZID=Europe/Berlin:20190307T010000
|
|
5
|
+
DTSTAMP:20190303T111937
|
|
6
|
+
UID:UYDQSG9TH4DE0WM3QFL2J
|
|
7
|
+
CLASS:PUBLIC
|
|
8
|
+
CREATED:20190303T111937
|
|
9
|
+
LAST-MODIFIED:20190303T111937
|
|
10
|
+
STATUS:CONFIRMED
|
|
11
|
+
END:VEVENT
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
BEGIN:VCALENDAR
|
|
2
|
+
VERSION:2.0
|
|
3
|
+
PRODID:-//SabreDAV//SabreDAV//EN
|
|
4
|
+
CALSCALE:GREGORIAN
|
|
5
|
+
X-WR-CALNAME:test
|
|
6
|
+
X-APPLE-CALENDAR-COLOR:#e78074
|
|
7
|
+
BEGIN:VTIMEZONE
|
|
8
|
+
TZID:Europe/Berlin
|
|
9
|
+
X-LIC-LOCATION:Europe/Berlin
|
|
10
|
+
BEGIN:DAYLIGHT
|
|
11
|
+
TZOFFSETFROM:+0100
|
|
12
|
+
TZOFFSETTO:+0200
|
|
13
|
+
TZNAME:CEST
|
|
14
|
+
DTSTART:19700329T020000
|
|
15
|
+
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
|
|
16
|
+
END:DAYLIGHT
|
|
17
|
+
BEGIN:STANDARD
|
|
18
|
+
TZOFFSETFROM:+0200
|
|
19
|
+
TZOFFSETTO:+0100
|
|
20
|
+
TZNAME:CET
|
|
21
|
+
DTSTART:19701025T030000
|
|
22
|
+
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
|
|
23
|
+
END:STANDARD
|
|
24
|
+
END:VTIMEZONE
|
|
25
|
+
BEGIN:VEVENT
|
|
26
|
+
CREATED:20190303T111937
|
|
27
|
+
DTSTAMP:20190303T111937
|
|
28
|
+
LAST-MODIFIED:20190303T111937
|
|
29
|
+
UID:UYDQSG9TH4DE0WM3QFL2J
|
|
30
|
+
SUMMARY:test4
|
|
31
|
+
CLASS:PUBLIC
|
|
32
|
+
STATUS:CONFIRMED
|
|
33
|
+
RRULE:FREQ=DAILY;COUNT=3;INTERVAL=3
|
|
34
|
+
DTSTART;TZID=Europe/Berlin:20190304T000000
|
|
35
|
+
DTEND;TZID=Europe/Berlin:20190304T010000
|
|
36
|
+
END:VEVENT
|
|
37
|
+
END:VCALENDAR
|
|
38
|
+
BEGIN:VCALENDAR
|
|
39
|
+
VERSION:2.0
|
|
40
|
+
PRODID:-//SabreDAV//SabreDAV//EN
|
|
41
|
+
CALSCALE:GREGORIAN
|
|
42
|
+
X-WR-CALNAME:test
|
|
43
|
+
X-APPLE-CALENDAR-COLOR:#e78074
|
|
44
|
+
BEGIN:VTIMEZONE
|
|
45
|
+
TZID:Europe/Berlin
|
|
46
|
+
X-LIC-LOCATION:Europe/Berlin
|
|
47
|
+
BEGIN:DAYLIGHT
|
|
48
|
+
TZOFFSETFROM:+0100
|
|
49
|
+
TZOFFSETTO:+0200
|
|
50
|
+
TZNAME:CEST
|
|
51
|
+
DTSTART:19700329T020000
|
|
52
|
+
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
|
|
53
|
+
END:DAYLIGHT
|
|
54
|
+
BEGIN:STANDARD
|
|
55
|
+
TZOFFSETFROM:+0200
|
|
56
|
+
TZOFFSETTO:+0100
|
|
57
|
+
TZNAME:CET
|
|
58
|
+
DTSTART:19701025T030000
|
|
59
|
+
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
|
|
60
|
+
END:STANDARD
|
|
61
|
+
END:VTIMEZONE
|
|
62
|
+
BEGIN:VEVENT
|
|
63
|
+
CREATED:20190303T111937
|
|
64
|
+
DTSTAMP:20190303T111937
|
|
65
|
+
LAST-MODIFIED:20190303T111937
|
|
66
|
+
UID:UYDQSG9TH4DE0WM3QFL2J
|
|
67
|
+
SUMMARY:test1
|
|
68
|
+
DTSTART;TZID=Europe/Berlin:20190304T080000
|
|
69
|
+
DTEND;TZID=Europe/Berlin:20190304T083000
|
|
70
|
+
END:VEVENT
|
|
71
|
+
END:VCALENDAR
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
BEGIN:VCALENDAR
|
|
2
|
+
VERSION:2.0
|
|
3
|
+
PRODID:-//SabreDAV//SabreDAV//EN
|
|
4
|
+
CALSCALE:GREGORIAN
|
|
5
|
+
X-WR-CALNAME:test
|
|
6
|
+
X-APPLE-CALENDAR-COLOR:#e78074
|
|
7
|
+
BEGIN:VTIMEZONE
|
|
8
|
+
TZID:Europe/Berlin
|
|
9
|
+
X-LIC-LOCATION:Europe/Berlin
|
|
10
|
+
BEGIN:DAYLIGHT
|
|
11
|
+
TZOFFSETFROM:+0100
|
|
12
|
+
TZOFFSETTO:+0200
|
|
13
|
+
TZNAME:CEST
|
|
14
|
+
DTSTART:19700329T020000
|
|
15
|
+
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
|
|
16
|
+
END:DAYLIGHT
|
|
17
|
+
BEGIN:STANDARD
|
|
18
|
+
TZOFFSETFROM:+0200
|
|
19
|
+
TZOFFSETTO:+0100
|
|
20
|
+
TZNAME:CET
|
|
21
|
+
DTSTART:19701025T030000
|
|
22
|
+
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
|
|
23
|
+
END:STANDARD
|
|
24
|
+
END:VTIMEZONE
|
|
25
|
+
BEGIN:VEVENT
|
|
26
|
+
CREATED:20190303T111937
|
|
27
|
+
DTSTAMP:20190303T111937
|
|
28
|
+
LAST-MODIFIED:20190303T111937
|
|
29
|
+
UID:UYDQSG9TH4DE0WM3QFL2J
|
|
30
|
+
SUMMARY:test1
|
|
31
|
+
DTSTART;TZID=Europe/Berlin:20190304T080000
|
|
32
|
+
DTEND;TZID=Europe/Berlin:20190304T083000
|
|
33
|
+
END:VEVENT
|
|
34
|
+
END:VCALENDAR
|
|
35
|
+
BEGIN:VCALENDAR
|
|
36
|
+
VERSION:2.0
|
|
37
|
+
PRODID:-//SabreDAV//SabreDAV//EN
|
|
38
|
+
CALSCALE:GREGORIAN
|
|
39
|
+
X-WR-CALNAME:test
|
|
40
|
+
X-APPLE-CALENDAR-COLOR:#e78074
|
|
41
|
+
BEGIN:VTIMEZONE
|
|
42
|
+
TZID:Europe/Berlin
|
|
43
|
+
X-LIC-LOCATION:Europe/Berlin
|
|
44
|
+
BEGIN:DAYLIGHT
|
|
45
|
+
TZOFFSETFROM:+0100
|
|
46
|
+
TZOFFSETTO:+0200
|
|
47
|
+
TZNAME:CEST
|
|
48
|
+
DTSTART:19700329T020000
|
|
49
|
+
RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=-1SU
|
|
50
|
+
END:DAYLIGHT
|
|
51
|
+
BEGIN:STANDARD
|
|
52
|
+
TZOFFSETFROM:+0200
|
|
53
|
+
TZOFFSETTO:+0100
|
|
54
|
+
TZNAME:CET
|
|
55
|
+
DTSTART:19701025T030000
|
|
56
|
+
RRULE:FREQ=YEARLY;BYMONTH=10;BYDAY=-1SU
|
|
57
|
+
END:STANDARD
|
|
58
|
+
END:VTIMEZONE
|
|
59
|
+
BEGIN:VEVENT
|
|
60
|
+
CREATED:20190303T111937
|
|
61
|
+
DTSTAMP:20190303T111937
|
|
62
|
+
LAST-MODIFIED:20190303T111937
|
|
63
|
+
UID:UYDQSG9TH4DE0WM3QFL2J
|
|
64
|
+
SUMMARY:test1
|
|
65
|
+
DTSTART;TZID=Europe/Berlin:20190304T080000
|
|
66
|
+
DTEND;TZID=Europe/Berlin:20190304T083000
|
|
67
|
+
END:VEVENT
|
|
68
|
+
END:VCALENDAR
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
"""Test the commmand line."""
|
|
2
2
|
|
|
3
|
+
from ics_query.version import version
|
|
4
|
+
|
|
3
5
|
from .conftest import IOTestCase
|
|
4
6
|
|
|
5
7
|
|
|
@@ -8,3 +10,10 @@ def test_check_program_output(io_testcase: IOTestCase):
|
|
|
8
10
|
result = io_testcase.run()
|
|
9
11
|
print(result.error)
|
|
10
12
|
assert result.output == io_testcase.expected_output
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def test_version(run):
|
|
16
|
+
"""Check the version is displayed."""
|
|
17
|
+
result = run("--version")
|
|
18
|
+
assert result.exit_code == 0
|
|
19
|
+
assert version in result.output
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
"""This tests parsing input times and dates."""
|
|
2
|
+
|
|
3
|
+
import pytest
|
|
4
|
+
|
|
5
|
+
from ics_query.parse import InvalidTimeFormat, to_time
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
@pytest.mark.parametrize(
|
|
9
|
+
("string_argument", "expected_result"),
|
|
10
|
+
[
|
|
11
|
+
# year
|
|
12
|
+
("2019", (2019,)),
|
|
13
|
+
("1991", (1991,)),
|
|
14
|
+
# month
|
|
15
|
+
("2000-11", (2000, 11)),
|
|
16
|
+
("2001-01", (2001, 1)),
|
|
17
|
+
("1990-1", (1990, 1)),
|
|
18
|
+
("201011", (2010, 11)),
|
|
19
|
+
("199003", (1990, 3)),
|
|
20
|
+
# day
|
|
21
|
+
("1990-01-01", (1990, 1, 1)),
|
|
22
|
+
("2001-3-4", (2001, 3, 4)),
|
|
23
|
+
("19801231", (1980, 12, 31)),
|
|
24
|
+
# hour
|
|
25
|
+
("1990-01-01 00", (1990, 1, 1, 0)),
|
|
26
|
+
("1991-12-31T23", (1991, 12, 31, 23)),
|
|
27
|
+
("2003-1-1T17", (2003, 1, 1, 17)),
|
|
28
|
+
("20010409T12", (2001, 4, 9, 12)),
|
|
29
|
+
("2014101018", (2014, 10, 10, 18)),
|
|
30
|
+
# minute
|
|
31
|
+
("1990-01-01 00:10", (1990, 1, 1, 0, 10)),
|
|
32
|
+
("1991-12-31T23:11", (1991, 12, 31, 23, 11)),
|
|
33
|
+
("2003-1-1T17:0", (2003, 1, 1, 17, 0)),
|
|
34
|
+
("2004-1-1T7:0", (2004, 1, 1, 7, 0)),
|
|
35
|
+
("20010409T12:59", (2001, 4, 9, 12, 59)),
|
|
36
|
+
("201410101830", (2014, 10, 10, 18, 30)),
|
|
37
|
+
# second
|
|
38
|
+
("1990-01-01 00:10:12", (1990, 1, 1, 0, 10, 12)),
|
|
39
|
+
("1991-12-31T23:11:0", (1991, 12, 31, 23, 11, 0)),
|
|
40
|
+
("2003-1-1T17:0:11", (2003, 1, 1, 17, 0, 11)),
|
|
41
|
+
("2004-1-1T7:0:10", (2004, 1, 1, 7, 0, 10)),
|
|
42
|
+
("20010409T12:59:58", (2001, 4, 9, 12, 59, 58)),
|
|
43
|
+
("20141010183012", (2014, 10, 10, 18, 30, 12)),
|
|
44
|
+
],
|
|
45
|
+
)
|
|
46
|
+
def test_parse_to_date_argument(string_argument, expected_result):
|
|
47
|
+
"""Check that we can properly parse what is accepted."""
|
|
48
|
+
result = to_time(string_argument)
|
|
49
|
+
assert result == expected_result
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@pytest.mark.parametrize(
|
|
53
|
+
"dt",
|
|
54
|
+
[
|
|
55
|
+
"",
|
|
56
|
+
"132",
|
|
57
|
+
"12345",
|
|
58
|
+
],
|
|
59
|
+
)
|
|
60
|
+
def test_invalid_time_format(dt: str):
|
|
61
|
+
"""Check invalid time formats."""
|
|
62
|
+
with pytest.raises(InvalidTimeFormat):
|
|
63
|
+
to_time(dt)
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: ics-query
|
|
3
|
-
Version: 0.1.
|
|
4
|
-
Summary: Find out what happens in ICS calendar files - query and filter RFC 5545 compatible
|
|
3
|
+
Version: 0.1.1a0
|
|
4
|
+
Summary: Find out what happens in ICS calendar files - query and filter RFC 5545 compatible .ics files for events, journals, TODOs and more.
|
|
5
5
|
Project-URL: Homepage, https://github.com/niccokunzmann/ics-query/
|
|
6
6
|
Project-URL: Repository, https://github.com/niccokunzmann/ics-query/
|
|
7
|
-
Project-URL: source_archive, https://github.com/niccokunzmann/ics-query/archive/
|
|
7
|
+
Project-URL: source_archive, https://github.com/niccokunzmann/ics-query/archive/fb12ef2822d23f693aa5f9faaa0cb97fa9f67516.zip
|
|
8
8
|
Project-URL: Issues, https://github.com/niccokunzmann/ics-query/issues
|
|
9
9
|
Project-URL: Documentation, https://github.com/niccokunzmann/ics-query/
|
|
10
10
|
Project-URL: Changelog, https://github.com/niccokunzmann/ics-query/#changelog
|
|
@@ -716,7 +716,79 @@ You can install this package from the [PyPI](https://pypi.org/project/ics-query/
|
|
|
716
716
|
pip install ics-query
|
|
717
717
|
```
|
|
718
718
|
|
|
719
|
-
##
|
|
719
|
+
## Usage
|
|
720
|
+
|
|
721
|
+
See how to use `ics-query`.
|
|
722
|
+
|
|
723
|
+
### Examples
|
|
724
|
+
|
|
725
|
+
You can easily get a calendar from the web and see what is on.
|
|
726
|
+
In this example, we show which German National Holidays happen in August 2024:
|
|
727
|
+
|
|
728
|
+
```shell
|
|
729
|
+
$ wget -qO- 'https://www.calendarlabs.com/ical-calendar/ics/46/Germany_Holidays.ics' | ./ics-query at 2024-08 - -
|
|
730
|
+
BEGIN:VEVENT
|
|
731
|
+
SUMMARY:Assumption Day (BY\, SL)
|
|
732
|
+
DTSTART;VALUE=DATE:20240815
|
|
733
|
+
DTEND;VALUE=DATE:20240815
|
|
734
|
+
DTSTAMP:20231013T092513Z
|
|
735
|
+
UID:65290cf9326601697189113@calendarlabs.com
|
|
736
|
+
SEQUENCE:0
|
|
737
|
+
DESCRIPTION:Visit https://calendarlabs.com/holidays/us/the-assumption-of-m
|
|
738
|
+
ary.php to know more about Assumption Day (BY\, SL). \n\n Like us on Faceb
|
|
739
|
+
ook: http://fb.com/calendarlabs to get updates
|
|
740
|
+
LOCATION:Germany
|
|
741
|
+
STATUS:CONFIRMED
|
|
742
|
+
TRANSP:TRANSPARENT
|
|
743
|
+
END:VEVENT
|
|
744
|
+
```
|
|
745
|
+
|
|
746
|
+
In the following example, we query a calendar file and print the result.
|
|
747
|
+
|
|
748
|
+
```shell
|
|
749
|
+
$ ics-query at 2019-03-04 one-event.ics -
|
|
750
|
+
BEGIN:VEVENT
|
|
751
|
+
SUMMARY:test1
|
|
752
|
+
DTSTART;TZID=Europe/Berlin:20190304T080000
|
|
753
|
+
DTEND;TZID=Europe/Berlin:20190304T083000
|
|
754
|
+
DTSTAMP:20190303T111937
|
|
755
|
+
UID:UYDQSG9TH4DE0WM3QFL2J
|
|
756
|
+
CREATED:20190303T111937
|
|
757
|
+
LAST-MODIFIED:20190303T111937
|
|
758
|
+
END:VEVENT
|
|
759
|
+
```
|
|
760
|
+
|
|
761
|
+
We can concatenate calendars and pipe them into `ics-query`.
|
|
762
|
+
In the example below, we get all events that happen right now in two calendars.
|
|
763
|
+
|
|
764
|
+
```shell
|
|
765
|
+
$ cat calendar1.ics calendar2.ics | ics-query at `date +%Y%m%d%H%M%S` - -
|
|
766
|
+
BEGIN:VEVENT
|
|
767
|
+
...
|
|
768
|
+
```
|
|
769
|
+
|
|
770
|
+
### Events at Certain Times
|
|
771
|
+
|
|
772
|
+
You can query which events happen at certain times:
|
|
773
|
+
|
|
774
|
+
```shell
|
|
775
|
+
ics-query at <date-time> calendar.ics -
|
|
776
|
+
```
|
|
777
|
+
|
|
778
|
+
`<date-time>` can be built up: It can be a year, a month, a day, an hour, a minute or a second.
|
|
779
|
+
|
|
780
|
+
Please see the command documentation for more help:
|
|
781
|
+
|
|
782
|
+
```shell
|
|
783
|
+
ics-query --help
|
|
784
|
+
ics-query at --help
|
|
785
|
+
```
|
|
786
|
+
|
|
787
|
+
## Vision
|
|
788
|
+
|
|
789
|
+
This section shows where we would like to get to.
|
|
790
|
+
|
|
791
|
+
### `ics-query at` - occurrences at certain times
|
|
720
792
|
|
|
721
793
|
You can get all **events** that happen at a certain **day**.
|
|
722
794
|
|
|
@@ -736,32 +808,32 @@ You can get all **TODO**s that happen at in certain **month**.
|
|
|
736
808
|
ics-query --components VTODO at 2029-12-24 calendar.ics
|
|
737
809
|
```
|
|
738
810
|
|
|
739
|
-
|
|
811
|
+
### `ics-query at` - time ranges
|
|
740
812
|
|
|
741
813
|
|
|
742
|
-
|
|
814
|
+
### `ics-query --output=count` - count occurrences
|
|
743
815
|
|
|
744
816
|
|
|
745
|
-
|
|
817
|
+
### `ics-query --output=ics` - use ics as output (default)
|
|
746
818
|
|
|
747
819
|
|
|
748
|
-
|
|
820
|
+
### `ics-query --select-index` - reduce output size
|
|
749
821
|
|
|
750
822
|
Examples: `0,2,4` `0-10`
|
|
751
823
|
|
|
752
|
-
|
|
824
|
+
### `ics-query all` - the whole calendar
|
|
753
825
|
|
|
754
|
-
|
|
826
|
+
### `ics-query between` - time ranges
|
|
755
827
|
|
|
756
828
|
```shell
|
|
757
829
|
ics-query between dt dt
|
|
758
830
|
ics-query between dt duration
|
|
759
831
|
```
|
|
760
832
|
|
|
761
|
-
|
|
833
|
+
### `ics-query --select-component` - filter for components
|
|
762
834
|
|
|
763
835
|
|
|
764
|
-
|
|
836
|
+
### `ics-query --select-uid` - filter by uid
|
|
765
837
|
|
|
766
838
|
|
|
767
839
|
## How to edit an event
|
|
@@ -859,12 +931,19 @@ To release new versions,
|
|
|
859
931
|
|
|
860
932
|
```shell
|
|
861
933
|
git tag v0.1.0a
|
|
934
|
+
git push origin v0.1.0a
|
|
862
935
|
```
|
|
863
936
|
|
|
864
937
|
5. Notify the issues about their release
|
|
865
938
|
|
|
866
939
|
## Changelog
|
|
867
940
|
|
|
941
|
+
- v0.1.1a
|
|
942
|
+
|
|
943
|
+
- Add `--version`
|
|
944
|
+
- Add `ics-query at <date>`
|
|
945
|
+
- Add support for multiple calendars in one input
|
|
946
|
+
|
|
868
947
|
- v0.1.0a
|
|
869
948
|
|
|
870
949
|
- Update Python version compatibility
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
ics_query/__init__.py,sha256=YxyZzqxwfA77sBVFjUyUj3_fvR2gL3bs0pkE93prRFY,216
|
|
2
|
+
ics_query/__main__.py,sha256=E6Gls0DNz8GQK2K-kOUIx8cYhgANW_CH54VKrfCfs14,52
|
|
3
|
+
ics_query/_version.py,sha256=cU7fSuITRwo1pTWU-OD7oXC6-FpABHrqGppdiounZFo,413
|
|
4
|
+
ics_query/cli.py,sha256=-ogJQ994ssQNNvU8tojCPDea4wJwFd4f9REoJ1hDCOs,6949
|
|
5
|
+
ics_query/parse.py,sha256=VkF4z5KR-uzg4YmEKaY7Zz67d4Ffxc4rsB6ezNtxBr8,1113
|
|
6
|
+
ics_query/version.py,sha256=LgqmHh_dcmWi9HMxfWJ8OhfmbzWsBTq3h08jIE18pYM,468
|
|
7
|
+
ics_query/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
+
ics_query/tests/conftest.py,sha256=MGwKXod7sFfepXK0mtvKkR8SaIiI1SZNW_rAhz6aSA0,2258
|
|
9
|
+
ics_query/tests/test_command_line.py,sha256=-XnpYWETEVEc1dJ5_dpd8dqBoMQFz1CyGaSoe_ETq5o,488
|
|
10
|
+
ics_query/tests/test_parse_date.py,sha256=ZMbllWHNCwuluGTr55xKlM7xBzoMNsoE7TQUC5uGlVA,1923
|
|
11
|
+
ics_query/tests/runs/at 2019-03-04 multiple-calendars.ics -.run,sha256=Cyeg9DMK6BibiCwkM8nW0ZtULUYSZFIgDvKRlSo_jTw,482
|
|
12
|
+
ics_query/tests/runs/at 2019-03-04 one-event-twice.ics -.run,sha256=hc-0yJhWpnSVzPNB_0_tJfR6dskNMm9xdbHeAhsX1zI,452
|
|
13
|
+
ics_query/tests/runs/at 2019-03-04 one-event.ics -.run,sha256=GVVmVpqzFjQLw2_RuhxnVTpCnwflNqeWbkFZ6u5WXxA,226
|
|
14
|
+
ics_query/tests/runs/at 2019-03-07 multiple-calendars.ics -.run,sha256=ZlLXxPMIai7vvmQTL4NyXJwdCQzoU1scEn_FpjmkkUA,256
|
|
15
|
+
ics_query/tests/runs/calendars/multiple-calendars.ics,sha256=lS1Q9eSPpzLs2TRDKUiwF9HmpnJMw8xqjLudA0-KtY4,1516
|
|
16
|
+
ics_query/tests/runs/calendars/one-event-twice.ics,sha256=GZ5wE-U4kjO91N2nIaLsYZpSOLvQTjkoLPI6saMocOM,1450
|
|
17
|
+
ics_query/tests/runs/calendars/one-event.ics,sha256=-uwohttEzg-jsTETNb6tQ5dO9PE3DXzlicwRiXJs1KQ,725
|
|
18
|
+
ics_query-0.1.1a0.dist-info/METADATA,sha256=cgssIiEuCQItTb7_5Iap9KFcfV6f9-z4DUfy-gd6MsU,48066
|
|
19
|
+
ics_query-0.1.1a0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
|
|
20
|
+
ics_query-0.1.1a0.dist-info/entry_points.txt,sha256=Jq_39vCKVOkNZjL7Wngf_04V_n_QRszLgLT2CbJKiH4,49
|
|
21
|
+
ics_query-0.1.1a0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
22
|
+
ics_query-0.1.1a0.dist-info/RECORD,,
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
ics_query/__init__.py,sha256=YxyZzqxwfA77sBVFjUyUj3_fvR2gL3bs0pkE93prRFY,216
|
|
2
|
-
ics_query/__main__.py,sha256=E6Gls0DNz8GQK2K-kOUIx8cYhgANW_CH54VKrfCfs14,52
|
|
3
|
-
ics_query/_version.py,sha256=Okky3KGcs_fu34VQzO05VuCc7CF609YKQueRW6fuvPU,413
|
|
4
|
-
ics_query/cli.py,sha256=P6eLIH7hgbfwhbJHloilHS9ut_YDjEP_QDabLTf0NQs,2367
|
|
5
|
-
ics_query/parse.py,sha256=6bCKtY39jr2NFSsqspVyGn32pwn6bMoEqJMHE26Ap1o,261
|
|
6
|
-
ics_query/version.py,sha256=LgqmHh_dcmWi9HMxfWJ8OhfmbzWsBTq3h08jIE18pYM,468
|
|
7
|
-
ics_query/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
8
|
-
ics_query/tests/conftest.py,sha256=A6R9pB3emaSo1DAIxyt-5QhWPq8LbsOoWJIRIsku2Zk,2003
|
|
9
|
-
ics_query/tests/test_command_line.py,sha256=bIJp2SCVW8MXF_lNZ1ol9A-r9Y_9zjnY2jhdTJET59g,283
|
|
10
|
-
ics_query/tests/runs/at 2019-03-04 one-event.ics -.run,sha256=GVVmVpqzFjQLw2_RuhxnVTpCnwflNqeWbkFZ6u5WXxA,226
|
|
11
|
-
ics_query/tests/runs/calendars/one-event.ics,sha256=-uwohttEzg-jsTETNb6tQ5dO9PE3DXzlicwRiXJs1KQ,725
|
|
12
|
-
ics_query-0.1.0a0.dist-info/METADATA,sha256=BoeIkgoAQE_Z-J-8Yf0mxLafokFb6vzdiaAVD8mBrAE,46147
|
|
13
|
-
ics_query-0.1.0a0.dist-info/WHEEL,sha256=1yFddiXMmvYK7QYTqtRNtX66WJ0Mz8PYEiEUoOUUxRY,87
|
|
14
|
-
ics_query-0.1.0a0.dist-info/entry_points.txt,sha256=Jq_39vCKVOkNZjL7Wngf_04V_n_QRszLgLT2CbJKiH4,49
|
|
15
|
-
ics_query-0.1.0a0.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
16
|
-
ics_query-0.1.0a0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|